diff --git a/doc/src/Errors_messages.rst b/doc/src/Errors_messages.rst index f3be94a239..96e21da681 100644 --- a/doc/src/Errors_messages.rst +++ b/doc/src/Errors_messages.rst @@ -502,7 +502,8 @@ Doc page with :doc:`WARNING messages ` *Bond/react: Unknown section in map file* Please ensure reaction map files are properly formatted. -*Bond/react: Atom affected by reaction too close to template edge* +*Bond/react: Atom type affected by reaction too close to template edge* +*Bond/react: Bond type affected by reaction too close to template edge* This means an atom which changes type or connectivity during the reaction is too close to an 'edge' atom defined in the map file. This could cause incorrect assignment of bonds, angle, etc. diff --git a/doc/src/fix_bond_react.rst b/doc/src/fix_bond_react.rst index b6f3be773f..2bbe53328a 100644 --- a/doc/src/fix_bond_react.rst +++ b/doc/src/fix_bond_react.rst @@ -14,19 +14,22 @@ Syntax react react-ID react-group-ID Nevery Rmin Rmax template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... ... -* ID, group-ID are documented in :doc:`fix ` command. Group-ID is ignored. +* ID, group-ID are documented in :doc:`fix ` command. * bond/react = style name of this fix command * the common keyword/values may be appended directly after 'bond/react' * this applies to all reaction specifications (below) -* common_keyword = *stabilization* +* common_keyword = *stabilization* or *reset_mol_ids* .. parsed-literal:: *stabilization* values = *no* or *yes* *group-ID* *xmax* - *no* = no reaction site stabilization + *no* = no reaction site stabilization (default) *yes* = perform reaction site stabilization *group-ID* = user-assigned prefix for the dynamic group of atoms not currently involved in a reaction *xmax* = xmax value that is used by an internally-created :doc:`nve/limit ` integrator + *reset_mol_ids* values = *yes* or *no* + *yes* = update molecule IDs based on new global topology (default) + *no* = do not update molecule IDs * react = mandatory argument indicating new reaction specification * react-ID = user-assigned name for the reaction @@ -50,9 +53,9 @@ Syntax *stabilize_steps* value = timesteps timesteps = number of timesteps to apply the internally-created :doc:`nve/limit ` fix to reacting atoms *update_edges* value = *none* or *charges* or *custom* - none = do not update topology near the edges of reaction templates - charges = update atomic charges of all atoms in reaction templates - custom = force the update of user-specified atomic charges + *none* = do not update topology near the edges of reaction templates + *charges* = update atomic charges of all atoms in reaction templates + *custom* = force the update of user-specified atomic charges Examples """""""" @@ -154,6 +157,13 @@ due to the internal dynamic grouping performed by fix bond/react. If the group-ID is an existing static group, react-group-IDs should also be specified as this static group, or a subset. +The *reset_mol_ids* keyword invokes the :doc:`reset_mol_ids ` +command after a reaction occurs, to ensure that molecule IDs are +consistent with the new bond topology. The group-ID used for +:doc:`reset_mol_ids ` is the group-ID for this fix. +Resetting molecule IDs is necessarily a global operation, and so can +be slow for very large systems. + The following comments pertain to each *react* argument (in other words, can be customized for each reaction, or reaction step): @@ -203,9 +213,10 @@ surrounding topology. As described below, the bonding atom pairs of the pre-reacted template are specified by atom ID in the map file. The pre-reacted molecule template should contain as few atoms as possible while still completely describing the topology of all atoms affected -by the reaction. For example, if the force field contains dihedrals, -the pre-reacted template should contain any atom within three bonds of -reacting atoms. +by the reaction (which includes all atoms that change atom type or +connectivity, and all bonds that change bond type). For example, if +the force field contains dihedrals, the pre-reacted template should +contain any atom within three bonds of reacting atoms. Some atoms in the pre-reacted template that are not reacting may have missing topology with respect to the simulation. For example, the @@ -554,7 +565,7 @@ Default """"""" The option defaults are stabilization = no, prob = 1.0, stabilize_steps = 60, -update_edges = none +reset_mol_ids = yes, update_edges = none ---------- diff --git a/doc/src/pair_granular.rst b/doc/src/pair_granular.rst index 4e4d96ed06..80663b7d49 100644 --- a/doc/src/pair_granular.rst +++ b/doc/src/pair_granular.rst @@ -93,7 +93,7 @@ on particle *i* due to contact with particle *j* is given by: .. math:: - \mathbf{F}_{ne, Hooke} = k_N \delta_{ij} \mathbf{n} + \mathbf{F}_{ne, Hooke} = k_n \delta_{ij} \mathbf{n} Where :math:`\delta_{ij} = R_i + R_j - \|\mathbf{r}_{ij}\|` is the particle overlap, :math:`R_i, R_j` are the particle radii, :math:`\mathbf{r}_{ij} = \mathbf{r}_i - \mathbf{r}_j` is the vector separating the two @@ -106,7 +106,7 @@ For the *hertz* model, the normal component of force is given by: .. math:: - \mathbf{F}_{ne, Hertz} = k_N R_{eff}^{1/2}\delta_{ij}^{3/2} \mathbf{n} + \mathbf{F}_{ne, Hertz} = k_n R_{eff}^{1/2}\delta_{ij}^{3/2} \mathbf{n} Here, :math:`R_{eff} = \frac{R_i R_j}{R_i + R_j}` is the effective radius, denoted for simplicity as *R* from here on. For *hertz*\ , the @@ -123,7 +123,7 @@ Here, :math:`E_{eff} = E = \left(\frac{1-\nu_i^2}{E_i} + \frac{1-\nu_j^2}{E_j}\r modulus, with :math:`\nu_i, \nu_j` the Poisson ratios of the particles of types *i* and *j*\ . Note that if the elastic modulus and the shear modulus of the two particles are the same, the *hertz/material* model -is equivalent to the *hertz* model with :math:`k_N = 4/3 E_{eff}` +is equivalent to the *hertz* model with :math:`k_n = 4/3 E_{eff}` The *dmt* model corresponds to the :ref:`(Derjaguin-Muller-Toporov) ` cohesive model, where the force @@ -140,7 +140,7 @@ where the force is computed as: \mathbf{F}_{ne, jkr} = \left(\frac{4Ea^3}{3R} - 2\pi a^2\sqrt{\frac{4\gamma E}{\pi a}}\right)\mathbf{n} -Here, *a* is the radius of the contact zone, related to the overlap +Here, :math:`a` is the radius of the contact zone, related to the overlap :math:`\delta` according to: .. math:: @@ -167,7 +167,7 @@ following general form: \mathbf{F}_{n,damp} = -\eta_n \mathbf{v}_{n,rel} -Here, :math:`\mathbf{v}_{n,rel} = (\mathbf{v}_j - \mathbf{v}_i) \cdot \mathbf{n} \mathbf{n}` is the component of relative velocity along +Here, :math:`\mathbf{v}_{n,rel} = (\mathbf{v}_j - \mathbf{v}_i) \cdot \mathbf{n}\ \mathbf{n}` is the component of relative velocity along :math:`\mathbf{n}`. The optional *damping* keyword to the *pair_coeff* command followed by @@ -259,7 +259,9 @@ tangential model choices and their expected parameters are as follows: 1. *linear_nohistory* : :math:`x_{\gamma,t}`, :math:`\mu_s` 2. *linear_history* : :math:`k_t`, :math:`x_{\gamma,t}`, :math:`\mu_s` 3. *mindlin* : :math:`k_t` or NULL, :math:`x_{\gamma,t}`, :math:`\mu_s` -4. *mindlin_rescale* : :math:`k_t` or NULL, :math:`x_{\gamma,t}`, :math:`\mu_s` +4. *mindlin/force* : :math:`k_t` or NULL, :math:`x_{\gamma,t}`, :math:`\mu_s` +5. *mindlin_rescale* : :math:`k_t` or NULL, :math:`x_{\gamma,t}`, :math:`\mu_s` +6. *mindlin_rescale/force* : :math:`k_t` or NULL, :math:`x_{\gamma,t}`, :math:`\mu_s` Here, :math:`x_{\gamma,t}` is a dimensionless multiplier for the normal damping :math:`\eta_n` that determines the magnitude of the tangential @@ -268,11 +270,11 @@ coefficient, and :math:`k_t` is the tangential stiffness coefficient. For *tangential linear_nohistory*, a simple velocity-dependent Coulomb friction criterion is used, which mimics the behavior of the *pair -gran/hooke* style. The tangential force (\mathbf{F}_t\) is given by: +gran/hooke* style. The tangential force :math:`\mathbf{F}_t` is given by: .. math:: - \mathbf{F}_t = -min(\mu_t F_{n0}, \|\mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} + \mathbf{F}_t = -\min(\mu_t F_{n0}, \|\mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} The tangential damping force :math:`\mathbf{F}_\mathrm{t,damp}` is given by: @@ -294,8 +296,8 @@ keyword also affects the tangential damping. The parameter literature use :math:`x_{\gamma,t} = 1` (:ref:`Marshall `, :ref:`Tsuji et al `, :ref:`Silbert et al `). The relative tangential velocity at the point of contact is given by -:math:`\mathbf{v}_{t, rel} = \mathbf{v}_{t} - (R_i\Omega_i + R_j\Omega_j) \times \mathbf{n}`, where :math:`\mathbf{v}_{t} = \mathbf{v}_r - \mathbf{v}_r\cdot\mathbf{n}{n}`, -:math:`\mathbf{v}_r = \mathbf{v}_j - \mathbf{v}_i`. +:math:`\mathbf{v}_{t, rel} = \mathbf{v}_{t} - (R_i\mathbf{\Omega}_i + R_j\mathbf{\Omega}_j) \times \mathbf{n}`, where :math:`\mathbf{v}_{t} = \mathbf{v}_r - \mathbf{v}_r\cdot\mathbf{n}\ \mathbf{n}`, +:math:`\mathbf{v}_r = \mathbf{v}_j - \mathbf{v}_i` . The direction of the applied force is :math:`\mathbf{t} = \mathbf{v_{t,rel}}/\|\mathbf{v_{t,rel}}\|` . The normal force value :math:`F_{n0}` used to compute the critical force @@ -314,21 +316,24 @@ form: .. math:: - F_{n0} = \|\mathbf{F}_ne + 2 F_{pulloff}\| + F_{n0} = \|\mathbf{F}_{ne} + 2 F_{pulloff}\| Where :math:`F_{pulloff} = 3\pi \gamma R` for *jkr*\ , and :math:`F_{pulloff} = 4\pi \gamma R` for *dmt*\ . The remaining tangential options all use accumulated tangential -displacement (i.e. contact history). This is discussed below in the -context of the *linear_history* option, but the same treatment of the -accumulated displacement applies to the other options as well. +displacement (i.e. contact history), except for the options +*mindlin/force* and *mindlin_rescale/force*, that use accumulated +tangential force instead, and are discussed further below. +The accumulated tangential displacement is discussed in details below +in the context of the *linear_history* option. The same treatment of +the accumulated displacement applies to the other options as well. For *tangential linear_history*, the tangential force is given by: .. math:: - \mathbf{F}_t = -min(\mu_t F_{n0}, \|-k_t\mathbf{\xi} + \mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} + \mathbf{F}_t = -\min(\mu_t F_{n0}, \|-k_t\mathbf{\xi} + \mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} Here, :math:`\mathbf{\xi}` is the tangential displacement accumulated during the entire duration of the contact: @@ -356,7 +361,7 @@ work: .. math:: - \mathbf{\xi} = \left(\mathbf{\xi'} - (\mathbf{n} \cdot \mathbf{\xi'})\mathbf{n}\right) \frac{\|\mathbf{\xi'}\|}{\|\mathbf{\xi'}\| - \mathbf{n}\cdot\mathbf{\xi'}} + \mathbf{\xi} = \left(\mathbf{\xi'} - (\mathbf{n} \cdot \mathbf{\xi'})\mathbf{n}\right) \frac{\|\mathbf{\xi'}\|}{\|\mathbf{\xi'} - (\mathbf{n}\cdot\mathbf{\xi'})\mathbf{n}\|} Here, :math:`\mathbf{\xi'}` is the accumulated displacement prior to the current time step and :math:`\mathbf{\xi}` is the corrected @@ -372,7 +377,7 @@ discussion): .. math:: - \mathbf{\xi} = -\frac{1}{k_t}\left(\mu_t F_{n0}\mathbf{t} + \mathbf{F}_{t,damp}\right) + \mathbf{\xi} = -\frac{1}{k_t}\left(\mu_t F_{n0}\mathbf{t} - \mathbf{F}_{t,damp}\right) The tangential force is added to the total normal force (elastic plus damping) to produce the total force on the particle. The tangential @@ -387,27 +392,68 @@ overlap region) to induce a torque on each particle according to: \mathbf{\tau}_j = -(R_j - 0.5 \delta) \mathbf{n} \times \mathbf{F}_t -For *tangential mindlin*\ , the :ref:`Mindlin ` no-slip solution is used, which differs from the *linear_history* -option by an additional factor of *a*\ , the radius of the contact region. The tangential force is given by: +For *tangential mindlin*\ , the :ref:`Mindlin ` no-slip solution +is used which differs from the *linear_history* option by an additional factor +of :math:`a`, the radius of the contact region. The tangential force is given by: .. math:: - \mathbf{F}_t = -min(\mu_t F_{n0}, \|-k_t a \mathbf{\xi} + \mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} + \mathbf{F}_t = -\min(\mu_t F_{n0}, \|-k_t a \mathbf{\xi} + \mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} -Here, *a* is the radius of the contact region, given by :math:`a =\sqrt{R\delta}` + +Here, :math:`a` is the radius of the contact region, given by :math:`a =\sqrt{R\delta}` for all normal contact models, except for *jkr*\ , where it is given implicitly by :math:`\delta = a^2/R - 2\sqrt{\pi \gamma a/E}`, see -discussion above. To match the Mindlin solution, one should set :math:`k_t = 4G/(2-\nu)`, where :math:`G` is the shear modulus, related to Young's modulus -:math:`E` by :math:`G = E/(2(1+\nu))`, where :math:`\nu` is Poisson's ratio. This -can also be achieved by specifying *NULL* for :math:`k_t`, in which case a +discussion above. To match the Mindlin solution, one should set +:math:`k_t = 8G_{eff}`, where :math:`G_{eff}` is the effective shear modulus given by: + +.. math:: + + G_{eff} = \left(\frac{2-\nu_i}{G_i} + \frac{2-\nu_j}{G_j}\right)^{-1} + +where :math:`G` is the shear modulus, related to Young's modulus :math:`E` +and Poisson's ratio :math:`\nu` by :math:`G = E/(2(1+\nu))`. This can also be +achieved by specifying *NULL* for :math:`k_t`, in which case a normal contact model that specifies material parameters :math:`E` and :math:`\nu` is required (e.g. *hertz/material*\ , *dmt* or *jkr*\ ). In this case, mixing of the shear modulus for different particle types *i* and -*j* is done according to: +*j* is done according to the formula above. + +.. note:: + + The radius of the contact region :math:`a` depends on the normal overlap. + As a result, the tangential force for *mindlin* can change due to + a variation in normal overlap, even with no change in tangential displacement. + +For *tangential mindlin/force*, the accumulated elastic tangential force +characterizes the contact history, instead of the accumulated tangential +displacement. This prevents the dependence of the tangential force on the +normal overlap as noted above. The tangential force is given by: .. math:: - 1/G = 2(2-\nu_i)(1+\nu_i)/E_i + 2(2-\nu_j)(1+\nu_j)/E_j + \mathbf{F}_t = -\min(\mu_t F_{n0}, \|\mathbf{F}_{te} + \mathbf{F}_\mathrm{t,damp}\|) \mathbf{t} + +The increment of the elastic component of the tangential force +:math:`\mathbf{F}_{te}` is given by: + +.. math:: + + \mathrm{d}\mathbf{F}_{te} = -k_t a \mathbf{v}_{t,rel} \mathrm{d}\tau + +The changes in frame of reference of the contacting pair of particles during +contact are accounted for by the same formula as above, replacing the +accumulated tangential displacement :math:`\xi`, by the accumulated tangential +elastic force :math:`F_{te}`. When the tangential force exceeds the critical +force, the tangential force is directly re-scaled to match the value for +the critical force: + +.. math:: + + \mathbf{F}_{te} = - \mu_t F_{n0}\mathbf{t} + \mathbf{F}_{t,damp} + +The same rules as those described for *mindlin* apply regarding the tangential +stiffness and mixing of the shear modulus for different particle types. The *mindlin_rescale* option uses the same form as *mindlin*\ , but the magnitude of the tangential displacement is re-scaled as the contact @@ -421,9 +467,32 @@ Here, :math:`t_{n-1}` indicates the value at the previous time step. This rescaling accounts for the fact that a decrease in the contact area upon unloading leads to the contact being unable to support the previous tangential loading, and spurious energy is -created without the rescaling above (:ref:`Walton ` ). See also -discussion in :ref:`Thornton et al, 2013 ` , particularly -equation 18(b) of that work and associated discussion. +created without the rescaling above (:ref:`Walton ` ). + +.. note:: + + For *mindlin*, a decrease in the tangential force already occurs as the + contact unloads, due to the dependence of the tangential force on the normal + force described above. By re-scaling :math:`\xi`, *mindlin_rescale* + effectively re-scales the tangential force twice, i.e., proportionally to + :math:`a^2`. This peculiar behavior results from use of the accumulated + tangential displacement to characterize the contact history. Although + *mindlin_rescale* remains available for historic reasons and backward + compatibility purposes, it should be avoided in favor of *mindlin_rescale/force*. + +The *mindlin_rescale/force* option uses the same form as *mindlin/force*, +but the magnitude of the tangential elastic force is re-scaled as the contact +unloads, i.e. if :math:`a < a_{t_{n-1}}`: + +.. math:: + + \mathbf{F}_{te} = \mathbf{F}_{te, t_{n-1}} \frac{a}{a_{t_{n-1}}} + +This approach provides a better approximation of the :ref:`Mindlin-Deresiewicz ` +laws and is more consistent than *mindlin_rescale*. See discussions in +:ref:`Thornton et al, 2013 `, particularly equation 18(b) of that +work and associated discussion, and :ref:`Agnolin and Roux, 2007 `, +particularly Appendix A. ---------- @@ -460,7 +529,7 @@ exceeds a critical value: .. math:: - \mathbf{F}_{roll} = min(\mu_{roll} F_{n,0}, \|\mathbf{F}_{roll,0}\|)\mathbf{k} + \mathbf{F}_{roll} = \min(\mu_{roll} F_{n,0}, \|\mathbf{F}_{roll,0}\|)\mathbf{k} Here, :math:`\mathbf{k} = \mathbf{v}_{roll}/\|\mathbf{v}_{roll}\|` is the direction of the pseudo-force. As with tangential displacement, the rolling @@ -512,7 +581,7 @@ is then truncated according to: .. math:: - \tau_{twist} = min(\mu_{twist} F_{n,0}, \tau_{twist,0}) + \tau_{twist} = \min(\mu_{twist} F_{n,0}, \tau_{twist,0}) Similar to the sliding and rolling displacement, the angular displacement is rescaled so that it corresponds to the critical value @@ -763,3 +832,15 @@ Technology, 233, 30-46. .. _WaltonPC: **(Otis R. Walton)** Walton, O.R., Personal Communication + +.. _Mindlin1953: + +**(Mindlin and Deresiewicz, 1953)** Mindlin, R.D., & Deresiewicz, H (1953). +Elastic Spheres in Contact under Varying Oblique Force. +J. Appl. Mech., ASME 20, 327-344. + +.. _AgnolinRoux2007: + +**(Agnolin and Roux 2007)** Agnolin, I. & Roux, J-N. (2007). +Internal states of model isotropic granular packings. +I. Assembling process, geometry, and contact networks. Phys. Rev. E, 76, 061302. diff --git a/doc/utils/sphinx-config/false_positives.txt b/doc/utils/sphinx-config/false_positives.txt index 2bf3e917cf..443478a9fc 100644 --- a/doc/utils/sphinx-config/false_positives.txt +++ b/doc/utils/sphinx-config/false_positives.txt @@ -43,6 +43,7 @@ Afshar agilio Agilio agni +Agnolin Ai Aidan aij @@ -599,6 +600,7 @@ Dequidt der dereference derekt +Deresiewicz Derjagin Derjaguin Derlet @@ -2219,6 +2221,7 @@ oxdna oxrna oxDNA oxRNA +packings padua Padua palegoldenrod diff --git a/src/GRANULAR/pair_granular.cpp b/src/GRANULAR/pair_granular.cpp index 80f208a1a2..8bf2d1bf17 100644 --- a/src/GRANULAR/pair_granular.cpp +++ b/src/GRANULAR/pair_granular.cpp @@ -55,7 +55,8 @@ using namespace MathSpecial; enum {HOOKE, HERTZ, HERTZ_MATERIAL, DMT, JKR}; enum {VELOCITY, MASS_VELOCITY, VISCOELASTIC, TSUJI}; enum {TANGENTIAL_NOHISTORY, TANGENTIAL_HISTORY, - TANGENTIAL_MINDLIN, TANGENTIAL_MINDLIN_RESCALE}; + TANGENTIAL_MINDLIN, TANGENTIAL_MINDLIN_RESCALE, + TANGENTIAL_MINDLIN_FORCE, TANGENTIAL_MINDLIN_RESCALE_FORCE}; enum {TWIST_NONE, TWIST_SDS, TWIST_MARSHALL}; enum {ROLL_NONE, ROLL_SDS}; @@ -175,7 +176,8 @@ void PairGranular::compute(int eflag, int vflag) double signtwist, magtwist, magtortwist, Mtcrit; double tortwist1, tortwist2, tortwist3; - double shrmag,rsht; + double shrmag,rsht,prjmag; + bool frameupdate; int *ilist,*jlist,*numneigh,**firstneigh; int *touch,**firsttouch; double *history,*allhistory,**firsthistory; @@ -372,6 +374,12 @@ void PairGranular::compute(int eflag, int vflag) // tangential force, including history effects //**************************************** + // For linear, mindlin, mindlin_rescale: + // history = cumulative tangential displacement + // + // For mindlin/force, mindlin_rescale/force: + // history = cumulative tangential elastic force + // tangential component vt1 = vr1 - vn1; vt2 = vr2 - vn2; @@ -414,12 +422,15 @@ void PairGranular::compute(int eflag, int vflag) damp_normal_prefactor; if (tangential_history) { - if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN) { + if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_FORCE) { k_tangential *= a; } else if (tangential_model[itype][jtype] == - TANGENTIAL_MINDLIN_RESCALE) { + TANGENTIAL_MINDLIN_RESCALE || + tangential_model[itype][jtype] == + TANGENTIAL_MINDLIN_RESCALE_FORCE) { k_tangential *= a; - // on unloading, rescale the shear displacements + // on unloading, rescale the shear displacements/force if (a < history[3]) { double factor = a/history[3]; history[0] *= factor; @@ -427,37 +438,66 @@ void PairGranular::compute(int eflag, int vflag) history[2] *= factor; } } - // rotate and update displacements. + // rotate and update displacements / force. // see e.g. eq. 17 of Luding, Gran. Matter 2008, v10,p235 if (historyupdate) { rsht = history[0]*nx + history[1]*ny + history[2]*nz; - if (fabs(rsht) < EPSILON) rsht = 0; - if (rsht > 0) { + if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_FORCE || + tangential_model[itype][jtype] == + TANGENTIAL_MINDLIN_RESCALE_FORCE) + frameupdate = fabs(rsht) < EPSILON*Fscrit; + else + frameupdate = fabs(rsht)*k_tangential < EPSILON*Fscrit; + if (frameupdate) { shrmag = sqrt(history[0]*history[0] + history[1]*history[1] + history[2]*history[2]); - // if rsht == shrmag, contacting pair has rotated 90 deg - // in one step, in which case you deserve a crash! - scalefac = shrmag/(shrmag - rsht); + // projection history[0] -= rsht*nx; history[1] -= rsht*ny; history[2] -= rsht*nz; + // also rescale to preserve magnitude + prjmag = sqrt(history[0]*history[0] + history[1]*history[1] + + history[2]*history[2]); + if (prjmag > 0) scalefac = shrmag/prjmag; + else scalefac = 0; history[0] *= scalefac; history[1] *= scalefac; history[2] *= scalefac; } // update history - history[0] += vtr1*dt; - history[1] += vtr2*dt; - history[2] += vtr3*dt; - if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_RESCALE) + if (tangential_model[itype][jtype] == TANGENTIAL_HISTORY || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_RESCALE) { + // tangential displacement + history[0] += vtr1*dt; + history[1] += vtr2*dt; + history[2] += vtr3*dt; + } else { + // tangential force + // see e.g. eq. 18 of Thornton et al, Pow. Tech. 2013, v223,p30-46 + history[0] -= k_tangential*vtr1*dt; + history[1] -= k_tangential*vtr2*dt; + history[2] -= k_tangential*vtr3*dt; + } + if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_RESCALE || + tangential_model[itype][jtype] == + TANGENTIAL_MINDLIN_RESCALE_FORCE) history[3] = a; } // tangential forces = history + tangential velocity damping - fs1 = -k_tangential*history[0] - damp_tangential*vtr1; - fs2 = -k_tangential*history[1] - damp_tangential*vtr2; - fs3 = -k_tangential*history[2] - damp_tangential*vtr3; + if (tangential_model[itype][jtype] == TANGENTIAL_HISTORY || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_RESCALE) { + fs1 = -k_tangential*history[0] - damp_tangential*vtr1; + fs2 = -k_tangential*history[1] - damp_tangential*vtr2; + fs3 = -k_tangential*history[2] - damp_tangential*vtr3; + } else { + fs1 = history[0] - damp_tangential*vtr1; + fs2 = history[1] - damp_tangential*vtr2; + fs3 = history[2] - damp_tangential*vtr3; + } // rescale frictional displacements and forces if needed fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); @@ -465,12 +505,21 @@ void PairGranular::compute(int eflag, int vflag) shrmag = sqrt(history[0]*history[0] + history[1]*history[1] + history[2]*history[2]); if (shrmag != 0.0) { - history[0] = -1.0/k_tangential*(Fscrit*fs1/fs + - damp_tangential*vtr1); - history[1] = -1.0/k_tangential*(Fscrit*fs2/fs + - damp_tangential*vtr2); - history[2] = -1.0/k_tangential*(Fscrit*fs3/fs + - damp_tangential*vtr3); + if (tangential_model[itype][jtype] == TANGENTIAL_HISTORY || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN || + tangential_model[itype][jtype] == + TANGENTIAL_MINDLIN_RESCALE) { + history[0] = -1.0/k_tangential*(Fscrit*fs1/fs + + damp_tangential*vtr1); + history[1] = -1.0/k_tangential*(Fscrit*fs2/fs + + damp_tangential*vtr2); + history[2] = -1.0/k_tangential*(Fscrit*fs3/fs + + damp_tangential*vtr3); + } else { + history[0] = Fscrit*fs1/fs + damp_tangential*vtr1; + history[1] = Fscrit*fs2/fs + damp_tangential*vtr2; + history[2] = Fscrit*fs3/fs + damp_tangential*vtr3; + } fs1 *= Fscrit/fs; fs2 *= Fscrit/fs; fs3 *= Fscrit/fs; @@ -512,18 +561,27 @@ void PairGranular::compute(int eflag, int vflag) int rhist1 = rhist0 + 1; int rhist2 = rhist1 + 1; - rolldotn = history[rhist0]*nx + history[rhist1]*ny + history[rhist2]*nz; + k_roll = roll_coeffs[itype][jtype][0]; + damp_roll = roll_coeffs[itype][jtype][1]; + Frcrit = roll_coeffs[itype][jtype][2] * Fncrit; + if (historyupdate) { - if (fabs(rolldotn) < EPSILON) rolldotn = 0; - if (rolldotn > 0) { // rotate into tangential plane + rolldotn = history[rhist0]*nx + history[rhist1]*ny + history[rhist2]*nz; + frameupdate = fabs(rolldotn)*k_roll < EPSILON*Frcrit; + if (frameupdate) { // rotate into tangential plane rollmag = sqrt(history[rhist0]*history[rhist0] + history[rhist1]*history[rhist1] + history[rhist2]*history[rhist2]); - scalefac = rollmag/(rollmag - rolldotn); + // projection history[rhist0] -= rolldotn*nx; history[rhist1] -= rolldotn*ny; history[rhist2] -= rolldotn*nz; // also rescale to preserve magnitude + prjmag = sqrt(history[rhist0]*history[rhist0] + + history[rhist1]*history[rhist1] + + history[rhist2]*history[rhist2]); + if (prjmag > 0) scalefac = rollmag/prjmag; + else scalefac = 0; history[rhist0] *= scalefac; history[rhist1] *= scalefac; history[rhist2] *= scalefac; @@ -533,14 +591,11 @@ void PairGranular::compute(int eflag, int vflag) history[rhist2] += vrl3*dt; } - k_roll = roll_coeffs[itype][jtype][0]; - damp_roll = roll_coeffs[itype][jtype][1]; fr1 = -k_roll*history[rhist0] - damp_roll*vrl1; fr2 = -k_roll*history[rhist1] - damp_roll*vrl2; fr3 = -k_roll*history[rhist2] - damp_roll*vrl3; // rescale frictional displacements and forces if needed - Frcrit = roll_coeffs[itype][jtype][2] * Fncrit; fr = sqrt(fr1*fr1 + fr2*fr2 + fr3*fr3); if (fr > Frcrit) { @@ -734,7 +789,8 @@ void PairGranular::coeff(int narg, char **arg) //Defaults normal_model_one = tangential_model_one = -1; - roll_model_one = twist_model_one = 0; + roll_model_one = ROLL_NONE; + twist_model_one = TWIST_NONE; damping_model_one = VISCOELASTIC; int iarg = 2; @@ -820,7 +876,9 @@ void PairGranular::coeff(int narg, char **arg) iarg += 4; } else if ((strcmp(arg[iarg+1], "linear_history") == 0) || (strcmp(arg[iarg+1], "mindlin") == 0) || - (strcmp(arg[iarg+1], "mindlin_rescale") == 0)) { + (strcmp(arg[iarg+1], "mindlin_rescale") == 0) || + (strcmp(arg[iarg+1], "mindlin/force") == 0) || + (strcmp(arg[iarg+1], "mindlin_rescale/force") == 0)) { if (iarg + 4 >= narg) error->all(FLERR,"Illegal pair_coeff command, " "not enough parameters provided for tangential model"); @@ -830,9 +888,15 @@ void PairGranular::coeff(int narg, char **arg) tangential_model_one = TANGENTIAL_MINDLIN; else if (strcmp(arg[iarg+1], "mindlin_rescale") == 0) tangential_model_one = TANGENTIAL_MINDLIN_RESCALE; + else if (strcmp(arg[iarg+1], "mindlin/force") == 0) + tangential_model_one = TANGENTIAL_MINDLIN_FORCE; + else if (strcmp(arg[iarg+1], "mindlin_rescale/force") == 0) + tangential_model_one = TANGENTIAL_MINDLIN_RESCALE_FORCE; tangential_history = 1; if ((tangential_model_one == TANGENTIAL_MINDLIN || - tangential_model_one == TANGENTIAL_MINDLIN_RESCALE) && + tangential_model_one == TANGENTIAL_MINDLIN_RESCALE || + tangential_model_one == TANGENTIAL_MINDLIN_FORCE || + tangential_model_one == TANGENTIAL_MINDLIN_RESCALE_FORCE) && (strcmp(arg[iarg+2], "NULL") == 0)) { if (normal_model_one == HERTZ || normal_model_one == HOOKE) { error->all(FLERR, "NULL setting for Mindlin tangential " @@ -1014,7 +1078,8 @@ void PairGranular::init_style() } for (int i = 1; i <= atom->ntypes; i++) for (int j = i; j <= atom->ntypes; j++) - if (tangential_model[i][j] == TANGENTIAL_MINDLIN_RESCALE) { + if (tangential_model[i][j] == TANGENTIAL_MINDLIN_RESCALE || + tangential_model[i][j] == TANGENTIAL_MINDLIN_RESCALE_FORCE) { size_history += 1; roll_history_index += 1; twist_history_index += 1; @@ -1484,6 +1549,12 @@ double PairGranular::single(int i, int j, int itype, int jtype, // tangential force, including history effects //**************************************** + // For linear, mindlin, mindlin_rescale: + // history = cumulative tangential displacement + // + // For mindlin/force, mindlin_rescale/force: + // history = cumulative tangential elastic force + // tangential component vt1 = vr1 - vn1; vt2 = vr2 - vn2; @@ -1519,9 +1590,7 @@ double PairGranular::single(int i, int j, int itype, int jtype, damp_tangential = tangential_coeffs[itype][jtype][1]*damp_normal_prefactor; if (tangential_history) { - if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN) { - k_tangential *= a; - } else if (tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_RESCALE) { + if (tangential_model[itype][jtype] != TANGENTIAL_HISTORY) { k_tangential *= a; } @@ -1529,9 +1598,17 @@ double PairGranular::single(int i, int j, int itype, int jtype, history[2]*history[2]); // tangential forces = history + tangential velocity damping - fs1 = -k_tangential*history[0] - damp_tangential*vtr1; - fs2 = -k_tangential*history[1] - damp_tangential*vtr2; - fs3 = -k_tangential*history[2] - damp_tangential*vtr3; + if (tangential_model[itype][jtype] == TANGENTIAL_HISTORY || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN || + tangential_model[itype][jtype] == TANGENTIAL_MINDLIN_RESCALE) { + fs1 = -k_tangential*history[0] - damp_tangential*vtr1; + fs2 = -k_tangential*history[1] - damp_tangential*vtr2; + fs3 = -k_tangential*history[2] - damp_tangential*vtr3; + } else { + fs1 = history[0] - damp_tangential*vtr1; + fs2 = history[1] - damp_tangential*vtr2; + fs3 = history[2] - damp_tangential*vtr3; + } // rescale frictional forces if needed fs = sqrt(fs1*fs1 + fs2*fs2 + fs3*fs3); @@ -1540,7 +1617,7 @@ double PairGranular::single(int i, int j, int itype, int jtype, fs1 *= Fscrit/fs; fs2 *= Fscrit/fs; fs3 *= Fscrit/fs; - fs *= Fscrit/fs; + fs *= Fscrit/fs; } else fs1 = fs2 = fs3 = fs = 0.0; } diff --git a/src/USER-REACTION/fix_bond_react.cpp b/src/USER-REACTION/fix_bond_react.cpp index 2c6c20c844..29aa476148 100644 --- a/src/USER-REACTION/fix_bond_react.cpp +++ b/src/USER-REACTION/fix_bond_react.cpp @@ -33,6 +33,7 @@ Contributing Author: Jacob Gissinger (jacob.gissinger@colorado.edu) #include "neigh_list.h" #include "neigh_request.h" #include "random_mars.h" +#include "reset_mol_ids.h" #include "molecule.h" #include "group.h" #include "citeme.h" @@ -92,6 +93,7 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : fix1 = NULL; fix2 = NULL; fix3 = NULL; + reset_mol_ids = NULL; if (narg < 8) error->all(FLERR,"Illegal fix bond/react command: " "too few arguments"); @@ -144,7 +146,8 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : int iarg = 3; stabilization_flag = 0; - int num_common_keywords = 1; + reset_mol_ids_flag = 1; + int num_common_keywords = 2; for (int m = 0; m < num_common_keywords; m++) { if (strcmp(arg[iarg],"stabilization") == 0) { if (strcmp(arg[iarg+1],"no") == 0) { @@ -162,11 +165,23 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : nve_limit_xmax = arg[iarg+3]; iarg += 4; } + } else if (strcmp(arg[iarg],"reset_mol_ids") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal fix bond/react command: " + "'reset_mol_ids' keyword has too few arguments"); + if (strcmp(arg[iarg+1],"yes") == 0) ; // default + if (strcmp(arg[iarg+1],"no") == 0) reset_mol_ids_flag = 0; + iarg += 2; } else if (strcmp(arg[iarg],"react") == 0) { break; } else error->all(FLERR,"Illegal fix bond/react command: unknown keyword"); } + if (reset_mol_ids_flag) { + delete reset_mol_ids; + reset_mol_ids = new ResetMolIDs(lmp); + reset_mol_ids->create_computes(id,group->names[igroup]); + } + // set up common variables as vectors of length 'nreacts' // nevery, cutoff, onemol, twomol, superimpose file @@ -229,11 +244,11 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : int n = strlen(arg[iarg]) + 1; if (n > MAXLINE) error->all(FLERR,"Reaction name (react-ID) is too long (limit: 256 characters)"); - strncpy(rxn_name[rxn],arg[iarg++],n); + strcpy(rxn_name[rxn],arg[iarg++]); - int igroup = group->find(arg[iarg++]); - if (igroup == -1) error->all(FLERR,"Could not find fix group ID"); - groupbits[rxn] = group->bitmask[igroup]; + int groupid = group->find(arg[iarg++]); + if (groupid == -1) error->all(FLERR,"Could not find fix group ID"); + groupbits[rxn] = group->bitmask[groupid]; if (strncmp(arg[iarg],"v_",2) == 0) { n = strlen(&arg[iarg][2]) + 1; @@ -499,6 +514,8 @@ FixBondReact::~FixBondReact() } delete [] random; + delete reset_mol_ids; + memory->destroy(partner); memory->destroy(finalpartner); memory->destroy(ncreate); @@ -623,9 +640,9 @@ void FixBondReact::post_constructor() group->assign(cmd); if (stabilization_flag == 1) { - int igroup = group->find(exclude_group); + int groupid = group->find(exclude_group); // create exclude_group if not already existing, or use as parent group if static - if (igroup == -1 || group->dynamic[igroup] == 0) { + if (groupid == -1 || group->dynamic[groupid] == 0) { // create stabilization per-atom property cmd = std::string("bond_react_stabilization_internal"); id_fix3 = new char[cmd.size()+1]; @@ -655,7 +672,7 @@ void FixBondReact::post_constructor() strcat(exclude_group,"_REACT"); group->find_or_create(exclude_group); - if (igroup == -1) + if (groupid == -1) cmd = fmt::format("{} dynamic all property statted_tags",exclude_group); else cmd = fmt::format("{} dynamic {} property statted_tags",exclude_group,exclude_PARENT_group); @@ -2061,7 +2078,7 @@ void FixBondReact::find_landlocked_atoms(int myrxn) for (int i = 0; i < twomol->natoms; i++) { if (twomol->type[i] != onemol->type[equivalences[i][1][myrxn]-1] && landlocked_atoms[i][myrxn] == 0) { char str[128]; - snprintf(str,128,"Bond/react: Atom affected by reaction %s too close to template edge",rxn_name[myrxn]); + snprintf(str,128,"Bond/react: Atom type affected by reaction %s too close to template edge",rxn_name[myrxn]); error->all(FLERR,str); } } @@ -2080,7 +2097,7 @@ void FixBondReact::find_landlocked_atoms(int myrxn) if (onemol_batom == equivalences[twomol_atomj-1][1][myrxn]) { if (twomol->bond_type[i][j] != onemol->bond_type[onemol_atomi-1][m]) { char str[128]; - snprintf(str,128,"Bond/react: Atom affected by reaction %s too close to template edge",rxn_name[myrxn]); + snprintf(str,128,"Bond/react: Bond type affected by reaction %s too close to template edge",rxn_name[myrxn]); error->all(FLERR,str); } } @@ -2092,7 +2109,7 @@ void FixBondReact::find_landlocked_atoms(int myrxn) if (onemol_batom == equivalences[i][1][myrxn]) { if (twomol->bond_type[i][j] != onemol->bond_type[onemol_atomj-1][m]) { char str[128]; - snprintf(str,128,"Bond/react: Atom affected by reaction %s too close to template edge",rxn_name[myrxn]); + snprintf(str,128,"Bond/react: Bond type affected by reaction %s too close to template edge",rxn_name[myrxn]); error->all(FLERR,str); } } @@ -2503,7 +2520,7 @@ void FixBondReact::ghost_glovecast() } /* ---------------------------------------------------------------------- -update charges, types, special lists and all topology +update molecule IDs, charges, types, special lists and all topology ------------------------------------------------------------------------- */ void FixBondReact::update_everything() @@ -3042,6 +3059,9 @@ void FixBondReact::update_everything() atom->natoms -= ndel; // done deleting atoms + // reset mol ids + if (reset_mol_ids_flag) reset_mol_ids->reset(); + // something to think about: this could done much more concisely if // all atom-level info (bond,angles, etc...) were kinda inherited from a common data struct --JG diff --git a/src/USER-REACTION/fix_bond_react.h b/src/USER-REACTION/fix_bond_react.h index e8a4253ce7..61a1bd4213 100644 --- a/src/USER-REACTION/fix_bond_react.h +++ b/src/USER-REACTION/fix_bond_react.h @@ -61,6 +61,7 @@ class FixBondReact : public Fix { int *max_rxn,*nlocalskips,*nghostlyskips; tagint lastcheck; int stabilization_flag; + int reset_mol_ids_flag; int custom_exclude_flag; int *stabilize_steps_flag; int *update_edges_flag; @@ -93,6 +94,7 @@ class FixBondReact : public Fix { class RanMars **random; // random number for 'prob' keyword class RanMars **rrhandom; // random number for Arrhenius constraint class NeighList *list; + class ResetMolIDs *reset_mol_ids; // class for resetting mol IDs int *reacted_mol,*unreacted_mol; int *limit_duration; // indicates how long to relax @@ -240,8 +242,10 @@ E: Bond/react: Invalid template atom ID in map file Atom IDs in molecule templates range from 1 to the number of atoms in the template. E or W: Bond/react: Atom affected by reaction %s too close to template edge + Bond/react: Atom type affected by reaction %s too close to template edge + Bond/react: Bond type affected by reaction %s too close to template edge -This means an atom which changes type or connectivity during the +This means an atom (or bond) that changes type or connectivity during the reaction is too close to an 'edge' atom defined in the map file. This could cause incorrect assignment of bonds, angle, etc. Generally, this means you must include more atoms in your templates, such that there diff --git a/src/reset_mol_ids.cpp b/src/reset_mol_ids.cpp index e3cc877548..a6cfb69185 100644 --- a/src/reset_mol_ids.cpp +++ b/src/reset_mol_ids.cpp @@ -17,7 +17,6 @@ #include "reset_mol_ids.h" #include -#include #include "atom.h" #include "domain.h" #include "comm.h" @@ -33,10 +32,32 @@ using namespace LAMMPS_NS; /* ---------------------------------------------------------------------- */ -ResetMolIDs::ResetMolIDs(LAMMPS *lmp) : Pointers(lmp) {} +ResetMolIDs::ResetMolIDs(LAMMPS *lmp) : Pointers(lmp) { + cfa = NULL; + cca = NULL; + + // default settings + + compressflag = 1; + singleflag = 0; + offset = -1; + + idfrag.clear(); + idchunk.clear(); +} /* ---------------------------------------------------------------------- */ +ResetMolIDs::~ResetMolIDs() +{ + if (!idfrag.empty()) modify->delete_compute(idfrag); + if (compressflag && !idchunk.empty()) modify->delete_compute(idchunk); +} + +/* ---------------------------------------------------------------------- + called as reset_mol_ids command in input script +------------------------------------------------------------------------- */ + void ResetMolIDs::command(int narg, char **arg) { if (domain->box_exist == 0) @@ -49,13 +70,7 @@ void ResetMolIDs::command(int narg, char **arg) // process args if (narg < 1) error->all(FLERR,"Illegal reset_mol_ids command"); - int igroup = group->find(arg[0]); - if (igroup == -1) error->all(FLERR,"Could not find reset_mol_ids group ID"); - int groupbit = group->bitmask[igroup]; - - int compressflag = 1; - int singleflag = 0; - tagint offset = -1; + char *groupid = arg[0]; int iarg = 1; while (iarg < narg) { @@ -86,20 +101,6 @@ void ResetMolIDs::command(int narg, char **arg) MPI_Barrier(world); double time1 = MPI_Wtime(); - // create instances of compute fragment/atom, compute reduce (if needed), - // and compute chunk/atom. all use the group-ID for this command - - const std::string idfrag = "reset_mol_ids_FRAGMENT_ATOM"; - if (singleflag) - modify->add_compute(fmt::format("{} {} fragment/atom single yes",idfrag,arg[0])); - else - modify->add_compute(fmt::format("{} {} fragment/atom single no",idfrag,arg[0])); - - const std::string idchunk = "reset_mol_ids_CHUNK_ATOM"; - if (compressflag) - modify->add_compute(fmt::format("{} {} chunk/atom molecule compress yes", - idchunk,arg[0])); - // initialize system since comm->borders() will be invoked lmp->init(); @@ -116,12 +117,73 @@ void ResetMolIDs::command(int narg, char **arg) comm->borders(); if (domain->triclinic) domain->lamda2x(atom->nlocal+atom->nghost); + // create computes + + create_computes((char *) "COMMAND",groupid); + + // reset molecule IDs + + reset(); + + // total time + + MPI_Barrier(world); + + if (comm->me == 0) { + if (nchunk < 0) + utils::logmesg(lmp,fmt::format(" number of new molecule IDs = unknown\n")); + else + utils::logmesg(lmp,fmt::format(" number of new molecule IDs = {}\n",nchunk)); + utils::logmesg(lmp,fmt::format(" reset_mol_ids CPU = {:.3f} seconds\n", + MPI_Wtime()-time1)); + } +} + +/* ---------------------------------------------------------------------- + create computes used by reset_mol_ids +------------------------------------------------------------------------- */ + +void ResetMolIDs::create_computes(char *fixid, char *groupid) +{ + int igroup = group->find(groupid); + if (igroup == -1) error->all(FLERR,"Could not find reset_mol_ids group ID"); + groupbit = group->bitmask[igroup]; + + // create instances of compute fragment/atom, compute reduce (if needed), + // and compute chunk/atom. all use the group-ID for this command. + // 'fixid' allows for creating independent instances of the computes + + idfrag = fmt::format("{}_reset_mol_ids_FRAGMENT_ATOM",fixid); + if (singleflag) + modify->add_compute(fmt::format("{} {} fragment/atom single yes",idfrag,groupid)); + else + modify->add_compute(fmt::format("{} {} fragment/atom single no",idfrag,groupid)); + + idchunk = fmt::format("{}_reset_mol_ids_CHUNK_ATOM",fixid); + if (compressflag) + modify->add_compute(fmt::format("{} {} chunk/atom molecule compress yes", + idchunk,groupid)); + + int icompute = modify->find_compute(idfrag); + cfa = (ComputeFragmentAtom *) modify->compute[icompute]; + + + if (compressflag) { + icompute = modify->find_compute(idchunk); + cca = (ComputeChunkAtom *) modify->compute[icompute]; + } +} + +/* ---------------------------------------------------------------------- + called from command() and directly from fixes that update molecules +------------------------------------------------------------------------- */ + +void ResetMolIDs::reset() +{ // invoke peratom method of compute fragment/atom // walks bond connectivity and assigns each atom a fragment ID // if singleflag = 0, atoms w/out bonds will be assigned fragID = 0 - int icompute = modify->find_compute(idfrag); - ComputeFragmentAtom *cfa = (ComputeFragmentAtom *) modify->compute[icompute]; cfa->compute_peratom(); double *fragIDs = cfa->vector_atom; @@ -138,15 +200,13 @@ void ResetMolIDs::command(int narg, char **arg) // if compressflag = 0, done // set nchunk = -1 since cannot easily determine # of new molecule IDs - int nchunk = -1; + nchunk = -1; // if compressflag = 1, invoke peratom method of compute chunk/atom // will compress new molecule IDs to be contiguous 1 to Nmol // NOTE: use of compute chunk/atom limits Nmol to a 32-bit int if (compressflag) { - icompute = modify->find_compute(idchunk); - ComputeChunkAtom *cca = (ComputeChunkAtom *) modify->compute[icompute]; cca->compute_peratom(); double *chunkIDs = cca->vector_atom; nchunk = cca->nchunk; @@ -193,22 +253,4 @@ void ResetMolIDs::command(int narg, char **arg) } } } - - // clean up - - modify->delete_compute(idfrag); - if (compressflag) modify->delete_compute(idchunk); - - // total time - - MPI_Barrier(world); - - if (comm->me == 0) { - if (nchunk < 0) - utils::logmesg(lmp,fmt::format(" number of new molecule IDs = unknown\n")); - else - utils::logmesg(lmp,fmt::format(" number of new molecule IDs = {}\n",nchunk)); - utils::logmesg(lmp,fmt::format(" reset_mol_ids CPU = {:.3f} seconds\n", - MPI_Wtime()-time1)); - } } diff --git a/src/reset_mol_ids.h b/src/reset_mol_ids.h index 83c0b5d80f..9d2dd24bc8 100644 --- a/src/reset_mol_ids.h +++ b/src/reset_mol_ids.h @@ -21,13 +21,28 @@ CommandStyle(reset_mol_ids,ResetMolIDs) #define LMP_RESET_MOL_IDS_H #include "pointers.h" +#include namespace LAMMPS_NS { class ResetMolIDs : protected Pointers { public: ResetMolIDs(class LAMMPS *); + ~ResetMolIDs(); void command(int, char **); + void create_computes(char *, char *); + void reset(); + +private: + std::string idfrag, idchunk; + int nchunk; + int groupbit; + int compressflag; // 1 = contiguous values for new IDs + int singleflag; // 0 = mol IDs of single atoms set to 0 + tagint offset; // offset for contiguous mol ID values + + class ComputeFragmentAtom *cfa; + class ComputeChunkAtom *cca; }; } diff --git a/src/utils.cpp b/src/utils.cpp index 8c19e889cd..f841086673 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -352,7 +352,7 @@ tagint utils::tnumeric(const char *file, int line, const char *str, Return string without leading or trailing whitespace ------------------------------------------------------------------------- */ -std::string utils::trim(const std::string & line) { +std::string utils::trim(const std::string &line) { int beg = re_match(line.c_str(),"\\S+"); int end = re_match(line.c_str(),"\\s+$"); if (beg < 0) beg = 0; @@ -365,7 +365,7 @@ std::string utils::trim(const std::string & line) { Return string without trailing # comment ------------------------------------------------------------------------- */ -std::string utils::trim_comment(const std::string & line) { +std::string utils::trim_comment(const std::string &line) { auto end = line.find_first_of("#"); if (end != std::string::npos) { return line.substr(0, end); @@ -377,7 +377,7 @@ std::string utils::trim_comment(const std::string & line) { return number of words ------------------------------------------------------------------------- */ -size_t utils::count_words(const char * text) { +size_t utils::count_words(const char *text) { size_t count = 0; const char * buf = text; char c = *buf; @@ -406,7 +406,7 @@ size_t utils::count_words(const char * text) { return number of words ------------------------------------------------------------------------- */ -size_t utils::count_words(const std::string & text) { +size_t utils::count_words(const std::string &text) { return utils::count_words(text.c_str()); } @@ -414,7 +414,7 @@ size_t utils::count_words(const std::string & text) { Return number of words ------------------------------------------------------------------------- */ -size_t utils::count_words(const std::string & text, const std::string & separators) { +size_t utils::count_words(const std::string &text, const std::string &separators) { size_t count = 0; size_t start = text.find_first_not_of(separators); @@ -435,7 +435,7 @@ size_t utils::count_words(const std::string & text, const std::string & separato Trim comment from string and return number of words ------------------------------------------------------------------------- */ -size_t utils::trim_and_count_words(const std::string & text, const std::string & separators) { +size_t utils::trim_and_count_words(const std::string &text, const std::string &separators) { return utils::count_words(utils::trim_comment(text), separators); } @@ -526,7 +526,7 @@ std::vector utils::split_words(const std::string &text) Return whether string is a valid integer number ------------------------------------------------------------------------- */ -bool utils::is_integer(const std::string & str) { +bool utils::is_integer(const std::string &str) { if (str.size() == 0) { return false; } @@ -542,7 +542,7 @@ bool utils::is_integer(const std::string & str) { Return whether string is a valid floating-point number ------------------------------------------------------------------------- */ -bool utils::is_double(const std::string & str) { +bool utils::is_double(const std::string &str) { if (str.size() == 0) { return false; } @@ -560,7 +560,7 @@ bool utils::is_double(const std::string & str) { strip off leading part of path, return just the filename ------------------------------------------------------------------------- */ -std::string utils::path_basename(const std::string & path) { +std::string utils::path_basename(const std::string &path) { #if defined(_WIN32) size_t start = path.find_last_of("/\\"); #else @@ -580,7 +580,7 @@ std::string utils::path_basename(const std::string & path) { join two paths ------------------------------------------------------------------------- */ -std::string utils::path_join(const std::string & a, const std::string & b) { +std::string utils::path_join(const std::string &a, const std::string &b) { #if defined(_WIN32) return fmt::format("{}\\{}", a, b); #else @@ -592,7 +592,7 @@ std::string utils::path_join(const std::string & a, const std::string & b) { try to open file for reading ------------------------------------------------------------------------- */ -bool utils::file_is_readable(const std::string & path) { +bool utils::file_is_readable(const std::string &path) { FILE * fp = fopen(path.c_str(), "r"); if(fp) { fclose(fp); @@ -607,7 +607,7 @@ bool utils::file_is_readable(const std::string & path) { specified ------------------------------------------------------------------------- */ -std::string utils::get_potential_file_path(const std::string& path) { +std::string utils::get_potential_file_path(const std::string &path) { std::string filepath = path; std::string filename = utils::path_basename(path); @@ -634,7 +634,7 @@ std::string utils::get_potential_file_path(const std::string& path) { if it has a DATE field, return the following word ------------------------------------------------------------------------- */ -std::string utils::get_potential_date(const std::string & path, const std::string & potential_name) { +std::string utils::get_potential_date(const std::string &path, const std::string &potential_name) { TextFileReader reader(path, potential_name); reader.ignore_comments = false; @@ -657,7 +657,7 @@ std::string utils::get_potential_date(const std::string & path, const std::strin if it has UNITS field, return following word ------------------------------------------------------------------------- */ -std::string utils::get_potential_units(const std::string & path, const std::string & potential_name) { +std::string utils::get_potential_units(const std::string &path, const std::string &potential_name) { TextFileReader reader(path, potential_name); reader.ignore_comments = false; @@ -710,7 +710,7 @@ double utils::get_conversion_factor(const int property, const int conversion) the strings "off" and "unlimited" result in -1.0; ------------------------------------------------------------------------- */ -double utils::timespec2seconds(const std::string & timespec) +double utils::timespec2seconds(const std::string ×pec) { double vals[3]; int i = 0; @@ -728,7 +728,7 @@ double utils::timespec2seconds(const std::string & timespec) if (!values.has_next()) break; vals[i] = values.next_int(); } - } catch (TokenizerException & e) { + } catch (TokenizerException &e) { return -1.0; } diff --git a/src/utils.h b/src/utils.h index 2751870d44..4b9d3f7e81 100644 --- a/src/utils.h +++ b/src/utils.h @@ -29,7 +29,7 @@ namespace LAMMPS_NS { namespace utils { - /** \brief Match text against a simplified regex pattern + /** Match text against a simplified regex pattern * * \param text the text to be matched against the pattern * \param pattern the search pattern, which may contain regexp markers @@ -37,14 +37,14 @@ namespace LAMMPS_NS { */ bool strmatch(const std::string &text, const std::string &pattern); - /** \brief Send message to screen and logfile, if available + /** Send message to screen and logfile, if available * * \param lmp pointer to LAMMPS class instance * \param mesg message to be printed */ void logmesg(LAMMPS *lmp, const std::string &mesg); - /** \brief return a string representing the current system error status + /** return a string representing the current system error status * * This is a wrapper around calling strerror(errno). * @@ -52,7 +52,7 @@ namespace LAMMPS_NS { */ std::string getsyserror(); - /** \brief safe wrapper around fgets() which aborts on errors + /** safe wrapper around fgets() which aborts on errors * or EOF and prints a suitable error message to help debugging * * \param srcname name of the calling source file (from FLERR macro) @@ -66,7 +66,7 @@ namespace LAMMPS_NS { void sfgets(const char *srcname, int srcline, char *s, int size, FILE *fp, const char *filename, Error *error); - /** \brief safe wrapper around fread() which aborts on errors + /** safe wrapper around fread() which aborts on errors * or EOF and prints a suitable error message to help debugging * * \param srcname name of the calling source file (from FLERR macro) @@ -81,7 +81,7 @@ namespace LAMMPS_NS { void sfread(const char *srcname, int srcline, void *s, size_t size, size_t num, FILE *fp, const char *filename, Error *error); - /** \brief Report if a requested style is in a package or may have a typo + /** Report if a requested style is in a package or may have a typo * * \param style type of style that is to be checked for * \param name name of style that was not found @@ -91,8 +91,8 @@ namespace LAMMPS_NS { std::string check_packages_for_style(const std::string &style, const std::string &name, LAMMPS *lmp); - /** \brief Convert a string to a floating point number while checking - if it is a valid floating point or integer number + /** Convert a string to a floating point number while checking + * if it is a valid floating point or integer number * * \param file name of source file for error message * \param line in source file for error message @@ -104,8 +104,8 @@ namespace LAMMPS_NS { double numeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); - /** \brief Convert a string to an integer number while checking - if it is a valid integer number (regular int) + /** Convert a string to an integer number while checking + * if it is a valid integer number (regular int) * * \param file name of source file for error message * \param line in source file for error message @@ -117,8 +117,8 @@ namespace LAMMPS_NS { int inumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); - /** \brief Convert a string to an integer number while checking - if it is a valid integer number (bigint) + /** Convert a string to an integer number while checking + * if it is a valid integer number (bigint) * * \param file name of source file for error message * \param line in source file for error message @@ -130,8 +130,8 @@ namespace LAMMPS_NS { bigint bnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); - /** \brief Convert a string to an integer number while checking - if it is a valid integer number (tagint) + /** Convert a string to an integer number while checking + * if it is a valid integer number (tagint) * * \param file name of source file for error message * \param line in source file for error message @@ -143,54 +143,51 @@ namespace LAMMPS_NS { tagint tnumeric(const char *file, int line, const char *str, bool do_abort, LAMMPS *lmp); - /** - * \brief Trim leading and trailing whitespace. Like TRIM() in Fortran. + /** Trim leading and trailing whitespace. Like TRIM() in Fortran. + * * \param line string that should be trimmed * \return new string without whitespace (string) */ std::string trim(const std::string &line); - /** - * \brief Trim anything from '#' onward + /** Trim anything from '#' onward + * * \param line string that should be trimmed * \return new string without comment (string) */ std::string trim_comment(const std::string &line); - /** - * \brief Count words in string + /** Count words in string + * * \param text string that should be searched * \param separators string containing characters that will be treated as whitespace * \return number of words found */ - size_t count_words(const std::string & text, const std::string & separators); + size_t count_words(const std::string &text, const std::string &separators); - /** - * \brief Count words in string, ignore any whitespace matching " \t\r\n\f" + /** Count words in string, ignore any whitespace matching " \t\r\n\f" + * * \param text string that should be searched - * \param separators string containing characters that will be treated as whitespace * \return number of words found */ - size_t count_words(const std::string & text); + size_t count_words(const std::string &text); - /** - * \brief Count words in C-string, ignore any whitespace matching " \t\r\n\f" + /** Count words in C-string, ignore any whitespace matching " \t\r\n\f" + * * \param text string that should be searched - * \param separators string containing characters that will be treated as whitespace * \return number of words found */ - size_t count_words(const char * text); + size_t count_words(const char *text); - /** - * \brief Count words in a single line, trim anything from '#' onward + /** Count words in a single line, trim anything from '#' onward + * * \param text string that should be trimmed and searched * \param separators string containing characters that will be treated as whitespace * \return number of words found */ - size_t trim_and_count_words(const std::string & text, const std::string & separators = " \t\r\n\f"); + size_t trim_and_count_words(const std::string &text, const std::string &separators = " \t\r\n\f"); - /** - * \brief Take text and split into non-whitespace words. + /** Take text and split into non-whitespace words. * * This can handle single and double quotes, escaped quotes, * and escaped codes within quotes, but due to using an STL @@ -203,21 +200,21 @@ namespace LAMMPS_NS { */ std::vector split_words(const std::string &text); - /** - * \brief Check if string can be converted to valid integer - * \param text string that should be checked + /** Check if string can be converted to valid integer + * + * \param str string that should be checked * \return true, if string contains valid integer, false otherwise */ - bool is_integer(const std::string & str); + bool is_integer(const std::string &str); - /** - * \brief Check if string can be converted to valid floating-point number - * \param text string that should be checked + /** Check if string can be converted to valid floating-point number + * + * \param str string that should be checked * \return true, if string contains valid floating-point number, false otherwise */ - bool is_double(const std::string & str); + bool is_double(const std::string &str); - /** \brief try to detect pathname from FILE pointer. + /** Try to detect pathname from FILE pointer. * * Currently only supported on Linux, otherwise will report "(unknown)". * @@ -228,12 +225,12 @@ namespace LAMMPS_NS { */ const char *guesspath(char *buf, int len, FILE *fp); - /** - * \brief Strip off leading part of path, return just the filename + /** Strip off leading part of path, return just the filename + * * \param path file path * \return file name */ - std::string path_basename(const std::string & path); + std::string path_basename(const std::string &path); /** * \brief Join two paths @@ -241,64 +238,65 @@ namespace LAMMPS_NS { * \param b second path * \return combined path */ - std::string path_join(const std::string & a, const std::string & b); + std::string path_join(const std::string &a, const std::string &b); /** * \brief Check if file exists and is readable * \param path file path * \return true if file exists and is readable */ - bool file_is_readable(const std::string & path); + bool file_is_readable(const std::string &path); - /** - * \brief Determine full path of potential file - * If file is not found in current directory, search LAMMPS_POTENTIALS folder + /** Determine full path of potential file. If file is not found in current directory, + * search LAMMPS_POTENTIALS folder + * * \param path file path * \return full path to potential file */ - std::string get_potential_file_path(const std::string& path); + std::string get_potential_file_path(const std::string &path); - /** - * \brief Read potential file and return DATE field if it is present + /** Read potential file and return DATE field if it is present + * * \param path file path * \param potential_name name of potential that is being read * \return DATE field if present */ - std::string get_potential_date(const std::string & path, const std::string & potential_name); + std::string get_potential_date(const std::string &path, const std::string &potential_name); - /** - * \brief Read potential file and return UNITS field if it is present + /** Read potential file and return UNITS field if it is present + * * \param path file path * \param potential_name name of potential that is being read * \return UNITS field if present */ - std::string get_potential_units(const std::string & path, const std::string & potential_name); + std::string get_potential_units(const std::string &path, const std::string &potential_name); enum { NOCONVERT = 0, METAL2REAL = 1, REAL2METAL = 1<<1 }; enum { UNKNOWN = 0, ENERGY }; - /** - * \brief Return bitmask of available conversion factors for a given propert + /** Return bitmask of available conversion factors for a given property + * * \param property property to be converted * \return bitmask indicating available conversions */ int get_supported_conversions(const int property); - /** - * \brief Return unit conversion factor for given property and selected from/to units + /** Return unit conversion factor for given property and selected from/to units + * * \param property property to be converted * \param conversion constant indicating the conversion * \return conversion factor */ double get_conversion_factor(const int property, const int conversion); - /** - * \brief Convert a time string to seconds - * The strings "off" and "unlimited" result in -1 + /** Convert a time string to seconds + * + * The strings "off" and "unlimited" result in -1 + * * \param timespec a string in the following format: ([[HH:]MM:]SS) * \return total in seconds */ - double timespec2seconds(const std::string & timespec); + double timespec2seconds(const std::string ×pec); } } diff --git a/unittest/force-styles/tests/atomic-pair-reax_c.yaml b/unittest/force-styles/tests/atomic-pair-reax_c.yaml index a1fea19d79..15f7bf02ab 100644 --- a/unittest/force-styles/tests/atomic-pair-reax_c.yaml +++ b/unittest/force-styles/tests/atomic-pair-reax_c.yaml @@ -1,7 +1,7 @@ --- lammps_version: 21 Aug 2020 date_generated: Sun Aug 23 06:35:54 202 -epsilon: 5e-12 +epsilon: 7.5e-12 prerequisites: ! | pair reax/c fix qeq/reax diff --git a/unittest/force-styles/tests/atomic-pair-reax_c_lgvdw.yaml b/unittest/force-styles/tests/atomic-pair-reax_c_lgvdw.yaml index 1383339444..7d549efc18 100644 --- a/unittest/force-styles/tests/atomic-pair-reax_c_lgvdw.yaml +++ b/unittest/force-styles/tests/atomic-pair-reax_c_lgvdw.yaml @@ -1,7 +1,7 @@ --- lammps_version: 21 Aug 2020 date_generated: Sun Aug 23 06:41:04 202 -epsilon: 5e-11 +epsilon: 1e-10 prerequisites: ! | pair reax/c fix qeq/reax