Add APIP package.

This commit is contained in:
David Immel
2025-04-08 15:26:45 +02:00
parent 16db8e1515
commit 535d08895a
75 changed files with 99196 additions and 8 deletions

View File

@ -276,6 +276,7 @@ option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF)
set(STANDARD_PACKAGES set(STANDARD_PACKAGES
ADIOS ADIOS
AMOEBA AMOEBA
APIP
ASPHERE ASPHERE
ATC ATC
AWPMD AWPMD
@ -605,7 +606,7 @@ else()
endif() endif()
foreach(PKG_WITH_INCL KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF foreach(PKG_WITH_INCL KSPACE PYTHON ML-IAP VORONOI COLVARS ML-HDNNP MDI MOLFILE NETCDF
PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS ML-PACE LEPTON EXTRA-COMMAND) PLUMED QMMM ML-QUIP SCAFACOS MACHDYN VTK KIM COMPRESS APIP ML-PACE LEPTON EXTRA-COMMAND)
if(PKG_${PKG_WITH_INCL}) if(PKG_${PKG_WITH_INCL})
include(Packages/${PKG_WITH_INCL}) include(Packages/${PKG_WITH_INCL})
endif() endif()

View File

@ -0,0 +1,3 @@
if(NOT PKG_ML-PACE)
message(FATAL_ERROR "Must enable ML-PACE package for enabling APIP package")
endif()

View File

@ -4,6 +4,7 @@
set(ALL_PACKAGES set(ALL_PACKAGES
ADIOS ADIOS
AMOEBA AMOEBA
APIP
ASPHERE ASPHERE
ATC ATC
AWPMD AWPMD

View File

@ -6,6 +6,7 @@
set(ALL_PACKAGES set(ALL_PACKAGES
ADIOS ADIOS
AMOEBA AMOEBA
APIP
ASPHERE ASPHERE
ATC ATC
AWPMD AWPMD

View File

@ -3,6 +3,7 @@
set(PACKAGES_WITH_LIB set(PACKAGES_WITH_LIB
ADIOS ADIOS
APIP
ATC ATC
AWPMD AWPMD
COMPRESS COMPRESS

View File

@ -22,6 +22,7 @@ OPT.
* :doc:`append/atoms <fix_append_atoms>` * :doc:`append/atoms <fix_append_atoms>`
* :doc:`atc <fix_atc>` * :doc:`atc <fix_atc>`
* :doc:`atom/swap <fix_atom_swap>` * :doc:`atom/swap <fix_atom_swap>`
* :doc:`apip_atom_weight <fix_apip_atom_weight>`
* :doc:`ave/atom <fix_ave_atom>` * :doc:`ave/atom <fix_ave_atom>`
* :doc:`ave/chunk <fix_ave_chunk>` * :doc:`ave/chunk <fix_ave_chunk>`
* :doc:`ave/correlate <fix_ave_correlate>` * :doc:`ave/correlate <fix_ave_correlate>`
@ -89,6 +90,7 @@ OPT.
* :doc:`imd <fix_imd>` * :doc:`imd <fix_imd>`
* :doc:`indent <fix_indent>` * :doc:`indent <fix_indent>`
* :doc:`ipi <fix_ipi>` * :doc:`ipi <fix_ipi>`
* :doc:`lambda <fix_lambda>`
* :doc:`langevin (k) <fix_langevin>` * :doc:`langevin (k) <fix_langevin>`
* :doc:`langevin/drude <fix_langevin_drude>` * :doc:`langevin/drude <fix_langevin_drude>`
* :doc:`langevin/eff <fix_langevin_eff>` * :doc:`langevin/eff <fix_langevin_eff>`
@ -97,6 +99,7 @@ OPT.
* :doc:`lb/momentum <fix_lb_momentum>` * :doc:`lb/momentum <fix_lb_momentum>`
* :doc:`lb/viscous <fix_lb_viscous>` * :doc:`lb/viscous <fix_lb_viscous>`
* :doc:`lineforce <fix_lineforce>` * :doc:`lineforce <fix_lineforce>`
* :doc:`lambda_thermostat <fix_lambda_thermostat>`
* :doc:`manifoldforce <fix_manifoldforce>` * :doc:`manifoldforce <fix_manifoldforce>`
* :doc:`mdi/qm <fix_mdi_qm>` * :doc:`mdi/qm <fix_mdi_qm>`
* :doc:`mdi/qmmm <fix_mdi_qmmm>` * :doc:`mdi/qmmm <fix_mdi_qmmm>`

View File

@ -96,7 +96,9 @@ OPT.
* :doc:`eam/cd <pair_eam>` * :doc:`eam/cd <pair_eam>`
* :doc:`eam/cd/old <pair_eam>` * :doc:`eam/cd/old <pair_eam>`
* :doc:`eam/fs (gikot) <pair_eam>` * :doc:`eam/fs (gikot) <pair_eam>`
* :doc:`eam/fs/apip <pair_eam_apip>`
* :doc:`eam/he <pair_eam>` * :doc:`eam/he <pair_eam>`
* :doc:`eam/apip <pair_eam_apip>`
* :doc:`edip (o) <pair_edip>` * :doc:`edip (o) <pair_edip>`
* :doc:`edip/multi <pair_edip>` * :doc:`edip/multi <pair_edip>`
* :doc:`edpd (g) <pair_mesodpd>` * :doc:`edpd (g) <pair_mesodpd>`
@ -124,6 +126,9 @@ OPT.
* :doc:`ilp/tmd (t) <pair_ilp_tmd>` * :doc:`ilp/tmd (t) <pair_ilp_tmd>`
* :doc:`kolmogorov/crespi/full <pair_kolmogorov_crespi_full>` * :doc:`kolmogorov/crespi/full <pair_kolmogorov_crespi_full>`
* :doc:`kolmogorov/crespi/z <pair_kolmogorov_crespi_z>` * :doc:`kolmogorov/crespi/z <pair_kolmogorov_crespi_z>`
* :doc:`lambda/zone <pair_lambda_zone>`
* :doc:`lambda_input <pair_lambda_input>`
* :doc:`lambda_input/csp <pair_lambda_input>`
* :doc:`lcbop <pair_lcbop>` * :doc:`lcbop <pair_lcbop>`
* :doc:`lebedeva/z <pair_lebedeva_z>` * :doc:`lebedeva/z <pair_lebedeva_z>`
* :doc:`lennard/mdf <pair_mdf>` * :doc:`lennard/mdf <pair_mdf>`
@ -236,6 +241,9 @@ OPT.
* :doc:`oxrna2/coaxstk <pair_oxrna2>` * :doc:`oxrna2/coaxstk <pair_oxrna2>`
* :doc:`pace (k) <pair_pace>` * :doc:`pace (k) <pair_pace>`
* :doc:`pace/extrapolation (k) <pair_pace>` * :doc:`pace/extrapolation (k) <pair_pace>`
* :doc:`pace/apip <pair_pace_apip>`
* :doc:`pace/apip/fast <pair_pace_apip>`
* :doc:`pace/apip/precise <pair_pace_apip>`
* :doc:`pedone (o) <pair_pedone>` * :doc:`pedone (o) <pair_pedone>`
* :doc:`pod (k) <pair_pod>` * :doc:`pod (k) <pair_pod>`
* :doc:`peri/eps <pair_peri>` * :doc:`peri/eps <pair_peri>`

View File

@ -91,6 +91,7 @@ Packages howto
Howto_manifold Howto_manifold
Howto_rheo Howto_rheo
Howto_spins Howto_spins
Howto_apip
Tutorials howto Tutorials howto
=============== ===============

130
doc/src/Howto_apip.rst Normal file
View File

@ -0,0 +1,130 @@
Adaptive-precision interatomic potentials (APIP)
================================================
The :ref:`APIP package <PKG-APIP>` allows to use adaptive-precision potentials
according to :ref:`(Immel) <Immel2025_1>`.
The potential energy :math:`E_i` of an atom :math:`i` of an adaptive-precision
interatomic potential is given by :ref:`(Immel) <Immel2025_1>`
.. math::
E_i = \lambda_i E_i^\text{(fast)} + (1-\lambda_i) E_i^\text{(precise)},
whereas :math:`E_i^\text{(fast)}` is the potential energy of atom :math:`i`
according to a fast interatomic potential,
:math:`E_i^\text{(precise)}` is the potential energy according to a precise
interatomic potential and :math:`\lambda_i\in[0,1]` is the
switching parameter that decides which potential energy is used.
The currently implemented potentials are:
.. list-table::
:header-rows: 1
* - Fast potential
- Precise potential
* - :doc:`ACE <pair_pace_apip>`
- :doc:`ACE <pair_pace_apip>`
* - :doc:`EAM <pair_eam_apip>`
-
In theory, any short-range potential can be used for an adaptive-precision
interatomic potential. How to implement a new (fast or precise)
adaptive-precision
potential is explained in :ref:`here <implementing_new_apip_styles>`.
To run a simulation with an adaptive-precision potential, one needs the
following components:
#. :doc:`atom_style apip <atom_style>` so that the switching parameter :math:`\lambda_i` can be stored.
#. A fast potential: :doc:`eam/apip <pair_eam_apip>` or :doc:`pace/apip/fast <pair_pace_apip>`.
#. A precise potential: :doc:`pace/apip/precise <pair_pace_apip>`.
#. :doc:`pair_style lambda_input <pair_lambda_input>` to calculate :math:`\lambda_i^\text{input}`, from which :math:`\lambda_i` is calculated.
#. :doc:`fix lambda <fix_lambda>` to calculate the switching parameter.
#. :doc:`pair_style lambda/zone <pair_lambda_zone>` to calculate the spatial transition zone of the switching parameter.
#. :doc:`pair_style hybrid/overlay <pair_hybrid>` to combine the previously mentioned pair_styles.
#. :doc:`fix lambda_thermostat <fix_lambda_thermostat>` to conserve the energy when switching parameters change.
#. :doc:`fix apip_atom_weight <fix_apip_atom_weight>` to approximate the load caused by every atom, as the computations of the pair_styles are only required for a subset of atoms.
#. :doc:`fix balance <fix_balance>` to perform dynamic load balancing with the calculated load.
Example
"""""""
.. note::
How to select the values of the parameters of an adaptive-precision
interatomic potential is discussed in detail in :ref:`(Immel) <Immel2025_1>`.
The affected parts of a LAMMPS script can look as follows:
.. code-block:: LAMMPS
atom_style apip
comm_style tiled
pair_style hybrid/overlay eam/fs/apip pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda/zone 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/apip Cu.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda/zone
fix 2 all lambda 2.5 3.0 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
fix 3 all lambda_thermostat N_rescaling 200
fix 4 all apip_atom_weight 100 eam ace lambda_input lambda all
variable myweight atom f_4
fix 5 all balance 100 1.1 rcb weight var myweight
First, the :doc:`atom_style <atom_style>` and the communication style are set.
.. note::
Note, that :doc:`comm_style <comm_style>` *tiled* is required for the style *rcb* of
:doc:`fix balance <fix_balance>`, but not for APIP.
However, the flexibility offered by the balancing style *rcb*, compared to the
balancing style *shift*, is advantageous for APIP.
An adaptive-precision EAM-ACE potential, for which the switching parameter
:math:`\lambda` is calculated from the CSP is defined via
:doc:`pair_style hybrid/overlay <pair_hybrid>`.
The fixes ensure that the switching parameter is calculated, the energy conserved,
the weight for the load balancing calculated and the load-balancing itself is done.
----------
.. _implementing_new_apip_styles:
Implementing new APIP pair styles
"""""""""""""""""""""""""""""""""
One can introduce adaptive-precision to an existing pair style by modifying
the original pair style.
One should calculate the force
:math:`F_i = - \nabla_i \sum_j E_j^\text{original}` for a fast potential or
:math:`F_i = - (1-\nabla_i) \sum_j E_j^\text{original}` for a precise
potential from the original potential
energy :math:`E_j^\text{original}` to see where the switching parameter
:math:`\lambda_i` needs to be introduced in the force calculation.
The switching parameter :math:`\lambda_i` is known for all atoms :math:`i`
in force calculation routine.
One needs to introduce an abortion criterion based on :math:`\lambda_i` to
ensure that all not required calculations are skipped and compute time can
be saved.
Furthermore, one needs to provide the number of calculations and measure the
computation time.
Communication within the force calculation needs to be prevented to allow
effective load-balancing.
With communication, the load balancer cannot balance few calculations of the
precise potential on one processor with many computations of the fast
potential on another processor.
All changes in the pair_style pace/apip compared to the pair_style pace
are annotated and commented.
Thus, the pair_style pace/apip can serve as an example for the implementation
of new adaptive-precision potentials.
----------
.. _Immel2025_1:
**(Immel)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)

View File

@ -28,6 +28,7 @@ gives those details.
* :ref:`ADIOS <PKG-ADIOS>` * :ref:`ADIOS <PKG-ADIOS>`
* :ref:`AMOEBA <PKG-AMOEBA>` * :ref:`AMOEBA <PKG-AMOEBA>`
* :ref:`APIP <PKG-APIP>`
* :ref:`ASPHERE <PKG-ASPHERE>` * :ref:`ASPHERE <PKG-ASPHERE>`
* :ref:`ATC <PKG-ATC>` * :ref:`ATC <PKG-ATC>`
* :ref:`AWPMD <PKG-AWPMD>` * :ref:`AWPMD <PKG-AWPMD>`
@ -186,6 +187,58 @@ provided by the Ponder group in their
---------- ----------
.. _PKG-APIP:
APIP package
------------
**Contents:**
This package provides adaptive-precision interatomic potentials (APIP) as
described in:
D. Immel, R. Drautz and G. Sutmann, "Adaptive-precision potentials for
large-scale atomistic simulations", J. Chem. Phys. 162, 114119 (2025)
`link <immel2025_doi_>`_
Adaptive-precision means, that a fast interatomic potential, such as EAM,
is coupled to a
precise interatomic potential, such as ACE.
This package provides the required pair_styles and fixes to run an efficient,
energy-conserving adaptive-precision simulation.
.. _immel2025_doi: https://doi.org/10.1063/5.0245877
**Authors:**
This package was written by David Immel^1,
Ralf Drautz^2 and Godehard Sutmann^1^2.
^1: Forschungszentrum Juelich, Juelich, Germany
^2: Ruhr-University Bochum, Bochum, Germany
**Install:**
The APIP package requires also the installation of ML-PACE, which has
:ref:`specific installation instructions <ml-pace>` on the
:doc:`Build extras <Build_extras>` page.
**Supporting info:**
* ``src/APIP``: filenames -> commands
* :doc:`Howto APIP <Howto_apip>`
* ``examples/PACKAGES/apip``
* :doc:`fix apip_atom_weight <fix_apip_atom_weight>`
* :doc:`fix lambda <fix_lambda>`
* :doc:`fix lambda_thermostat <fix_lambda_thermostat>`
* :doc:`pair_style eam/apip <pair_eam_apip>`
* :doc:`pair_style lambda/zone <pair_lambda_zone>`
* :doc:`pair_style lambda_input <pair_lambda_input>`
* :doc:`pair_style pace/apip <pair_pace_apip>`
----------
.. _PKG-ASPHERE: .. _PKG-ASPHERE:
ASPHERE package ASPHERE package

View File

@ -38,6 +38,11 @@ whether an extra library is needed to build and use the package:
- :doc:`AMOEBA and HIPPO howto <Howto_amoeba>` - :doc:`AMOEBA and HIPPO howto <Howto_amoeba>`
- amoeba - amoeba
- no - no
* - :ref:`APIP <PKG-APIP>`
- adaptive-precision interatomic potentials
- :doc:`Howto APIP <Howto_apip>`
- ``PACKAGES/APIP``
- ext
* - :ref:`ASPHERE <PKG-ASPHERE>` * - :ref:`ASPHERE <PKG-ASPHERE>`
- aspherical particle models - aspherical particle models
- :doc:`Howto spherical <Howto_spherical>` - :doc:`Howto spherical <Howto_spherical>`

View File

@ -10,7 +10,7 @@ Syntax
atom_style style args atom_style style args
* style = *amoeba* or *angle* or *atomic* or *body* or *bond* or *charge* or *dielectric* or *dipole* or *dpd* or *edpd* or *electron* or *ellipsoid* or *full* or *line* or *mdpd* or *molecular* or *oxdna* or *peri* or *smd* or *sph* or *sphere* or *bpm/sphere* or *spin* or *tdpd* or *tri* or *template* or *wavepacket* or *hybrid* * style = *amoeba* or *angle* or *apip* or *atomic* or *body* or *bond* or *charge* or *dielectric* or *dipole* or *dpd* or *edpd* or *electron* or *ellipsoid* or *full* or *line* or *mdpd* or *molecular* or *oxdna* or *peri* or *smd* or *sph* or *sphere* or *bpm/sphere* or *spin* or *tdpd* or *tri* or *template* or *wavepacket* or *hybrid*
.. parsed-literal:: .. parsed-literal::
@ -117,6 +117,10 @@ the Additional Information section below.
- *bond* + "angle data" - *bond* + "angle data"
- :ref:`MOLECULE <PKG-MOLECULE>` - :ref:`MOLECULE <PKG-MOLECULE>`
- bead-spring polymers with stiffness - bead-spring polymers with stiffness
* - *apip*
- *atomic* + lambda, lambda_required, lambda_input, lambda_const, lambda_input_ta, e_simple, e_complex, f_const_lambda, f_dyn_lambda
- :ref:`APIP <PKG-APIP>`
- adaptive-precision interatomic potentials(APIP), see :doc:`APIP howto <Howto_apip>`
* - *atomic* * - *atomic*
- tag, type, x, v, f, image, mask - tag, type, x, v, f, image, mask
- -

View File

@ -201,6 +201,7 @@ accelerated styles exist.
* :doc:`append/atoms <fix_append_atoms>` - append atoms to a running simulation * :doc:`append/atoms <fix_append_atoms>` - append atoms to a running simulation
* :doc:`atc <fix_atc>` - initiates a coupled MD/FE simulation * :doc:`atc <fix_atc>` - initiates a coupled MD/FE simulation
* :doc:`atom/swap <fix_atom_swap>` - Monte Carlo atom type swapping * :doc:`atom/swap <fix_atom_swap>` - Monte Carlo atom type swapping
* :doc:`apip_atom_weight <fix_apip_atom_weight>` - compute atomic load of an :doc:`APIP potential <Howto_apip>` for load balancing
* :doc:`ave/atom <fix_ave_atom>` - compute per-atom time-averaged quantities * :doc:`ave/atom <fix_ave_atom>` - compute per-atom time-averaged quantities
* :doc:`ave/chunk <fix_ave_chunk>` - compute per-chunk time-averaged quantities * :doc:`ave/chunk <fix_ave_chunk>` - compute per-chunk time-averaged quantities
* :doc:`ave/correlate <fix_ave_correlate>` - compute/output time correlations * :doc:`ave/correlate <fix_ave_correlate>` - compute/output time correlations
@ -268,6 +269,7 @@ accelerated styles exist.
* :doc:`imd <fix_imd>` - implements the "Interactive MD" (IMD) protocol * :doc:`imd <fix_imd>` - implements the "Interactive MD" (IMD) protocol
* :doc:`indent <fix_indent>` - impose force due to an indenter * :doc:`indent <fix_indent>` - impose force due to an indenter
* :doc:`ipi <fix_ipi>` - enable LAMMPS to run as a client for i-PI path-integral simulations * :doc:`ipi <fix_ipi>` - enable LAMMPS to run as a client for i-PI path-integral simulations
* :doc:`lambda <fix_lambda>` - compute switching parameter, that controls the precision of an :doc:`APIP potential <Howto_apip>`
* :doc:`langevin <fix_langevin>` - Langevin temperature control * :doc:`langevin <fix_langevin>` - Langevin temperature control
* :doc:`langevin/drude <fix_langevin_drude>` - Langevin temperature control of Drude oscillators * :doc:`langevin/drude <fix_langevin_drude>` - Langevin temperature control of Drude oscillators
* :doc:`langevin/eff <fix_langevin_eff>` - Langevin temperature control for the electron force field model * :doc:`langevin/eff <fix_langevin_eff>` - Langevin temperature control for the electron force field model
@ -276,6 +278,7 @@ accelerated styles exist.
* :doc:`lb/momentum <fix_lb_momentum>` - :doc:`fix momentum <fix_momentum>` replacement for use with a lattice-Boltzmann fluid * :doc:`lb/momentum <fix_lb_momentum>` - :doc:`fix momentum <fix_momentum>` replacement for use with a lattice-Boltzmann fluid
* :doc:`lb/viscous <fix_lb_viscous>` - :doc:`fix viscous <fix_viscous>` replacement for use with a lattice-Boltzmann fluid * :doc:`lb/viscous <fix_lb_viscous>` - :doc:`fix viscous <fix_viscous>` replacement for use with a lattice-Boltzmann fluid
* :doc:`lineforce <fix_lineforce>` - constrain atoms to move in a line * :doc:`lineforce <fix_lineforce>` - constrain atoms to move in a line
* :doc:`lambda_thermostat <fix_lambda_thermostat>` - apply energy conserving correction for an :doc:`APIP potential <Howto_apip>`
* :doc:`manifoldforce <fix_manifoldforce>` - restrain atoms to a manifold during minimization * :doc:`manifoldforce <fix_manifoldforce>` - restrain atoms to a manifold during minimization
* :doc:`mdi/qm <fix_mdi_qm>` - LAMMPS operates as a client for a quantum code via the MolSSI Driver Interface (MDI) * :doc:`mdi/qm <fix_mdi_qm>` - LAMMPS operates as a client for a quantum code via the MolSSI Driver Interface (MDI)
* :doc:`mdi/qmmm <fix_mdi_qmmm>` - LAMMPS operates as client for QM/MM simulation with a quantum code via the MolSSI Driver Interface (MDI) * :doc:`mdi/qmmm <fix_mdi_qmmm>` - LAMMPS operates as client for QM/MM simulation with a quantum code via the MolSSI Driver Interface (MDI)

View File

@ -0,0 +1,143 @@
.. index:: fix apip_atom_weight
fix apip_atom_weight command
============================
Syntax
""""""
.. code-block:: LAMMPS
fix ID group-ID apip_atom_weight nevery fast_potential precise_potential lambda_input lambda group_lambda_input [no_rescale]
* ID, group-ID are documented in :doc:`fix <fix>` command
* apip_atom_weight = style name of this fix command
* nevery = perform load calculation every this many steps
* fast_potential = *eam* or *ace* for time measurements of the corresponding pair_style or float for constant time
* precise_potential = *ace* for a time measurement of the pair_style pace/apip or float for constant time
* lambda_input = *lambda_input* for a time measurement of pair_style lambda_input or float for constant time
* lambda = *lambda/zone* for a time measurement of pair_style lambda/zone or float for constant time
* group_lambda_input = group-ID of the group for which lambda_input is computed
* no_rescale = do not rescale the work per processor to the measured total force-computation time
Examples
""""""""
.. code-block:: LAMMPS
fix 2 all apip_atom_weight 50 eam ace lambda_input lambda/zone all
fix 2 all apip_atom_weight 50 1e-05 0.0004 4e-06 4e-06 all
fix 2 all apip_atom_weight 50 ace ace 4e-06 4e-06 all no_rescale
Description
"""""""""""
This command approximates the load every atom causes when an
adaptive-precision interatomic potential (APIP) according to
:ref:`(Immel) <Immel2025_2>` is used.
This approximated load can be saved as atomic variable and
used as input for the dynamic load balancing via the
:doc:`fix balance <fix_balance>` command.
An adaptive-precision potential like :doc:`eam/apip <pair_eam_apip>`
and :doc:`pace/apip <pair_pace_apip>` is calculated only
for a subset of atoms.
The switching parameter that determines per atom, which potential energy is
used, can be also calculated by
:doc:`pair_style lambda_input <pair_lambda_input>`.
A spatial switching zone, that ensures a smooth transition between two
different interatomic potentials, can be calculated by
:doc:`pair_style lambda/zone <pair_lambda_zone>`.
Thus, there are up to four force-subroutines, that are computed only for a
subset of atoms and combined via the pair_style :doc:`hybrid/overlay <pair_hybrid>`.
For all four force-subroutines, the average work per atom is be measured
per processor by the corresponding pair_style.
This fix extracts these measurements of the pair styles every *nevery*
steps. The average compute times are used to calculates a per-atom vector with
the approximated atomic weight, whereas the average compute time of the four
subroutines contributes only to the load of atoms, for which the corresponding
subroutine was calculated.
If not disabled via *no_rescale*, the so calculated load is
rescaled per processor so that the total atomic compute time matches the
also measured total compute time of the whole pair_style.
This atomic weight is intended to be used
as input for :doc:`fix balance <fix_balance>`:
.. code-block:: LAMMPS
variable nevery equal 10
fix weight_atom all apip_atom_weight ${nevery} eam ace lambda_input lambda/zone all
variable myweight atom f_weight_atom
fix balance all balance ${nevery} 1.1 rcb weight var myweight
Furthermore, this fix provides the over the processors averaged compute time of the
four pair_styles, which are used to approximate the atomic weight, as vector.
----------
Restart, fix_modify, output, run start/stop, minimize info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
No information about this fix is written to
:doc:`binary restart files <restart>`. None of the
:doc:`fix_modify <fix_modify>` options are relevant to this fix.
This fix produces a per-atom vector that contains the atomic
weight of each atom.
The per-atom vector can only be accessed on timesteps that are multiples
of *nevery*.
Furthermore, this fix computes a global vector of length 4 with
statistical information about the four different (possibly)
measured compute times per force subroutine. The four
values in the vector are as follows:
#. average compute time for one atom using the fast pair_style
#. average compute time for one atom using the precise pair_style
#. average compute time of lambda_input for one atom
#. average compute time of lambda/zone for one atom
The compute times are computed as average of all processors that
measured at least one computation of the corresponding style.
The vector values calculated by this fix are "intensive" and
updated whenever the per-atom vector is computed, i.e., in
timesteps that are multiples of *nevery*.
The vector and the per-atom vector can be accessed by various
:doc:`output commands <Howto_output>`.
No parameter of this fix can be used with the *start/stop* keywords of
the :doc:`run <run>` command. This fix is not invoked during
:doc:`energy minimization <minimize>`.
----------
Restrictions
""""""""""""
This fix is part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`fix balance <fix_balance>`,
:doc:`fix lambda <fix_lambda>`,
:doc:`fix lambda_thermostat <fix_lambda_thermostat>`,
:doc:`pair_style lambda/zone <pair_lambda_zone>`,
:doc:`pair_style lambda_input <pair_lambda_input>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
Default
"""""""
*no_rescale* is not used by default.
----------
.. _Immel2025_2:
**(Immel)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)

262
doc/src/fix_lambda.rst Normal file
View File

@ -0,0 +1,262 @@
.. index:: fix lambda
fix lambda command
==============================
Syntax
""""""
.. code-block:: LAMMPS
fix ID group-ID lambda thr_lo thr_hi keyword args ...
* ID, group-ID are documented in :doc:`fix <fix>` command
* lambda = style name of this fix command
* thr_lo = value below which :math:`\lambda_i^\text{input}` results in a switching parameter of 1
* thr_hi = value above which :math:`\lambda_i^\text{input}` results in a switching parameter of 0
* zero or one keyword/args pairs may be appended
* keyword = *time_averaged_zone* or *min_delta_lambda* or *lambda_non_group* or *store_atomic_stats* or *dump_atomic_history* or *group_fast* or *group_precise* or *group_ignore_lambda_input*
.. parsed-literal::
*time_averaged_zone* args = cut_lo cut_hi history_len_lambda_input history_len_lambda
cut_lo = distance at which the radial function decreases from 1
cut_hi = distance from which on the radial function is 0
history_len_lambda_input = number of time steps for which lambda_input is averaged
history_len_lambda = number of time steps for which the switching parameter is averaged
*min_delta_lambda* args = delta
delta = value below which changes of the switching parameter are neglegted (>= 0)
*lambda_non_group* args = lambda_ng
lambda_ng = *precise* or *fast* or float
*precise* = assign a constant switching parameter of 0 to atoms, that are not in the group specified by group-ID
*fast* = assign a constant switching parameter of 1 to atoms, that are not in the group specified by group-ID
float = assign this constant switching parameter to atoms, that are not in the group specified by group-ID (0 <= float <= 1)
*group_fast* args = group-ID-fast
group-ID-fast = the switching parameter of 1 is used instead of the one computed by lambda_input for atoms in the group specified by group-ID-fast
*group_precise* args = group-ID-precise
group-ID-precise = the switching parameter of 0 is used instead of the one computed by lambda_input for atoms in the group specified by group-ID-precise
*group_ignore_lambda_input* args = group-ID-ignore-lambda-input
group-ID-ignore-lambda-input = the switching parameter of lambda_ng is used instead of the one computed by lambda_input for atoms in the group specified by group-ID-ignore-lambda-input
*store_atomic_stats* args = none
*dump_atomic_history* args = none
Examples
""""""""
.. code-block:: LAMMPS
fix 2 all lambda 3.0 3.5 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
fix 2 mobile lambda 3.0 3.5 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01 group_ignore_lambda_input immobile lambda_non_group fast
Description
"""""""""""
The potential energy :math:`E_i` of an atom :math:`i` of an adaptive-precision
potential according to :ref:`(Immel) <Immel2025_3>` is given by
.. math::
E_i = \lambda_i E_i^\text{(fast)} + (1-\lambda_i) E_i^\text{(precise)},
whereas :math:`E_i^\text{(fast)}` is the potential energy of atom :math:`i`
according to a fast interatomic potential like EAM,
:math:`E_i^\text{(precise)}` is the potential energy according to a precise
interatomic potential such as ACE and :math:`\lambda_i\in[0,1]` is the
switching parameter that decides which potential energy is used.
This fix calculates the switching parameter :math:`\lambda_i` based on the
input provided from :doc:`pair_style lambda_input <pair_lambda_input>`.
The calculation of the switching parameter is described in detail in
:ref:`(Immel) <Immel2025_3>`.
This fix calculates the switching parameter for all atoms in the
:doc:`group <group>`
described by group-ID, while the value of *lambda_non_group* is used
as switching parameter for all other atoms.
First, this fix calculates per atom :math:`i` the time averaged input
:math:`\lambda^\text{input}_{\text{avg},i}` from
:math:`\lambda^\text{input}_{i}`, whereas the number of averaged timesteps
can be set via *time_averaged_zone*.
.. note::
:math:`\lambda^\text{input}_{i}` is calculated by
:doc:`pair_style lambda_input <pair_lambda_input>`, which needs to be included
in the input script as well.
The time averaged input :math:`\lambda^\text{input}_{\text{avg},i}` is then
used to calculate the switching parameter
.. math::
\lambda_{0,i}(t) = f^\text{(cut)} \left(\frac{\lambda_{\text{avg},i}^\text{input}(t) - \lambda_\text{lo}^\text{input}}{\lambda_\text{hi}^\text{input} - \lambda_\text{lo}^\text{input}} \right)\,,
whereas the thresholds :math:`\lambda_\text{hi}^\text{input}`
and :math:`\lambda_\text{lo}^\text{input}` are set by the
values provided as *thr_lo* and *thr_hi* and :math:`f^\text{(cut)}(x)` is a cutoff function
that is 1 for :math:`x\leq 0`, decays from 1 to 0 for :math:`x\in[0,1]`, and
is 0 for :math:`x\geq 1`.
If the *group_precise* argument is used, :math:`\lambda_{0,i}=0` is used for all
atoms :math:`i` assigned to the corresponding :doc:`group <group>`.
If the *group_fast* argument is used, :math:`\lambda_{0,i}=1` is used for all
atoms :math:`i` assigned to the corresponding :doc:`group <group>`.
If an atom is in the groups *group_fast* and *group_precise*,
:math:`\lambda_{0,i}=0` is used.
If the *group_ignore_lambda_input* argument is used,
:math:`\lambda_i^\text{input}` is not computed for all atoms :math:`i` assigned
to the corresponding :doc:`group <group>`; instead, if the value is not already
set by *group_fast* or *group_precise*, the value of *lambda_non_group* is
used.
.. note::
The computation of :math:`\lambda_i^\text{input}` is not required for
atoms that are in the groups *group_fast* and *group_precise*.
Thus, one should use *group_ignore_lambda_input* and prevent the
computation of :math:`\lambda_i^\text{input}` for all atoms, for
which a constant input is used.
A spatial transition zone between the fast and the precise potential is
introduced via
.. math::
\lambda_{\text{min},i}(t) = \text{min}\left(\left\{1 - (1 -\lambda_{0,j}(t)) f^\text{(cut)}\left(\frac{r_{ij}(t)-r_{\lambda,\text{lo}}}{r_{\lambda,\text{hi}} - r_{\lambda,\text{lo}}}\right) : j \in \Omega_{\lambda,i} \right\}\right)\,,
whereas the thresholds :math:`r_{\lambda,\text{lo}}` and
:math:`r_{\lambda,\text{hi}}`
of the cutoff function are set via *time_averaged_zone* and
:math:`\Omega_{\lambda,i}` is the set of
neighboring atoms of atom :math:`i`.
.. note::
:math:`\lambda_{\text{min},i}` is calculated by
:doc:`pair_style lambda/zone <pair_lambda_zone>`, which needs to be included
in the input script as well.
The switching parameter is smoothed by the calculation of the time average
.. math::
\lambda_{\text{avg},i}(t) = \frac{1}{N_{\lambda,\text{avg}}} \sum_{n=1}^{N_{\lambda,\text{avg}}} \lambda_{\text{min},i}(t - n \Delta t)\,,
whereas :math:`\Delta t` is the :doc:`timestep <timestep>` and
:math:`N_{\lambda,\text{avg}}` is the number of averaged timesteps, that
can be set via *time_averaged_zone*.
Finally, numerical fluctuations of the switching parameter are suppressed by the usage of
.. math::
\lambda_{i}(t) = \left\{
\begin{array}{ll}
\lambda_{\text{avg},i}(t) & \text{ for } \left|\lambda_{\text{avg},i}(t) - \lambda_{i}(t-\Delta t)\right|\geq \Delta\lambda_\text{min} \text{ or } \lambda_{\text{avg},i}(t)\in\{0,1\}, \\
\lambda_{i}(t-\Delta t) & \text{ otherwise}\,,
\end{array}
\right.
whereas the minimum change :math:`\Delta\lambda_\text{min}` is set by the
*min_delta_lambda* argument.
.. note::
*group_fast* affects only :math:`\lambda_{0,i}(t)`. The switching parameter
of atoms in this :doc:`group <group>` may change due to the calculation of the
spatial switching zone.
A switching parameter of 1 can be enforced by excluding the corresponding
atoms from the :doc:`group <group>` described by group-ID and using *lambda_non_group* 1
as argument.
----------
A code example for the calculation of the switching parameter for an
adaptive-precision potential is given in the following:
The adaptive-precision potential is created
by combining :doc:`pair_style eam/fs/apip <pair_eam_apip>`
and :doc:`pair_style pace/apip/precise <pair_pace_apip>`.
The input, from which the switching parameter is calculated, is provided
by :doc:`pair lambda_input/csp <pair_lambda_input>`.
The switching parameter is calculated by this fix, whereas the spatial
transition zone of the switching parameter is calculated by
:doc:`pair_style lambda/zone <pair_lambda_zone>`.
.. code-block:: LAMMPS
pair_style hybrid/overlay eam/fs/apip pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/apip Cu_precise.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda
fix 2 all lambda 3.0 3.5 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
----------
Restart, fix_modify, output, run start/stop, minimize info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
The saved history of the switching parameter :math:`\lambda_i`
and the saved history of
:math:`\lambda_i^\text{input}` are written to
:doc:`binary restart files <restart>` allow a smooth restart of a simulation.
None of the :doc:`fix_modify <fix_modify>` options are relevant to this fix.
If the *store_atomic_stats* argument is used, basic statistics is provided as
per-atom array:
#. :math:`\lambda_i^\text{input}(t)`
#. :math:`\lambda_{\text{avg},i}^\text{input}(t)`
#. :math:`\lambda_{0,i}(t)`
#. :math:`\lambda_{\text{min},i}(t)`
#. :math:`\lambda_{i}(t)`
If the *dump_atomic_history* argument is used, the whole saved history
of :math:`\lambda_i^\text{input}(t)` is appended to the previously
mentioned array per atom.
The per-atom vector can be accessed by various
:doc:`output commands <Howto_output>`.
No parameter of this fix can be used with the *start/stop* keywords of
the :doc:`run <run>` command. This fix is not invoked during
:doc:`energy minimization <minimize>`.
----------
Restrictions
""""""""""""
This fix is part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`pair_style lambda/zone <pair_lambda_zone>`,
:doc:`pair_style lambda_input <pair_lambda_input>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`
:doc:`fix lambda_thermostat <fix_lambda_thermostat>`,
Default
"""""""
*min_delta_lambda* = 0,
*lambda_non_group* = 1,
*cut_lo* = 4.0,
*cut_hi* = 12.0,
*history_len_lambda_input* = 100,
*history_len_lambda* = 100,
*store_atomic_stats* is not used,
*dump_atomic_history* is not used,
*group_fast* is not used,
*group_precise* is not used,
*group_ignore_lambda_input* is not used
----------
.. _Immel2025_3:
**(Immel)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)

View File

@ -0,0 +1,176 @@
.. index:: fix lambda_thermostat
fix lambda_thermostat command
=============================
Syntax
""""""
.. code-block:: LAMMPS
fix ID group-ID lambda_thermostat keyword values ...
* ID, group-ID are documented in :doc:`fix <fix>` command
* lambda_thermostat = style name of this fix command
* zero or more keyword/value pairs may be appended
* keyword = *seed* or *store_atomic_forces* or *N_rescaling*
.. parsed-literal::
*seed* value = integer
integer = integer that is used as seed for the random number generator (> 0)
*store_atomic_forces* value = nevery
nevery = provide per-atom output every this many steps
*N_rescaling* value = groupsize
groupsize = rescale this many neighboring atoms (> 1)
Examples
""""""""
.. code-block:: LAMMPS
fix 2 all lambda_thermostat
fix 2 all lambda_thermostat N_rescaling 100
fix 2 all lambda_thermostat seed 42
fix 2 all lambda_thermostat seed 42 store_atomic_forces 1000
Description
"""""""""""
This command applies the local thermostat described in
:ref:`(Immel) <Immel2025_4>`
to conserve the energy when the switching parameters of an
:doc:`adaptive-precision interatomic potential <Howto_apip>` (APIP)
are updated while the gradient
of the switching parameter is neglected in the force calculation.
.. warning::
The temperature change caused by this fix is only the means to the end of
conserving the energy. Thus, this fix is not a classical thermostat, that
ensures a given temperature in the system.
All available thermostats are listed :doc:`here <Howto_thermostat>`.
The potential energy :math:`E_i` of an atom :math:`i` is given by the formula from
:ref:`(Immel) <Immel2025_4>`
.. math::
E_i = \lambda_i E_i^\text{(fast)} + (1-\lambda_i) E_i^\text{(precise)},
whereas :math:`E_i^\text{(fast)}` is the potential energy of atom :math:`i`
according to a fast interatomic potential like EAM,
:math:`E_i^\text{(precise)}` is the potential energy according to a precise
interatomic potential such as ACE and :math:`\lambda_i\in[0,1]` is the
switching parameter that decides which potential energy is used.
This potential energy and the corresponding forces are conservative when
the switching parameter :math:`\lambda_i` is constant in time for all atoms
:math:`i`.
For a conservative force calculation and dynamic switching parameters,
the atomic force on an atom is given by
:math:`F_i = -\nabla_i \sum_j E_j` and includes the derivative of the switching
parameter :math:`\lambda_i`.
The force contribution of this gradient of the switching function can cause
large forces which are not similar to the forces of the fast or the precise
interatomic potential as discussed in :ref:`(Immel) <Immel2025_4>`.
Thus, one can neglect the gradient of the switching parameter in the force
calculation and compensate for the violation of energy conservation by
the application of the local thermostat implemented in this fix.
One can compute the violation of the energy conservation :math:`\Delta H_i`
for all atoms :math:`i` as discussed in :ref:`(Immel) <Immel2025_4>`.
To locally correct this energy violation :math:`\Delta H_i`, one
can rescale the velocity of atom :math:`i` and of neighboring atoms.
The rescaling is done relative to the center-of-mass velocity of the
group and, thus, conserves the momentum.
.. note::
This local thermostat provides the NVE ensemble rather than the NVT
ensemble as
the energy :math:`\Delta H_i` determines the rescaling factor rather than
a temperature.
Velocities :math:`v` are updated by the integrator according to
:math:`\Delta v_i = (F_i/m_i)\Delta t`, whereas `m` denotes the mass of atom
:math:`i` and :math:`\Delta t` is the time step.
One can interpret the velocity difference :math:`\Delta v` caused by the
rescaling as the application of an additional force which is given by
:math:`F^\text{lt}_i = (v^\text{unscaled}_i - v^\text{rescaled}_i) m_i
/ \Delta t` :ref:`(Immel) <Immel2025_4>`.
This additional force is computed when the *store_atomic_forces* option
is used.
The local thermostat is not appropriate for simulations at a temperature of 0K.
.. note::
The maximum decrease of the kinetic energy is achieved with a rescaling
factor of 0, i.e., the relative velocity of the group of rescaled atoms
is set to zero. One cannot decrease the energy further. Thus, the
local thermostat can fail, which is, however, reported by the returned
vector.
----------
Restart, fix_modify, output, run start/stop, minimize info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
No information about this fix is written to
:doc:`binary restart files <restart>`. None of the
:doc:`fix_modify <fix_modify>` options are relevant to this fix.
If the *store_atomic_forces* option is used, this fix produces every
*nevery* time steps a per-atom array that contains the theoretical force
applied by the local thermostat in all three spatial dimensions in the first
three components. :math:`\Delta H_i` is the fourth component of the per-atom
array.
The per-atom array can only be accessed on timesteps that are multiples
of *nevery*.
Furthermore, this fix computes a global vector of length 6 with
information about the rescaling:
#. number of atoms whose energy changed due to the last :math:`\lambda` update
#. contribution of the potential energy to the last computed :math:`\Delta H`
#. contribution of the kinetic energy to the last computed :math:`\Delta H`
#. sum over all atoms of the absolute energy change caused by the last rescaling step
#. energy change that could not be compensated accumulated over all timesteps
#. number of atoms whose energy change could not be compensated accumulated over all timesteps
The vector and the per-atom vector can be accessed by various
:doc:`output commands <Howto_output>`.
No parameter of this fix can be used with the *start/stop* keywords of
the :doc:`run <run>` command. This fix is not invoked during
:doc:`energy minimization <minimize>`.
----------
Restrictions
""""""""""""
This fix is part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`fix lambda <fix_lambda>`,
:doc:`pair_style lambda/zone <pair_lambda_zone>`,
:doc:`pair_style lambda_input <pair_lambda_input>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`
Default
"""""""
seed = 42, N_rescaling = 200, *store_atomic_forces* is not used
----------
.. _Immel2025_4:
**(Immel)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)

126
doc/src/pair_eam_apip.rst Normal file
View File

@ -0,0 +1,126 @@
.. index:: pair_style eam/apip
.. index:: pair_style eam/fs/apip
pair_style eam/apip command
=============================
Constant precision variant: *eam*
pair_style eam/fs/apip command
================================
Constant precision variant: *eam/fs*
Syntax
""""""
.. code-block:: LAMMPS
pair_style eam/apip
pair_style eam/fs/apip
Examples
""""""""
.. code-block:: LAMMPS
pair_style hybrid/overlay eam/fs/apip pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/apip Cu_precise.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda
Description
"""""""""""
Style *eam* computes pairwise interactions for metals and metal alloys
using embedded-atom method (EAM) potentials :ref:`(Daw) <Daw2>`. The total
energy :math:`E_i` of an atom :math:`i` is given by
.. math::
E_i^\text{EAM} = F_\alpha \left(\sum_{j \neq i}\ \rho_\beta (r_{ij})\right) +
\frac{1}{2} \sum_{j \neq i} \phi_{\alpha\beta} (r_{ij})
where :math:`F` is the embedding energy which is a function of the atomic
electron density :math:`\rho`, :math:`\phi` is a pair potential interaction,
and :math:`\alpha` and :math:`\beta` are the element types of atoms
:math:`i` and :math:`j`. The multi-body nature of the EAM potential is a
result of the embedding energy term. Both summations in the formula are over
all neighbors :math:`j` of atom :math:`i` within the cutoff distance.
EAM is documented in detail in :doc:`pair_style eam <pair_eam>`.
The potential energy :math:`E_i` of an atom :math:`i` of an adaptive-precision
interatomic potential (APIP) according to :ref:`(Immel) <Immel2025_5>` is given by
.. math::
E_i^\text{APIP} = \lambda_i E_i^\text{(fast)} + (1-\lambda_i) E_i^\text{(precise)}\,,
whereas the switching parameter :math:`\lambda_i` is computed by
:doc:`fix lambda <fix_lambda>`.
The pair style *eam/fs/apip* computes the potential energy
:math:`\lambda_i E_i^\text{EAM}` and the
corresponding force and should be combined
with a precise potential like
:doc:`pair_style pace/apip/precise <pair_pace_apip>` that computes the
potential energy :math:`(1-\lambda_i) E_i^\text{(precise)}` and the
corresponding force via :doc:`pair_style hybrid/overlay <pair_hybrid>`.
Mixing, shift, table, tail correction, restart, rRESPA info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
For atom type pairs I,J and I != J, where types I and J correspond to
two different element types, mixing is performed by LAMMPS as
described above with the individual styles. You never need to specify
a pair_coeff command with I != J arguments for the eam/apip styles.
This pair style does not support the :doc:`pair_modify <pair_modify>`
shift, table, and tail options.
The eam/apip pair styles do not write their information to :doc:`binary
restart files <restart>`, since it is stored in tabulated potential
files. Thus, you need to re-specify the pair_style and pair_coeff
commands in an input script that reads a restart file.
The eam/apip pair styles can only be used via the *pair* keyword of the
:doc:`run_style respa <run_style>` command. They do not support the
*inner*, *middle*, *outer* keywords.
----------
Restrictions
""""""""""""
This pair styles are part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`pair_style eam <pair_eam>`,
:doc:`pair_style hybrid/overlay <pair_hybrid>`,
:doc:`fix lambda <fix_lambda>`,
:doc:`fix lambda_thermostat <fix_lambda_thermostat>`,
:doc:`pair_style lambda/zone <pair_lambda_zone>`,
:doc:`pair_style lambda_input <pair_lambda_input>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`
Default
"""""""
none
----------
.. _Immel2025_5:
**(Immel)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)
.. _Daw2:
**(Daw)** Daw, Baskes, Phys Rev Lett, 50, 1285 (1983).
Daw, Baskes, Phys Rev B, 29, 6443 (1984).

View File

@ -0,0 +1,151 @@
.. index:: pair_style lambda_input
.. index:: pair_style lambda_input/csp
pair_style lambda_input command
===============================
Syntax
""""""
.. code-block:: LAMMPS
pair_style lambda_input cutoff
* lambda_input = style name of this pair style
* cutoff = global cutoff (distance units)
pair_style lambda_input/csp command
===================================
Syntax
""""""
.. code-block:: LAMMPS
pair_style lambda_input/csp lattice keyword args
* lambda_input/csp = style name of this pair style
* lattice = *fcc* or *bcc* or integer
.. parsed-literal::
*fcc* = use 12 nearest neighbors to calculate the CSP like in a perfect fcc lattice
*bcc* = use 8 nearest neighbors to calculate the CSP like in a perfect bcc lattice
integer = use N nearest neighbors to calculate the CSP
* zero or more keyword/args pairs may be appended
* keyword = *cutoff* or *N_buffer*
.. parsed-literal::
*cutoff* args = cutoff
cutoff = distance in which neighbouring atoms are considered (> 0)
*N_buffer* args = N_buffer
N_buffer = numer of additional neighbours, which are included in the j-j+N/2 calculation
Examples
""""""""
.. code-block:: LAMMPS
pair_style lambda_input/csp fcc
pair_style lambda_input/csp fcc cutoff 5.0
pair_style lambda_input/csp bcc cutoff 5.0 N_buffer 2
pair_style lambda_input/csp 14
Description
"""""""""""
This pair_styles calculates :math:`\lambda_i^\text{input}(t)`, which
is required for :doc:`fix lambda <fix_lambda>`.
The pair_style lambda_input sets :math:`\lambda_i^\text{input}(t) = 0`.
The pair_style lambda_input/csp calculates
:math:`\lambda_i^\text{input}(t) = \text{CSP}_i(t)`.
The centro-symmetry parameter (CSP) :ref:`(Kelchner) <Kelchner_2>` is described
in :doc:`compute centro/atom <compute_centro_atom>`.
The lattice argument is described in
:doc:`compute centro/atom <compute_centro_atom>` and determines
the number of neighboring atoms that are used to compute the CSP.
The *N_buffer* argument allows to include more neighboring atoms in
the calculation of the contributions from the pair j,j+N/2 to the CSP as
discussed in :ref:`(Immel) <Immel2025_6>`.
The computation of :math:`\lambda_i^\text{input}(t)` is done by this
pair_style instead of by :doc:`fix lambda <fix_lambda>`, as this computation
takes time and this pair_style can be included in the load-balancing via
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`.
A code example for the calculation of the switching parameter for an adaptive-
precision potential is given in the following:
The adaptive-precision potential is created
by combining :doc:`pair_style eam/fs/apip <pair_eam_apip>`
and :doc:`pair_style pace/apip/precise <pair_pace_apip>`.
The input, from which the switching parameter is calculated, is provided
by this pair_style.
The switching parameter is calculated by :doc:`fix lambda <fix_lambda>`,
whereas the spatial
transition zone of the switching parameter is calculated by
:doc:`pair_style lambda/zone <pair_lambda_zone>`.
.. code-block:: LAMMPS
pair_style hybrid/overlay eam/fs/apip pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/apip Cu_precise.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda
fix 2 all lambda 3.0 3.5 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
----------
Mixing, shift, table, tail correction, restart, rRESPA info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
The cutoff distance for this pair style can be mixed. The default mix
value is *geometric*\ . See the "pair_modify" command for details.
This pair style does not support the :doc:`pair_modify <pair_modify>`
shift, table, and tail options.
This pair style writes no information to :doc:`binary restart files <restart>`, so pair_style and pair_coeff commands need
to be specified in an input script that reads a restart file.
This pair style does not support the use of the *inner*, *middle*,
and *outer* keywords of the :doc:`run_style respa <run_style>` command.
----------
Restrictions
""""""""""""
This fix is part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`compute centro/atom <compute_centro_atom>`,
:doc:`fix lambda <fix_lambda>`,
:doc:`fix lambda_thermostat <fix_lambda_thermostat>`,
:doc:`pair_style lambda/zone <pair_lambda_zone>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`
Default
"""""""
N_buffer=0, cutoff=5.0
----------
.. _Kelchner_2:
**(Kelchner)** Kelchner, Plimpton, Hamilton, Phys Rev B, 58, 11085 (1998).
.. _Immel2025_6:
**(Immel)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)

View File

@ -0,0 +1,106 @@
.. index:: pair_style lambda/zone
pair_style lambda/zone command
==============================
Syntax
""""""
.. code-block:: LAMMPS
pair_style lambda/zone cutoff
* lambda/zone = style name of this pair style
* cutoff = global cutoff (distance units)
Examples
""""""""
.. code-block:: LAMMPS
pair_style lambda/zone 12.0
Description
"""""""""""
This pair_style calculates :math:`\lambda_{\text{min},i}`, which
is required for :doc:`fix lambda <fix_lambda>`.
The meaning of :math:`\lambda_{\text{min},i}` is documented in
:doc:`fix lambda <fix_lambda>`, as this pair_style is for use with
:doc:`fix lambda <fix_lambda>` only.
This pair_style requires only the global cutoff as argument.
The remaining quantities, that are required to calculate
:math:`\lambda_{\text{min},i}` are extracted from
:doc:`fix lambda <fix_lambda>` and, thus,
do not need to be passed to this pair_style as arguments.
.. warning::
The cutoff given as argument to this pair style is only relevant for the
neighbor list creation. The radii, which define :math:`r_{\lambda,\text{hi}}` and :math:`r_{\lambda,\text{lo}}` are defined by :doc:`fix lambda <fix_lambda>`.
The computation of :math:`\lambda_{\text{min},i}` is done by this
pair_style instead of by :doc:`fix lambda <fix_lambda>`, as this computation
takes time and this pair_style can be included in the load-balancing via
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`.
A code example for the calculation of the switching parameter for an
adaptive-precision interatomic potential (APIP) is given in the following:
The adaptive-precision potential is created
by combining :doc:`pair_style eam/fs/apip <pair_eam_apip>`
and :doc:`pair_style pace/apip/precise <pair_pace_apip>`.
The input, from which the switching parameter is calculated, is provided
by :doc:`pair lambda_input/csp <pair_lambda_input>`.
The switching parameter is calculated by :doc:`fix lambda <fix_lambda>`,
whereas the spatial transition zone of the switching parameter is calculated
by this pair style.
.. code-block:: LAMMPS
pair_style hybrid/overlay eam/fs/apip pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda/zone 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/apip Cu_precise.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda/zone
fix 2 all lambda 3.0 3.5 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
----------
Mixing, shift, table, tail correction, restart, rRESPA info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
The cutoff distance for this pair style can be mixed. The default mix
value is *geometric*\ . See the "pair_modify" command for details.
This pair style does not support the :doc:`pair_modify <pair_modify>`
shift, table, and tail options.
This pair style writes no information to :doc:`binary restart files <restart>`, so pair_style and pair_coeff commands need
to be specified in an input script that reads a restart file.
This pair style does not support the use of the *inner*, *middle*,
and *outer* keywords of the :doc:`run_style respa <run_style>` command.
----------
Restrictions
""""""""""""
This fix is part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`fix lambda <fix_lambda>`,
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`
:doc:`pair_style lambda_input <pair_lambda_input>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix lambda_thermostat <fix_lambda_thermostat>`,
Default
"""""""
none

146
doc/src/pair_pace_apip.rst Normal file
View File

@ -0,0 +1,146 @@
.. index:: pair_style pace/apip
.. index:: pair_style pace/apip/fast
.. index:: pair_style pace/apip/precise
pair_style pace/apip command
==============================
pair_style pace/apip/fast command
===================================
pair_style pace/apip/precise command
======================================
Constant precision variant: *pace*
Syntax
""""""
.. code-block:: LAMMPS
pair_style pace/apip ... keyword values ...
pair_style pace/apip/fast ... keyword values ...
pair_style pace/apip/precise ... keyword values ...
* one or more keyword/value pairs may be appended
.. parsed-literal::
keyword = keywords of :doc:`pair pace <pair_pace>`
Examples
""""""""
.. code-block:: LAMMPS
pair_style hybrid/overlay pace/apip/fast pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda 12.0
pair_coeff * * pace/apip/fast Cu_fast.yace Cu
pair_coeff * * pace/apip/precise Cu_precise.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda
pair_style hybrid/overlay eam/fs/apip pace/apip/precise lambda_input/csp fcc cutoff 5.0 lambda 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/apip Cu_precise.yace Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda
Description
"""""""""""
Pair style :doc:`pace <pair_pace>` computes interactions using the Atomic
Cluster Expansion (ACE), which is a general expansion of the atomic energy in
multi-body basis functions :ref:`(Drautz19) <Drautz2019_2>`. The *pace*
pair style provides an efficient implementation that is described in
this paper :ref:`(Lysogorskiy21) <Lysogorskiy20211_2>`.
The potential energy :math:`E_i` of an atom :math:`i` of an adaptive-precision
interatomic potential (APIP) according to
:ref:`(Immel25) <Immel2025_7>` is given by
.. math::
E_i^\text{APIP} = \lambda_i E_i^\text{(fast)} + (1-\lambda_i) E_i^\text{(precise)}\,,
whereas the switching parameter :math:`\lambda_i` is computed by
:doc:`fix lambda <fix_lambda>`.
The pair style *pace/apip/precise* computes the potential energy
:math:`(1-\lambda_i) E_i^\text{(pace)}` and the
corresponding force and should be combined
with a fast potential that computes the potential energy
:math:`\lambda_i E_i^\text{(fast)}` and the corresponding force
via :doc:`pair_style hybrid/overlay <pair_hybrid>`.
The pair style *pace/apip/fast* computes the potential energy
:math:`\lambda_i E_i^\text{(pace)}` and the
corresponding force and should be combined
with a precise potential that computes the potential energy
:math:`(1-\lambda_i) E_i^\text{(precise)}` and the corresponding force
via :doc:`pair_style hybrid/overlay <pair_hybrid>`.
The pair_styles *pace/apip/fast* and *pace/apip/precise*
commands may be followed by the optional keywords of
:doc:`pair_style pace <pair_pace>`, which are described
:doc:`here <pair_pace>`.
Mixing, shift, table, tail correction, restart, rRESPA info
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
For atom type pairs I,J and I != J, where types I and J correspond to
two different element types, mixing is performed by LAMMPS with
user-specifiable parameters as described above. You never need to
specify a pair_coeff command with I != J arguments for this style.
This pair styles does not support the :doc:`pair_modify <pair_modify>`
shift, table, and tail options.
This pair styles does not write its information to :doc:`binary restart
files <restart>`, since it is stored in potential files. Thus, you need
to re-specify the pair_style and pair_coeff commands in an input script
that reads a restart file.
This pair styles can only be used via the *pair* keyword of the
:doc:`run_style respa <run_style>` command. It does not support the
*inner*, *middle*, *outer* keywords.
----------
Restrictions
""""""""""""
This pair styles are part of the APIP package. It is only enabled if
LAMMPS was built with that package. See the :doc:`Build package
<Build_package>` page for more info.
Related commands
""""""""""""""""
:doc:`pair_style pace <pair_pace>`,
:doc:`pair_style hybrid/overlay <pair_hybrid>`,
:doc:`fix lambda <fix_lambda>`,
:doc:`fix lambda_thermostat <fix_lambda_thermostat>`,
:doc:`pair_style lambda/zone <pair_lambda_zone>`,
:doc:`pair_style lambda_input <pair_lambda_input>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`fix apip_atom_weight <fix_apip_atom_weight>`
Default
"""""""
See :doc:`pair_style pace <pair_pace>`.
----------
.. _Drautz2019_2:
**(Drautz19)** Drautz, Phys Rev B, 99, 014104 (2019).
.. _Lysogorskiy20211_2:
**(Lysogorskiy21)** Lysogorskiy, van der Oord, Bochkarev, Menon, Rinaldi, Hammerschmidt, Mrovec, Thompson, Csanyi, Ortner, Drautz, npj Comp Mat, 7, 97 (2021).
.. _Immel2025_7:
**(Immel25)** Immel, Drautz and Sutmann, J Chem Phys, 162, 114119 (2025)

View File

@ -188,7 +188,9 @@ accelerated styles exist.
* :doc:`eam/cd <pair_eam>` - concentration-dependent EAM * :doc:`eam/cd <pair_eam>` - concentration-dependent EAM
* :doc:`eam/cd/old <pair_eam>` - older two-site model for concentration-dependent EAM * :doc:`eam/cd/old <pair_eam>` - older two-site model for concentration-dependent EAM
* :doc:`eam/fs <pair_eam>` - Finnis-Sinclair EAM * :doc:`eam/fs <pair_eam>` - Finnis-Sinclair EAM
* :doc:`eam/fs/apip <pair_eam_apip>` - :doc:`adaptive precision <Howto_apip>` version of FS EAM, used as fast potential
* :doc:`eam/he <pair_eam>` - Finnis-Sinclair EAM modified for Helium in metals * :doc:`eam/he <pair_eam>` - Finnis-Sinclair EAM modified for Helium in metals
* :doc:`eam/apip <pair_eam_apip>` - :doc:`adaptive-precision <Howto_apip>` version of EAM, used as fast potential
* :doc:`edip <pair_edip>` - three-body EDIP potential * :doc:`edip <pair_edip>` - three-body EDIP potential
* :doc:`edip/multi <pair_edip>` - multi-element EDIP potential * :doc:`edip/multi <pair_edip>` - multi-element EDIP potential
* :doc:`edpd <pair_mesodpd>` - eDPD particle interactions * :doc:`edpd <pair_mesodpd>` - eDPD particle interactions
@ -217,6 +219,9 @@ accelerated styles exist.
* :doc:`kim <pair_kim>` - interface to potentials provided by KIM project * :doc:`kim <pair_kim>` - interface to potentials provided by KIM project
* :doc:`kolmogorov/crespi/full <pair_kolmogorov_crespi_full>` - Kolmogorov-Crespi (KC) potential with no simplifications * :doc:`kolmogorov/crespi/full <pair_kolmogorov_crespi_full>` - Kolmogorov-Crespi (KC) potential with no simplifications
* :doc:`kolmogorov/crespi/z <pair_kolmogorov_crespi_z>` - Kolmogorov-Crespi (KC) potential with normals along z-axis * :doc:`kolmogorov/crespi/z <pair_kolmogorov_crespi_z>` - Kolmogorov-Crespi (KC) potential with normals along z-axis
* :doc:`lambda/zone <pair_lambda_zone>` - transition zone of an :doc:`adaptive-precision interatomic potential <Howto_apip>`
* :doc:`lambda_input <pair_lambda_input>` - constant as input for the precision calculation of an :doc:`adaptive-precision interatomic potential (APIP) <Howto_apip>`
* :doc:`lambda_input/csp <pair_lambda_input>` - CSP as input for the precision calculation of an :doc:`adaptive-precision interatomic potential (APIP) <Howto_apip>`
* :doc:`lcbop <pair_lcbop>` - long-range bond-order potential (LCBOP) * :doc:`lcbop <pair_lcbop>` - long-range bond-order potential (LCBOP)
* :doc:`lebedeva/z <pair_lebedeva_z>` - Lebedeva interlayer potential for graphene with normals along z-axis * :doc:`lebedeva/z <pair_lebedeva_z>` - Lebedeva interlayer potential for graphene with normals along z-axis
* :doc:`lennard/mdf <pair_mdf>` - LJ potential in A/B form with a taper function * :doc:`lennard/mdf <pair_mdf>` - LJ potential in A/B form with a taper function
@ -329,6 +334,9 @@ accelerated styles exist.
* :doc:`oxrna2/xstk <pair_oxrna2>` - * :doc:`oxrna2/xstk <pair_oxrna2>` -
* :doc:`pace <pair_pace>` - Atomic Cluster Expansion (ACE) machine-learning potential * :doc:`pace <pair_pace>` - Atomic Cluster Expansion (ACE) machine-learning potential
* :doc:`pace/extrapolation <pair_pace>` - Atomic Cluster Expansion (ACE) machine-learning potential with extrapolation grades * :doc:`pace/extrapolation <pair_pace>` - Atomic Cluster Expansion (ACE) machine-learning potential with extrapolation grades
* :doc:`pace/apip <pair_pace_apip>` - :doc:`adaptive-precision <Howto_apip>` version of ACE, used as precise potential
* :doc:`pace/apip/fast <pair_pace_apip>` - :doc:`adaptive-precision <Howto_apip>` version of ACE, used as fast potential
* :doc:`pace/apip/precise <pair_pace_apip>` - :doc:`adaptive-precision <Howto_apip>` version of ACE, used as precise potential
* :doc:`pedone <pair_pedone>` - Pedone (PMMCS) potential (non-Coulomb part) * :doc:`pedone <pair_pedone>` - Pedone (PMMCS) potential (non-Coulomb part)
* :doc:`pod <pair_pod>` - Proper orthogonal decomposition (POD) machine-learning potential * :doc:`pod <pair_pod>` - Proper orthogonal decomposition (POD) machine-learning potential
* :doc:`peri/eps <pair_peri>` - Peridynamic EPS potential * :doc:`peri/eps <pair_peri>` - Peridynamic EPS potential

View File

@ -131,6 +131,8 @@ anton
Antonelli Antonelli
anysize anysize
api api
apip
APIP
apolar apolar
Apoorva Apoorva
Appl Appl
@ -640,6 +642,7 @@ CSiC
csld csld
cslib cslib
CSlib CSlib
csp
cstdio cstdio
cstdlib cstdlib
cstring cstring
@ -1354,6 +1357,7 @@ gmres
gname gname
gneb gneb
GNEB GNEB
Godehard
Goerigk Goerigk
Goga Goga
Goldfarb Goldfarb
@ -1588,6 +1592,7 @@ Imageint
Imagemagick Imagemagick
imagename imagename
imd imd
Immel
Impey Impey
impl impl
impropers impropers
@ -3780,6 +3785,7 @@ Thiaville
Thibaudeau Thibaudeau
Thijsse Thijsse
Thirumalai Thirumalai
thr
threebody threebody
thrid thrid
ThunderX ThunderX

View File

@ -0,0 +1,34 @@
NOTE: This is a simple potential for the example. Production usage without testing is not recommended. Provided by Yury Lysogorskiy (ICAMS, RUB, Germany).
elements: [Cu]
E0: [0]
deltaSplineBins: 0.001
embeddings:
0: {ndensity: 2, FS_parameters: [1, 1, 1, 0.5], npoti: FinnisSinclairShiftedScaled, rho_core_cutoff: 100000, drho_core_cutoff: 250}
bonds:
[0, 0]: {nradmax: 2, lmax: 2, nradbasemax: 15, radbasename: ChebPow, radparameters: [2], radcoefficients: [[[0.99440439385969503, -0.085048653403583918, -0.23248632054717755, -0.22732701549371864, 0.026354948476648921, 0.21853318667456997, 0.05745747498169812, -0.19717925712228765, -0.11474256770370879, 0.12738668745839368, 0.053777769435472259, -0.11094768379576209, 0.072620812391582482, -0.058715761632824881, 0.030359986427775303], [0.96259704765772924, -0.10129488003029259, -0.10345557604916655, -0.020393848425879282, 0.076671442494272601, 0.10318554794001746, 0.0555341702761026, 0.00083194423680727696, -0.018184436957498409, -0.021866885826555403, -0.020179969116479776, 0.021880011516616484, 0.053112509345249602, -0.083707026393616657, 0.020611714544479017], [1.001530579978529, -0.030080648426358471, -0.13318582671063051, -0.24371635685809706, -0.22760541127468878, -0.041144767051648642, 0.18080289144697201, 0.24543156067198274, 0.11014559411659355, -0.069512010077804498, -0.1172049950938457, -0.027509386703874331, 0.056985864219913585, 0.037536629112081353, -0.044222474537374087]], [[0.25716120576634355, 1.7485527550537943, 0.91889737965719875, 0.50902244208852199, -0.15895537149482841, -0.48109723575282892, -0.17843605933015286, 0.39450608859531944, 0.59293909285591195, 0.18268386912819001, -0.34706543720907351, -0.3210061634328315, 0.21678650779400246, 0.39500148786376449, -0.31820913370341625], [0.0079213202761679105, 1.0212489038630681, 0.011530454475879359, -0.049445152058907642, -0.15268524878755677, -0.2319378608755131, -0.20612580998548105, -0.067027395211212315, 0.08241096034972574, 0.11288597065081186, 0.01355948960244063, -0.074722461388416803, -0.022724332047049267, 0.088871664887057056, 0.031667459613258314], [-0.0069872405356639312, 0.9939655327342134, 0.035044055182587928, 0.099765277857093104, 0.11687607289674087, 0.030241996404391416, -0.12367698594314165, -0.22480900218170197, -0.17727517861619441, -0.015144941558075584, 0.11375495728241894, 0.090680932947050971, -0.041190210394591399, -0.10085768296286811, 0.055789864104988186]]], prehc: 0, lambdahc: 0, rcut: 3.8999999999999999, dcut: 0.01, rcut_in: 0, dcut_in: 0, inner_cutoff_type: density}
functions:
0:
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [1], ls: [0], ms_combs: [0], ctildes: [0.26072556900842869, -0.03073189825062177]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [2], ls: [0], ms_combs: [0], ctildes: [0.64429175483702295, -0.1630534353246999]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [3], ls: [0], ms_combs: [0], ctildes: [0.51856313423563594, -0.4259316875879266]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [4], ls: [0], ms_combs: [0], ctildes: [-0.078113533662468398, -0.70352070540668643]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [5], ls: [0], ms_combs: [0], ctildes: [-0.45633111544093646, -0.7859368117550467]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [6], ls: [0], ms_combs: [0], ctildes: [-0.19608401600520556, -0.59151667874441172]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [7], ls: [0], ms_combs: [0], ctildes: [0.30580228338697285, -0.29248216980800118]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [8], ls: [0], ms_combs: [0], ctildes: [0.40167461008815436, -0.15647925731818518]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [9], ls: [0], ms_combs: [0], ctildes: [0.053519057558225343, -0.25900906688118652]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [10], ls: [0], ms_combs: [0], ctildes: [-0.20446546815457517, -0.40019216010057629]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [11], ls: [0], ms_combs: [0], ctildes: [-0.070020661105060208, -0.33441939205411986]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [12], ls: [0], ms_combs: [0], ctildes: [0.15734064575001952, -0.055233119903794807]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [13], ls: [0], ms_combs: [0], ctildes: [0.10021406559793103, 0.18641744536767416]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [14], ls: [0], ms_combs: [0], ctildes: [-0.14066730990975543, 0.14711096149210373]}
- {mu0: 0, rank: 1, ndensity: 2, num_ms_combs: 1, mus: [0], ns: [15], ls: [0], ms_combs: [0], ctildes: [0.031100766650549283, -0.13720067925313634]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 1, mus: [0, 0], ns: [1, 1], ls: [0, 0], ms_combs: [0, 0], ctildes: [1.0984212008195524, 0.49756623164565855]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 2, mus: [0, 0], ns: [1, 1], ls: [1, 1], ms_combs: [0, 0, 1, -1], ctildes: [0.2591109116320176, 0.21348077494861176, -0.5182218232640351, -0.4269615498972234]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 3, mus: [0, 0], ns: [1, 1], ls: [2, 2], ms_combs: [0, 0, 1, -1, 2, -2], ctildes: [0.015905361441871636, 0.023783303055646809, -0.031810722883743273, -0.047566606111293624, 0.031810722883743286, 0.047566606111293638]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 1, mus: [0, 0], ns: [2, 1], ls: [0, 0], ms_combs: [0, 0], ctildes: [0.63958612617724186, 1.6623415103929948]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 2, mus: [0, 0], ns: [2, 1], ls: [1, 1], ms_combs: [0, 0, 1, -1], ctildes: [0.14199022782503917, 0.0069900458821809735, -0.28398045565007829, -0.013980091764361944]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 3, mus: [0, 0], ns: [2, 1], ls: [2, 2], ms_combs: [0, 0, 1, -1, 2, -2], ctildes: [0.028732470496968317, -0.037173039560267927, -0.05746494099393664, 0.074346079120535868, 0.057464940993936654, -0.074346079120535882]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 1, mus: [0, 0], ns: [2, 2], ls: [0, 0], ms_combs: [0, 0], ctildes: [0.056442895466964321, 0.0054387873274233034]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 2, mus: [0, 0], ns: [2, 2], ls: [1, 1], ms_combs: [0, 0, 1, -1], ctildes: [0.025326283180140272, -0.19511149476156769, -0.050652566360280531, 0.39022298952313533]}
- {mu0: 0, rank: 2, ndensity: 2, num_ms_combs: 3, mus: [0, 0], ns: [2, 2], ls: [2, 2], ms_combs: [0, 0, 1, -1, 2, -2], ctildes: [0.012754475331985416, -0.058934602152610385, -0.025508950663970836, 0.11786920430522078, 0.025508950663970843, -0.11786920430522081]}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
The APIP package is based on the paper:
David Immel, Ralf Drautz, Godehard Sutmann; Adaptive-precision potentials for large-scale atomistic simulations. J. Chem. Phys. 14 March 2025; 162 (11): 114119. https://doi.org/10.1063/5.0245877
The pair_style pace/apip requires the installation of lib/pace of the ML-PACE package.
The installation of lib/pace is described in src/ML-PACE/README .
Examples of how to use an adaptive-precision potential are provided in examples/PACKAGES/apip .
in.vacancy contains a small example that can be used to visualize the transition region and get a visual impression of the selected parameters.
in.surface.balance in a more realistic example, in which a surface is simulated and the benefit of fix apip_atom_weight and fix balance for adaptive-precision interatomic potentials is demonstrated.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
##################################################
# parameters of the adaptive-precision potential #
##################################################
# We couple an EAM potential with an ACE potential.
variable eamfs_file string "Cu_300K_Immel_2023.eam.fs"
variable ace_file1 string "Cu-1.yace"
variable ace_file2 string "../../../potentials/Cu-PBE-core-rep.ace"
# The csp is used as detection mechanism for atoms of interest.
variable csp_lattice string "fcc"
variable csp_cutoff equal 6.0
# The range [r_sw_lo, r_sw_hi] determines where the switching parameter changes from 0 to 1.
variable r_sw_lo equal 4.0
variable r_sw_hi equal 12.0
# Thresholds between which the switching parameter changes from 1 to 0 based on the csp.
variable lambda_input_thr_lo equal 7.5
variable lambda_input_thr_hi equal 8.0
# Number of averaged steps.
variable lambda_input_histlen equal 110
variable lambda_histlen equal 110
# Minimum required change of the switching parameter
variable min_delta_lambda equal 1/${lambda_histlen}
# number of atoms rescaled by the lambda_thermostat
variable N_rescaling equal 600
# basic stuff
units metal
dimension 3
boundary p p s
atom_style apip # own atom style required for APIP
timestep 0.001
read_data data.surface.balance
fix nve all nve
comm_style tiled
# Only the upper surface should be treated precisely.
# Thus, we create group, for whose atoms the csp is ignored, as the corresponding
# argument of fix lambda is used.
region bottom block INF INF INF INF INF 0 units box
group group_ignore_csp region bottom
# use adaptive-precision eam-ace potential with lambda_thermostat
pair_style hybrid/overlay eam/fs/apip pace/apip lambda_input/csp ${csp_lattice} cutoff ${csp_cutoff} lambda/zone ${r_sw_hi}
pair_coeff * * eam/fs/apip ${eamfs_file} Cu
pair_coeff * * pace/apip ${ace_file2} Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda/zone
fix lambda all lambda ${lambda_input_thr_lo} ${lambda_input_thr_hi} time_averaged_zone ${r_sw_lo} ${r_sw_hi} ${lambda_input_histlen} ${lambda_histlen} min_delta_lambda ${min_delta_lambda} group_ignore_lambda_input group_ignore_csp
fix lambda_thermostat all lambda_thermostat N_rescaling ${N_rescaling}
fix weight_atom all apip_atom_weight 10 eam ace lambda_input lambda/zone all
# store weight in variable
fix property_atom all property/atom d_usedweight
variable myweight atom f_weight_atom
#dump 1 all custom 10 dump/surface_ap_balance.dump.* id type x y z lambda lambda_input f_weight_atom proc d_usedweight
## apply load balancing
## no load-balancing
#fix balance all balance 10 0.9 report weight time 1.0 weight store usedweight
## load balancing with times per atom per processor
#fix balance all balance 10 0.9 rcb weight time 1.0 weight store usedweight
## load balancing with an approximated load per atom by fix apip_atom_weight
fix balance all balance 10 0.9 rcb weight var myweight weight store usedweight
thermo_style custom step f_balance spcpu f_weight_atom[*]
thermo 10
run 100

View File

@ -0,0 +1,92 @@
##################################################
# parameters of the adaptive-precision potential #
##################################################
# We couple an EAM potential with an ACE potential.
variable eamfs_file string "Cu_300K_Immel_2023.eam.fs"
variable ace_file1 string "Cu-1.yace"
variable ace_file2 string "../../../potentials/Cu-PBE-core-rep.ace"
# The csp is used as detection mechanism for atoms of interest.
variable csp_lattice string "fcc"
variable csp_cutoff equal 6.0
# The range [r_sw_lo, r_sw_hi] determines where the switching parameter changes from 0 to 1.
variable r_sw_lo equal 4.0
variable r_sw_hi equal 12.0
# Thresholds between which the switching parameter changes from 1 to 0 based on the csp.
variable lambda_input_thr_lo equal 2.5
variable lambda_input_thr_hi equal 3.0
# Number of averaged steps.
variable lambda_input_histlen equal 110
variable lambda_histlen equal 110
# Minimum required change of the switching parameter
variable min_delta_lambda equal 1/${lambda_histlen}
# number of atoms rescaled by the lambda_thermostat
variable N_rescaling equal 600
## basic stuff
units metal
atom_style apip # own atom style required for APIP
timestep 0.001
# copper at room temperature with a vacancy
read_data data.vacancy
fix nve all nve
## Use adaptive-precision ace-ace potential without lambda_thermostat.
## Calculate atomic weight that could be used for load balancing.
#pair_style hybrid/overlay pace/apip/fast pace/apip/precise lambda_input/csp ${csp_lattice} cutoff ${csp_cutoff} lambda/zone ${r_sw_hi}
#pair_coeff * * pace/apip/fast ${ace_file1} Cu
#pair_coeff * * pace/apip/precise ${ace_file2} Cu
#pair_coeff * * lambda_input/csp
#pair_coeff * * lambda/zone
#fix lambda all lambda ${lambda_input_thr_lo} ${lambda_input_thr_hi} time_averaged_zone ${r_sw_lo} ${r_sw_hi} ${lambda_input_histlen} ${lambda_histlen} min_delta_lambda ${min_delta_lambda}
#fix weight_atom all apip_atom_weight 100 ace ace lambda_input lambda/zone all
# Use adaptive-precision eam-ace potential without lambda_thermostat.
# Calculate atomic weight that could be used for load balancing.
pair_style hybrid/overlay eam/fs/apip pace/apip lambda_input/csp ${csp_lattice} cutoff ${csp_cutoff} lambda/zone ${r_sw_hi}
pair_coeff * * eam/fs/apip ${eamfs_file} Cu
pair_coeff * * pace/apip ${ace_file2} Cu
pair_coeff * * lambda_input/csp
pair_coeff * * lambda/zone
fix lambda all lambda ${lambda_input_thr_lo} ${lambda_input_thr_hi} time_averaged_zone ${r_sw_lo} ${r_sw_hi} ${lambda_input_histlen} ${lambda_histlen} min_delta_lambda ${min_delta_lambda}
fix weight_atom all apip_atom_weight 100 eam ace lambda_input lambda/zone all
## One can comment out fix lambda_thermostat to see the energy change caused by the neglection of the
## gradient of the switching function. This neglection can be compensated by the local thermostat and the
## energy can be conserved within numerical precision.
fix lambda_thermostat all lambda_thermostat N_rescaling ${N_rescaling} store_atomic_forces 100
thermo_style custom step etotal
thermo 1
run 100
# dump atoms
#write_dump all custom dump/vacancy.dump.* id type x y z fx fy fz lambda lambda_input f_weight_atom f_lambda_thermostat[*]
## A smooth restart of the simulation is possible as the history of lambda and lambda_input is stored.
#write_restart vacancy_ap.restart
#clear
#read_restart vacancy_ap.restart
#pair_style hybrid/overlay eam/fs/apip pace/apip lambda_input/csp fcc cutoff 6.0 lambda/zone 12.0
#pair_coeff * * eam/fs/apip "Cu_300K_Immel_2023.eam.fs" Cu
#pair_coeff * * pace/apip "../../../potentials/Cu-PBE-core-rep.ace" Cu
#pair_coeff * * lambda_input/csp
#pair_coeff * * lambda/zone
#fix lambda all lambda 2.5 3.0 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda $(1/110)
#fix lambda_thermostat all lambda_thermostat N_rescaling ${N_rescaling} store_atomic_forces 100
#fix nve all nve
#thermo_style custom step etotal
#thermo 1
#run 10
#shell rm vacancy_ap.restart

View File

@ -0,0 +1,108 @@
LAMMPS (4 Feb 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 -1.1656272) to (36.15 36.15 362.81506)
1 by 1 by 4 MPI processor grid
reading atoms ...
40200 atoms
reading velocities ...
40200 velocities
read_data CPU = 1.143 seconds
200 atoms in group group_ignore_csp
ACE version: 2023.11.25
Recursive evaluator is used by ACE
Loading ../../../potentials/Cu-PBE-core-rep.ace
Total number of basis functions
Cu: 16 (r=1) 726 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
atomic load lambda:
fast potential: extract eam/apip:time_per_atom
precise potential: extract pace/apip:time_per_atom
lambda_input: extract lambda_input:time_per_atom
lambda: extract lambda_zone:time_per_atom
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Your simulation uses code contributions which should be cited:
- fix lambda command: doi.org/10.1063/5.0245877
The log file lists these citations in BibTeX format.
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 14
ghost atom cutoff = 14
binsize = 7, bins = 6 6 52
5 neighbor lists, perpetual/occasional/extra = 5 0 0
(1) pair eam/fs/apip, perpetual
attributes: full, newton on
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(2) pair pace/apip, perpetual, trim from (4)
attributes: full, newton on, cut 9.4
pair build: trim
stencil: none
bin: none
(3) pair lambda_input/csp, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat, perpetual, copy from (1)
attributes: full, newton on
pair build: copy
stencil: none
bin: none
Setting up Verlet run ...
Unit style : metal
Current step : 0
Time step : 0.001
Per MPI rank memory allocation (min/avg/max) = 297.9 | 306.9 | 315.9 Mbytes
Step f_balance S/CPU f_weight_atom[1] f_weight_atom[2] f_weight_atom[3] f_weight_atom[4]
0 1.0139303 0 0 0 0 0
10 2.4606058 1.8697255 5.5957166e-06 0.0002553649 2.7500342e-06 4.6802407e-06
20 2.0765883 1.7644069 5.9864867e-06 0.0002751227 2.9574758e-06 5.078614e-06
30 2.0955022 1.729507 5.9541701e-06 0.00028593742 3.1165205e-06 5.2349106e-06
40 2.1015667 1.7241359 6.0115135e-06 0.00028640643 3.1154236e-06 5.2528746e-06
50 2.0981491 1.6201337 6.0470102e-06 0.00028778687 3.1443635e-06 5.2497711e-06
60 2.1327959 1.3101362 7.7658865e-06 0.00037855901 4.1641447e-06 6.9162514e-06
70 2.098134 1.3357863 7.5378657e-06 0.00037046011 4.0510159e-06 6.9345367e-06
80 2.1000138 1.3475583 7.5679751e-06 0.00036818876 4.0188623e-06 6.7257237e-06
90 2.0966073 1.1728052 7.6393621e-06 0.00036755436 4.0052073e-06 6.6818754e-06
100 1.8896711 1.3506386 7.5946481e-06 0.00036649967 3.984174e-06 6.6518701e-06
Loop time of 67.2407 on 4 procs for 100 steps with 40200 atoms
Performance: 0.128 ns/day, 186.780 hours/ns, 1.487 timesteps/s, 59.785 katom-step/s
99.8% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 10.34 | 21.891 | 56.059 | 421.6 | 32.56
Neigh | 8.1064 | 9.1925 | 10.667 | 33.9 | 13.67
Comm | 1.3105 | 23.525 | 45.141 | 437.6 | 34.99
Output | 0.00053598 | 0.73462 | 2.8834 | 144.7 | 1.09
Modify | 1.4504 | 2.6152 | 5.8041 | 114.0 | 3.89
Other | | 9.282 | | | 13.80
Nlocal: 10050 ave 10180 max 10000 min
Histogram: 2 1 0 0 0 0 0 0 0 1
Nghost: 27854.5 ave 30385 max 25525 min
Histogram: 2 0 0 0 0 0 0 0 0 2
Neighs: 0 ave 0 max 0 min
Histogram: 4 0 0 0 0 0 0 0 0 0
FullNghs: 9.49222e+06 ave 9.76687e+06 max 9.29466e+06 min
Histogram: 2 0 0 0 0 0 1 0 0 1
Total # of neighbors = 37968882
Ave neighs/atom = 944.49955
Neighbor list builds = 10
Dangerous builds = 0
Total wall time: 0:01:11

View File

@ -0,0 +1,108 @@
LAMMPS (4 Feb 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 -1.1656272) to (36.15 36.15 362.81506)
1 by 1 by 4 MPI processor grid
reading atoms ...
40200 atoms
reading velocities ...
40200 velocities
read_data CPU = 1.141 seconds
200 atoms in group group_ignore_csp
ACE version: 2023.11.25
Recursive evaluator is used by ACE
Loading ../../../potentials/Cu-PBE-core-rep.ace
Total number of basis functions
Cu: 16 (r=1) 726 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
atomic load lambda:
fast potential: extract eam/apip:time_per_atom
precise potential: extract pace/apip:time_per_atom
lambda_input: extract lambda_input:time_per_atom
lambda: extract lambda_zone:time_per_atom
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Your simulation uses code contributions which should be cited:
- fix lambda command: doi.org/10.1063/5.0245877
The log file lists these citations in BibTeX format.
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 14
ghost atom cutoff = 14
binsize = 7, bins = 6 6 52
5 neighbor lists, perpetual/occasional/extra = 5 0 0
(1) pair eam/fs/apip, perpetual
attributes: full, newton on
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(2) pair pace/apip, perpetual, trim from (4)
attributes: full, newton on, cut 9.4
pair build: trim
stencil: none
bin: none
(3) pair lambda_input/csp, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat, perpetual, copy from (1)
attributes: full, newton on
pair build: copy
stencil: none
bin: none
Setting up Verlet run ...
Unit style : metal
Current step : 0
Time step : 0.001
Per MPI rank memory allocation (min/avg/max) = 298.1 | 306.3 | 314.5 Mbytes
Step f_balance S/CPU f_weight_atom[1] f_weight_atom[2] f_weight_atom[3] f_weight_atom[4]
0 1 0 0 0 0 0
10 1.0010585 1.8144651 5.3526071e-06 0.00025703793 2.7730117e-06 4.7190461e-06
20 1.0016587 3.0895281 6.0547706e-06 0.00030627156 2.9311147e-06 2.8627034e-06
30 1.0006595 2.1815977 7.7086095e-06 0.00034346642 3.4831025e-06 3.6494203e-06
40 1.0003702 2.7847348 6.9889341e-06 0.00031605872 3.2092228e-06 2.9177266e-06
50 1.0013639 2.8085372 7.1723284e-06 0.00032050748 3.2148509e-06 2.9280764e-06
60 1.001567 2.7692583 7.3191318e-06 0.00032348311 3.2792367e-06 3.0731907e-06
70 1.0008473 2.7848811 7.4547446e-06 0.00032207119 3.2487866e-06 3.0045227e-06
80 1.0008791 2.1902426 8.8463371e-06 0.00037692301 3.878561e-06 3.5166374e-06
90 1.0005633 2.0207998 1.0049193e-05 0.00043004983 4.4508803e-06 4.0369005e-06
100 1.0011185 2.11148 9.5552543e-06 0.00040548477 4.282726e-06 4.0021617e-06
Loop time of 41.9356 on 4 procs for 100 steps with 40200 atoms
Performance: 0.206 ns/day, 116.488 hours/ns, 2.385 timesteps/s, 95.861 katom-step/s
99.2% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 21.856 | 23.33 | 25.627 | 29.8 | 55.63
Neigh | 12.078 | 12.235 | 12.335 | 2.8 | 29.18
Comm | 1.8098 | 3.4438 | 5.8848 | 91.3 | 8.21
Output | 0.0013883 | 0.0067437 | 0.010676 | 4.1 | 0.02
Modify | 1.7044 | 1.8301 | 1.9304 | 7.0 | 4.36
Other | | 1.09 | | | 2.60
Nlocal: 10050 ave 10381 max 9892 min
Histogram: 1 2 0 0 0 0 0 0 0 1
Nghost: 52763 ave 52921 max 52432 min
Histogram: 1 0 0 0 0 0 0 0 2 1
Neighs: 0 ave 0 max 0 min
Histogram: 4 0 0 0 0 0 0 0 0 0
FullNghs: 9.49222e+06 ave 9.80751e+06 max 9.34231e+06 min
Histogram: 1 2 0 0 0 0 0 0 0 1
Total # of neighbors = 37968882
Ave neighs/atom = 944.49955
Neighbor list builds = 10
Dangerous builds = 0
Total wall time: 0:00:46

View File

@ -0,0 +1,108 @@
LAMMPS (4 Feb 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 -1.1656272) to (36.15 36.15 362.81506)
1 by 1 by 4 MPI processor grid
reading atoms ...
40200 atoms
reading velocities ...
40200 velocities
read_data CPU = 1.549 seconds
200 atoms in group group_ignore_csp
ACE version: 2023.11.25
Recursive evaluator is used by ACE
Loading ../../../potentials/Cu-PBE-core-rep.ace
Total number of basis functions
Cu: 16 (r=1) 726 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
atomic load lambda:
fast potential: extract eam/apip:time_per_atom
precise potential: extract pace/apip:time_per_atom
lambda_input: extract lambda_input:time_per_atom
lambda: extract lambda_zone:time_per_atom
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Your simulation uses code contributions which should be cited:
- fix lambda command: doi.org/10.1063/5.0245877
The log file lists these citations in BibTeX format.
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 14
ghost atom cutoff = 14
binsize = 7, bins = 6 6 52
5 neighbor lists, perpetual/occasional/extra = 5 0 0
(1) pair eam/fs/apip, perpetual
attributes: full, newton on
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(2) pair pace/apip, perpetual, trim from (4)
attributes: full, newton on, cut 9.4
pair build: trim
stencil: none
bin: none
(3) pair lambda_input/csp, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat, perpetual, copy from (1)
attributes: full, newton on
pair build: copy
stencil: none
bin: none
Setting up Verlet run ...
Unit style : metal
Current step : 0
Time step : 0.001
Per MPI rank memory allocation (min/avg/max) = 298.1 | 306.3 | 314.5 Mbytes
Step f_balance S/CPU f_weight_atom[1] f_weight_atom[2] f_weight_atom[3] f_weight_atom[4]
0 1 0 0 0 0 0
10 1.0001725 1.7198837 5.5314343e-06 0.00025930431 2.7883957e-06 4.707143e-06
20 1.0001862 1.8265719 5.6099429e-06 0.00027931903 2.9758981e-06 5.002563e-06
30 1.00056 1.8317507 5.9076763e-06 0.0002894911 3.1109902e-06 5.1394341e-06
40 1.0004862 2.5730799 6.243133e-06 0.00030439841 3.1674858e-06 3.784468e-06
50 1.0001422 1.7867846 6.1734458e-06 0.00029065411 3.1286655e-06 5.1705237e-06
60 1.0008942 1.4365047 7.2226216e-06 0.00035501193 3.8052887e-06 6.42234e-06
70 1.0001463 1.9946081 8.1287731e-06 0.00038928999 4.1631973e-06 4.9840279e-06
80 1.0004234 1.4099223 7.8487793e-06 0.00037185703 4.022587e-06 6.8562431e-06
90 1.000166 1.6477504 1.1740135e-05 0.00039310073 4.5190508e-06 5.9037201e-06
100 1.0004352 1.3977939 7.9664586e-06 0.00037057101 4.0412051e-06 6.8616558e-06
Loop time of 58.5219 on 4 procs for 100 steps with 40200 atoms
Performance: 0.148 ns/day, 162.561 hours/ns, 1.709 timesteps/s, 68.692 katom-step/s
99.7% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 13.129 | 22.408 | 43.047 | 253.7 | 38.29
Neigh | 2.9649 | 8.8754 | 14.209 | 175.4 | 15.17
Comm | 4.4729 | 19.561 | 38.596 | 306.3 | 33.43
Output | 0.0076245 | 0.28239 | 0.93926 | 72.3 | 0.48
Modify | 1.0567 | 2.2026 | 4.445 | 92.5 | 3.76
Other | | 5.192 | | | 8.87
Nlocal: 10050 ave 16633 max 2176 min
Histogram: 1 0 1 0 0 0 0 0 0 2
Nghost: 27619 ave 42218 max 8849 min
Histogram: 1 0 0 1 0 0 0 0 0 2
Neighs: 0 ave 0 max 0 min
Histogram: 4 0 0 0 0 0 0 0 0 0
FullNghs: 9.49222e+06 ave 1.56603e+07 max 1.80672e+06 min
Histogram: 1 0 1 0 0 0 0 0 0 2
Total # of neighbors = 37968882
Ave neighs/atom = 944.49955
Neighbor list builds = 10
Dangerous builds = 0
Total wall time: 0:01:02

View File

@ -0,0 +1,193 @@
LAMMPS (4 Feb 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 0) to (28.92 28.92 28.92)
1 by 2 by 2 MPI processor grid
reading atoms ...
2047 atoms
reading velocities ...
2047 velocities
read_data CPU = 0.037 seconds
ACE version: 2023.11.25
Recursive evaluator is used by ACE
Loading ../../../potentials/Cu-PBE-core-rep.ace
Total number of basis functions
Cu: 16 (r=1) 726 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
atomic load lambda:
fast potential: extract eam/apip:time_per_atom
precise potential: extract pace/apip:time_per_atom
lambda_input: extract lambda_input:time_per_atom
lambda: extract lambda_zone:time_per_atom
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Your simulation uses code contributions which should be cited:
- fix lambda command: doi.org/10.1063/5.0245877
The log file lists these citations in BibTeX format.
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
WARNING: The energy is not conserved when lambda changes as fix lambda_thermostat is not used. (../fix_lambda.cpp:247)
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 14
ghost atom cutoff = 14
binsize = 7, bins = 5 5 5
4 neighbor lists, perpetual/occasional/extra = 4 0 0
(1) pair eam/fs/apip, perpetual
attributes: full, newton on
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(2) pair pace/apip, perpetual, trim from (4)
attributes: full, newton on, cut 9.4
pair build: trim
stencil: none
bin: none
(3) pair lambda_input/csp, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
Setting up Verlet run ...
Unit style : metal
Current step : 0
Time step : 0.001
Per MPI rank memory allocation (min/avg/max) = 58.65 | 58.75 | 59.03 Mbytes
Step TotEng
0 -7409.9869
1 -7409.9869
2 -7409.987
3 -7409.987
4 -7409.987
5 -7409.987
6 -7409.987
7 -7409.987
8 -7409.987
9 -7409.987
10 -7409.9871
11 -7409.9876
12 -7409.9876
13 -7409.9876
14 -7409.988
15 -7409.9876
16 -7409.9864
17 -7409.9864
18 -7409.9853
19 -7409.9847
20 -7409.9865
21 -7409.9852
22 -7409.9867
23 -7409.9864
24 -7409.9869
25 -7409.9844
26 -7409.9849
27 -7409.9856
28 -7409.9851
29 -7409.9835
30 -7409.9843
31 -7409.9861
32 -7409.9862
33 -7409.9869
34 -7409.9863
35 -7409.9885
36 -7409.9924
37 -7409.9945
38 -7409.9942
39 -7409.9903
40 -7409.9909
41 -7409.9962
42 -7409.996
43 -7409.9959
44 -7410.0004
45 -7410.0044
46 -7409.9993
47 -7409.9948
48 -7409.9923
49 -7409.9844
50 -7409.9861
51 -7409.9849
52 -7409.986
53 -7409.9828
54 -7409.9799
55 -7409.9804
56 -7409.9782
57 -7409.9777
58 -7409.9777
59 -7409.9781
60 -7409.982
61 -7409.982
62 -7409.9824
63 -7409.9844
64 -7409.9826
65 -7409.9816
66 -7409.9788
67 -7409.9769
68 -7409.9727
69 -7409.9716
70 -7409.9693
71 -7409.9674
72 -7409.9691
73 -7409.9693
74 -7409.9682
75 -7409.9664
76 -7409.9678
77 -7409.966
78 -7409.9674
79 -7409.9654
80 -7409.9649
81 -7409.9636
82 -7409.967
83 -7409.9693
84 -7409.9655
85 -7409.9654
86 -7409.9644
87 -7409.961
88 -7409.9609
89 -7409.9594
90 -7409.96
91 -7409.9643
92 -7409.9603
93 -7409.9622
94 -7409.9603
95 -7409.9584
96 -7409.9555
97 -7409.9596
98 -7409.9587
99 -7409.9585
100 -7409.9602
Loop time of 10.6279 on 4 procs for 100 steps with 2047 atoms
Performance: 0.813 ns/day, 29.522 hours/ns, 9.409 timesteps/s, 19.261 katom-step/s
99.2% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 7.4326 | 8.9455 | 10.524 | 49.4 | 84.17
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0.089305 | 1.6676 | 3.18 | 114.5 | 15.69
Output | 0.0028034 | 0.0041679 | 0.0061404 | 1.9 | 0.04
Modify | 0.0040779 | 0.0053928 | 0.0062702 | 1.1 | 0.05
Other | | 0.005266 | | | 0.05
Nlocal: 511.75 ave 529 max 502 min
Histogram: 1 1 0 1 0 0 0 0 0 1
Nghost: 7684.25 ave 7693 max 7668 min
Histogram: 1 0 0 0 0 0 0 1 1 1
Neighs: 0 ave 0 max 0 min
Histogram: 4 0 0 0 0 0 0 0 0 0
FullNghs: 490648 ave 507159 max 481313 min
Histogram: 1 1 0 1 0 0 0 0 0 1
Total # of neighbors = 1962590
Ave neighs/atom = 958.76404
Neighbor list builds = 0
Dangerous builds = 0
Total wall time: 0:00:12

View File

@ -0,0 +1,197 @@
LAMMPS (4 Feb 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 0) to (28.92 28.92 28.92)
1 by 2 by 2 MPI processor grid
reading atoms ...
2047 atoms
reading velocities ...
2047 velocities
read_data CPU = 0.041 seconds
ACE version: 2023.11.25
Recursive evaluator is used by ACE
Loading ../../../potentials/Cu-PBE-core-rep.ace
Total number of basis functions
Cu: 16 (r=1) 726 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
atomic load lambda:
fast potential: extract eam/apip:time_per_atom
precise potential: extract pace/apip:time_per_atom
lambda_input: extract lambda_input:time_per_atom
lambda: extract lambda_zone:time_per_atom
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Your simulation uses code contributions which should be cited:
- fix lambda command: doi.org/10.1063/5.0245877
The log file lists these citations in BibTeX format.
CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE-CITE
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 14
ghost atom cutoff = 14
binsize = 7, bins = 5 5 5
5 neighbor lists, perpetual/occasional/extra = 5 0 0
(1) pair eam/fs/apip, perpetual
attributes: full, newton on
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(2) pair pace/apip, perpetual, trim from (4)
attributes: full, newton on, cut 9.4
pair build: trim
stencil: none
bin: none
(3) pair lambda_input/csp, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat, perpetual, copy from (1)
attributes: full, newton on
pair build: copy
stencil: none
bin: none
Setting up Verlet run ...
Unit style : metal
Current step : 0
Time step : 0.001
Per MPI rank memory allocation (min/avg/max) = 61.44 | 61.63 | 62.2 Mbytes
Step TotEng
0 -7409.9869
1 -7409.9869
2 -7409.987
3 -7409.987
4 -7409.987
5 -7409.987
6 -7409.987
7 -7409.987
8 -7409.987
9 -7409.987
10 -7409.987
11 -7409.9871
12 -7409.9871
13 -7409.9871
14 -7409.9871
15 -7409.9871
16 -7409.9871
17 -7409.9871
18 -7409.9871
19 -7409.9872
20 -7409.9872
21 -7409.9872
22 -7409.9872
23 -7409.9872
24 -7409.9872
25 -7409.9872
26 -7409.9872
27 -7409.9872
28 -7409.9872
29 -7409.9872
30 -7409.9872
31 -7409.9872
32 -7409.9872
33 -7409.9872
34 -7409.9872
35 -7409.9872
36 -7409.9872
37 -7409.9873
38 -7409.9873
39 -7409.9873
40 -7409.9873
41 -7409.9873
42 -7409.9874
43 -7409.9874
44 -7409.9874
45 -7409.9874
46 -7409.9875
47 -7409.9875
48 -7409.9875
49 -7409.9875
50 -7409.9875
51 -7409.9876
52 -7409.9876
53 -7409.9876
54 -7409.9876
55 -7409.9876
56 -7409.9876
57 -7409.9876
58 -7409.9876
59 -7409.9876
60 -7409.9876
61 -7409.9876
62 -7409.9875
63 -7409.9875
64 -7409.9875
65 -7409.9875
66 -7409.9874
67 -7409.9874
68 -7409.9874
69 -7409.9873
70 -7409.9873
71 -7409.9873
72 -7409.9873
73 -7409.9872
74 -7409.9872
75 -7409.9872
76 -7409.9872
77 -7409.9872
78 -7409.9871
79 -7409.9871
80 -7409.9871
81 -7409.9871
82 -7409.9871
83 -7409.9871
84 -7409.9871
85 -7409.9871
86 -7409.9871
87 -7409.9871
88 -7409.9871
89 -7409.9871
90 -7409.9871
91 -7409.9871
92 -7409.9871
93 -7409.9872
94 -7409.9872
95 -7409.9872
96 -7409.9872
97 -7409.9872
98 -7409.9872
99 -7409.9872
100 -7409.9872
Loop time of 11.8395 on 4 procs for 100 steps with 2047 atoms
Performance: 0.730 ns/day, 32.887 hours/ns, 8.446 timesteps/s, 17.290 katom-step/s
99.2% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 8.1066 | 9.7912 | 11.642 | 53.8 | 82.70
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0.057574 | 1.9086 | 3.5932 | 121.8 | 16.12
Output | 0.022525 | 0.027196 | 0.031414 | 2.2 | 0.23
Modify | 0.10334 | 0.10795 | 0.11223 | 1.2 | 0.91
Other | | 0.004525 | | | 0.04
Nlocal: 511.75 ave 529 max 502 min
Histogram: 1 1 0 1 0 0 0 0 0 1
Nghost: 7684.25 ave 7693 max 7668 min
Histogram: 1 0 0 0 0 0 0 1 1 1
Neighs: 0 ave 0 max 0 min
Histogram: 4 0 0 0 0 0 0 0 0 0
FullNghs: 490648 ave 507159 max 481313 min
Histogram: 1 1 0 1 0 0 0 0 0 1
Total # of neighbors = 1962590
Ave neighs/atom = 958.76404
Neighbor list builds = 0
Dangerous builds = 0
Total wall time: 0:00:13

24
src/.gitignore vendored
View File

@ -109,6 +109,30 @@
/pair_pace_extrapolation.cpp /pair_pace_extrapolation.cpp
/pair_pace_extrapolation.h /pair_pace_extrapolation.h
/fix_lambda.cpp
/fix_lambda.h
/fix_lambda_thermostat.cpp
/fix_lambda_thermostat.h
/pair_lambda_input.cpp
/pair_lambda_input.h
/pair_lambda_input_csp.cpp
/pair_lambda_input_csp.h
/pair_eam_fs_apip.cpp
/pair_eam_fs_apip.h
/pair_eam_apip.cpp
/pair_eam_apip.h
/pair_lambda_zone.cpp
/pair_lambda_zone.h
/pair_pace_apip.cpp
/pair_pace_apip.h
/pair_pace_apip_precise.cpp
/pair_pace_apip_precise.h
/pair_pace_apip_fast.cpp
/pair_pace_apip_fast.h
/fix_apip_atom_weight.cpp
/fix_apip_atom_weight.h
/pair_pod.cpp /pair_pod.cpp
/pair_pod.h /pair_pod.h
/eapod.cpp /eapod.cpp

73
src/APIP/Install.sh Normal file
View File

@ -0,0 +1,73 @@
# Install/unInstall package files in LAMMPS
# mode = 0/1/2 for uninstall/install/update
mode=$1
# enforce using portable C locale
LC_ALL=C
export LC_ALL
# arg1 = file, arg2 = file it depends on
action () {
if (test $mode = 0) then
rm -f ../$1
elif (! cmp -s $1 ../$1) then
if (test -z "$2" || test -e ../$2) then
cp $1 ..
if (test $mode = 2) then
echo " updating src/$1"
fi
fi
elif (test -n "$2") then
if (test ! -e ../$2) then
rm -f ../$1
fi
fi
}
# all package files with no dependencies
for file in *.cpp *.h; do
test -f ${file} && action $file
done
# edit 2 Makefile.package files to include/exclude package info
if (test $1 = 1) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*pace[^ \t]* //' ../Makefile.package
sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(pace_SYSINC) |' ../Makefile.package
sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(pace_SYSLIB) |' ../Makefile.package
sed -i -e 's|^PKG_SYSPATH =[ \t]*|&$(pace_SYSPATH) |' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^[ \t]*include.*pace.*$/d' ../Makefile.package.settings
# multiline form needed for BSD sed on Macs
sed -i -e '4 i \
include ..\/..\/lib\/pace\/Makefile.lammps
' ../Makefile.package.settings
fi
elif (test $1 = 0) then
# The ML-PACE package depends also on the pace library.
# Remove in Makefiles only if there are no remaining includes from ML-PACE.
# grep searches also in kspace.cpp and thus returns no error.
if (test $(grep '#include "ace' ../*pace*.cpp | wc -l) = 0) then
# update Makefiles
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*pace[^ \t]* //' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^[ \t]*include.*pace.*$/d' ../Makefile.package.settings
fi
fi
fi

8
src/APIP/README Normal file
View File

@ -0,0 +1,8 @@
The APIP package is based on the paper:
David Immel, Ralf Drautz, Godehard Sutmann; Adaptive-precision potentials for large-scale atomistic simulations. J. Chem. Phys. 14 March 2025; 162 (11): 114119. https://doi.org/10.1063/5.0245877
Example of how to use an adaptive-precision potential is provided in examples/PACKAGES/APIP .
The pair_style pace/apip requires the installation of lib/pace of the ML-PACE package.
The installation of lib/pace is described in src/ML-PACE/README .

View File

@ -0,0 +1,539 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "fix_apip_atom_weight.h"
#include "atom.h"
#include "atom_vec_apip.h"
#include "comm.h"
#include "error.h"
#include "fix_store_atom.h"
#include "force.h"
#include "group.h"
#include "modify.h"
#include "pair.h"
#include "timer.h"
#include "update.h"
using namespace LAMMPS_NS;
using namespace FixConst;
FixApipAtomWeight::FixApipAtomWeight(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg), fixstore(nullptr), time_simple_extract_name(nullptr),
time_complex_extract_name(nullptr), time_lambda_extract_name(nullptr),
time_group_extract_name(nullptr), time_group_name(nullptr), fix_lambda(nullptr),
ap_timer(nullptr)
{
if (narg < 9) error->all(FLERR, "Illegal fix balance command");
ap_timer = new APIPtimer(lmp);
// set defaults
time_simple_atom = time_complex_atom = time_group_atom = time_lambda_atom = -1;
n_simple = n_complex = n_group = n_lambda = 0;
nevery = -1;
rescale_work = true;
peratom_flag = 1;
size_peratom_cols = 0; // vector
time_group_i = -1;
time_group_bit = 0;
vector_flag = 1;
size_vector = 4;
extvector = 0;
for (int i = 0; i < size_vector; i++) avg_time_atom[i] = 0;
nevery = utils::inumeric(FLERR, arg[3], false, lmp);
if (strcmp(arg[4], "eam") == 0) {
time_simple_extract_name = utils::strdup("eam/apip:time_per_atom");
} else if (strcmp(arg[4], "ace") == 0) {
time_simple_extract_name = utils::strdup("pace/apip/fast:time_per_atom");
} else {
time_simple_atom = utils::numeric(FLERR, arg[4], false, lmp);
avg_time_atom[0] = time_simple_atom;
}
if (strcmp(arg[5], "ace") == 0) {
time_complex_extract_name = utils::strdup("pace/apip:time_per_atom");
} else {
time_complex_atom = utils::numeric(FLERR, arg[5], false, lmp);
avg_time_atom[1] = time_complex_atom;
}
if (strcmp(arg[6], "lambda_input") == 0) {
time_group_extract_name = utils::strdup("lambda_input:time_per_atom");
} else {
time_group_atom = utils::numeric(FLERR, arg[6], false, lmp);
avg_time_atom[2] = time_group_atom;
}
if (strcmp(arg[7], "lambda/zone") == 0) {
time_lambda_extract_name = utils::strdup("lambda_zone:time_per_atom");
} else {
time_lambda_atom = utils::numeric(FLERR, arg[7], false, lmp);
avg_time_atom[3] = time_lambda_atom;
}
// read name of group
time_group_name = utils::strdup(arg[8]);
time_group_i = group->find(time_group_name);
if (time_group_i == -1)
error->all(FLERR, "apip_atom_weight: group {} does not exist", time_group_name);
time_group_bit = group->bitmask[time_group_i];
// parse remaining arguments
for (int iarg = 9; iarg < narg; iarg++) {
if (strcmp(arg[iarg], "no_rescale") == 0) {
rescale_work = false;
} else {
error->all(FLERR, "apip_atom_weight: unknown argument {}", arg[iarg]);
}
}
// check arguments
if (nevery < 1) error->all(FLERR, "apip_atom_weight: nevery > 0 required");
if (!atom->lambda_required_flag)
error->all(FLERR, "apip_atom_weight: atomic style with lambda_required required");
if (time_simple_extract_name || time_complex_extract_name || time_group_extract_name ||
time_lambda_extract_name) {
if (force->pair == nullptr)
error->all(FLERR, "apip_atom_weight: extract requires a defined pair style");
}
int useless_dim = -1;
void *extracted_ptr = nullptr;
if (time_simple_extract_name) {
if (force->pair->extract(time_simple_extract_name, useless_dim) == nullptr)
error->all(FLERR, "apip_atom_weight: simple time cannot be extracted with {} from {}",
time_simple_extract_name, force->pair_style);
} else {
if (time_simple_atom <= 0)
error->all(FLERR, "apip_atom_weight: time_simple_atom needs to be positive instead of {}",
time_simple_atom);
}
if (time_complex_extract_name) {
if (force->pair->extract(time_complex_extract_name, useless_dim) == nullptr)
error->all(FLERR, "apip_atom_weight: complex time cannot be extracted with {} from {}",
time_complex_extract_name, force->pair_style);
} else {
if (time_complex_atom <= 0)
error->all(FLERR, "apip_atom_weight: time_complex_atom needs to be positive instead of {}",
time_complex_atom);
}
if (time_group_extract_name) {
if (force->pair->extract(time_group_extract_name, useless_dim) == nullptr)
error->all(FLERR, "apip_atom_weight: group time cannot be extracted with {} from {}",
time_group_extract_name, force->pair_style);
} else {
if (time_group_atom <= 0)
error->all(FLERR, "apip_atom_weight: time_group_atom needs to be positive instead of {}",
time_group_atom);
}
if (time_lambda_extract_name) {
if (force->pair->extract(time_lambda_extract_name, useless_dim) == nullptr)
error->all(FLERR, "apip_atom_weight: lambda time cannot be extracted with {} from {}",
time_lambda_extract_name, force->pair_style);
} else {
if (time_lambda_atom <= 0)
error->all(FLERR, "apip_atom_weight: time_lambda_atom needs to be positive instead of {}",
time_lambda_atom);
}
if (comm->me == 0) {
utils::logmesg(lmp, "atomic load lambda:\n");
if (time_simple_extract_name)
utils::logmesg(lmp, "\tfast potential: extract {}\n", time_simple_extract_name);
else
utils::logmesg(lmp, "\tfast potential: const {}\n", time_simple_atom);
if (time_complex_extract_name)
utils::logmesg(lmp, "\tprecise potential: extract {}\n", time_complex_extract_name);
else
utils::logmesg(lmp, "\tprecise potential: const {}\n", time_complex_atom);
if (time_group_extract_name)
utils::logmesg(lmp, "\tlambda_input: extract {}\n", time_group_extract_name);
else
utils::logmesg(lmp, "\tlambda_input: const {}\n", time_group_atom);
if (time_lambda_extract_name)
utils::logmesg(lmp, "\tlambda: extract {}\n", time_lambda_extract_name);
else
utils::logmesg(lmp, "\tlambda: const {}\n", time_lambda_atom);
}
global_freq = nevery;
peratom_freq = nevery;
}
/**
* Deconstructor. Delete allocated memory.
*/
FixApipAtomWeight::~FixApipAtomWeight()
{
delete ap_timer;
delete[] time_simple_extract_name;
delete[] time_complex_extract_name;
delete[] time_lambda_extract_name;
delete[] time_group_extract_name;
delete[] time_group_name;
// check nfix in case all fixes have already been deleted
if (fixstore && modify->nfix) modify->delete_fix(fixstore->id);
fixstore = nullptr;
}
/**
* allocate per-particle weight storage via FixStoreAtom
* fix could already be allocated if fix balance is re-specified
*/
void FixApipAtomWeight::post_constructor()
{
std::string cmd;
cmd = id;
cmd += "LAMBDA_WEIGHT";
fixstore = dynamic_cast<FixStoreAtom *>(modify->get_fix_by_id(cmd));
if (!fixstore)
fixstore = dynamic_cast<FixStoreAtom *>(modify->add_fix(cmd + " all STORE/ATOM 1 0 0 1"));
// do not carry weights with atoms during normal atom migration
fixstore->disable = 1;
vector_atom = fixstore->vstore;
}
/**
* For lammps.
* @return mask
*/
int FixApipAtomWeight::setmask()
{
int mask = 0;
mask |= PRE_EXCHANGE;
mask |= PRE_FORCE; // for setup_pre_force only
mask |= END_OF_STEP;
return mask;
}
/**
* Initialise calculated variables and setup timer objects.
*/
void FixApipAtomWeight::init()
{
int counter = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "apip_atom_weight") == 0) counter++;
}
if (counter > 1) error->all(FLERR, "More than one apip_atom_weight fix");
// get ptr to fix lambda
counter = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "lambda") == 0) {
fix_lambda = modify->fix[i];
counter++;
}
}
if (counter > 1) error->all(FLERR, "More than one fix lambda");
// This fix is evaluated in pre_exchange, but needs to be evaluated before load-balancing fixes.
for (auto ifix : modify->get_fix_list()) {
if (strcmp(id, ifix->id) == 0) {
// The remaining fixes are called after fix atom_load_lambda and ,thus, are not of interest.
break;
} else if (ifix->box_change == BOX_CHANGE_DOMAIN) {
error->all(FLERR, "apip_atom_weight: fix {} should come after fix {}", ifix->id, id);
}
}
// check that group for time_group has not been deleted
if (time_group_name) {
time_group_i = group->find(time_group_name);
if (time_group_i == -1)
error->all(FLERR, "apip_atom_weight: group {} does not exist", time_group_name);
time_group_bit = group->bitmask[time_group_i];
}
ap_timer->init();
last_calc = -1;
}
/**
* Set 1 as initial weight as no forces have been calculated yet.
*/
void FixApipAtomWeight::setup_pre_exchange()
{
// fix balance rebalances in setup_pre_exchange.
// setup_pre_exchange is called prior to force-calculations.
// Thus, there are no measured times yet.
// Fix balance with weight time 1.0 uses 1.0 as weight for each atom.
int i, nlocal;
double *weight;
nlocal = atom->nlocal;
weight = fixstore->vstore;
for (i = 0; i < nlocal; i++) weight[i] = 1;
// store pointer to weight for lammps
vector_atom = fixstore->vstore;
// fix balance can move particles and the weight information is required
// at the end of the step for the dump output.
// Thus, weights need to migrate with atoms.
fixstore->disable = 0;
}
/**
* Initial atom migration is done.
* The atoms do not need to migrate with atoms any more.
*/
void FixApipAtomWeight::setup_pre_force(int /*vflag*/)
{
fixstore->disable = 1;
// Atoms are with their weights now.
// -> update vector_atom
vector_atom = fixstore->vstore;
}
/**
* Compute weight of particles for load balancing.
*/
void FixApipAtomWeight::pre_exchange()
{
if (update->ntimestep % peratom_freq != 0) { return; }
calc_work_per_particle();
}
/**
* Update output pointer for output.
*/
void FixApipAtomWeight::end_of_step()
{
if (update->ntimestep % peratom_freq != 0) { return; }
// The work is not calculated twice.
// Call a second time as pre_exchange is not called when there is no exchange.
calc_work_per_particle();
// weights should not migrate with atoms
fixstore->disable = 1;
vector_atom = fixstore->vstore;
}
/**
* Calculate the work for a simple/complex atom.
* Times and number of atoms are extracted from pair styles.
* @note updates particle number and time variables of this class.
*/
void FixApipAtomWeight::calc_work_per_particle()
{
// calculating twice would destroy time measurements
if (update->ntimestep == last_calc) { return; }
last_calc = update->ntimestep;
char *extract_name[4] = {time_simple_extract_name, time_complex_extract_name,
time_group_extract_name, time_lambda_extract_name};
double *time_pa_ptr[4] = {&time_simple_atom, &time_complex_atom, &time_group_atom,
&time_lambda_atom};
double buffer[8];
int useless_dim = -1;
// extract times per atom from pair styles if required
int counter = 0;
for (int i = 0; i < 4; i++) {
if (extract_name[i]) {
// get time per atom
*(time_pa_ptr[i]) = *((double *) force->pair->extract(extract_name[i], useless_dim));
// save time per atom to buffer
if (*(time_pa_ptr[i]) < 0) {
// no calculations
buffer[counter] = buffer[counter + 1] = 0;
} else {
// save time per calculation
buffer[counter] = *(time_pa_ptr[i]);
buffer[counter + 1] = 1;
}
counter += 2;
}
}
if (counter) {
// do not use averaged values for all processors since they depend on the number of neighbours
// (which can vary, e.g. at a surface
// -> only use global value if there is no local one
MPI_Allreduce(MPI_IN_PLACE, buffer, counter, MPI_DOUBLE, MPI_SUM, world);
for (int i = 3; i >= 0; i--) {
if (extract_name[i]) {
// calculate average over all processors
avg_time_atom[i] = buffer[counter - 1] > 0 ? buffer[counter - 2] / buffer[counter - 1] : 0;
// use average if there is no local value
if (*(time_pa_ptr[i]) < 0) *(time_pa_ptr[i]) = avg_time_atom[i];
counter -= 2;
}
}
}
//set weight for each particle
double work_atom, *weight, **lambda_input_history;
int dim, *mask, *lambda_required;
weight = fixstore->vstore;
mask = atom->mask;
lambda_required = atom->lambda_required;
// get lambda(time averaged lambda input)
lambda_input_history = (double **) fix_lambda->extract("fix_lambda:lambda_input_history", dim);
const int histlen_lambda_input =
*((int *) fix_lambda->extract("fix_lambda:lambda_input_history_len", dim));
if (lambda_input_history == nullptr || histlen_lambda_input < 1)
error->all(FLERR, "apip_atom_weight: extracting fix_lambda:lambda_input_history failed");
int nlocal = atom->nlocal;
// assume a homogeneous time per simple and complex particle
n_simple = n_complex = n_group = n_lambda = 0;
for (int i = 0; i < nlocal; i++) {
work_atom = 0;
if (lambda_required[i] & LambdaRequired::SIMPLE) {
work_atom += time_simple_atom;
n_simple++;
}
if (lambda_required[i] & LambdaRequired::COMPLEX) {
work_atom += time_complex_atom;
n_complex++;
}
if (mask[i] & time_group_bit) {
work_atom += time_group_atom;
n_group++;
}
if (lambda_input_history[i][histlen_lambda_input + 1] != 1) {
work_atom += time_lambda_atom;
n_lambda++;
}
weight[i] = work_atom;
}
if (rescale_work) {
// calculate sum of vector work
double work_atoms = 0;
for (int i = 0; i < nlocal; i++) work_atoms += weight[i];
// calculate rescale factor
double rescale_factor = ap_timer->get_work() / work_atoms;
// apply rescale factor
for (int i = 0; i < nlocal; i++) weight[i] *= rescale_factor;
}
// store pointer to weight for lammps
vector_atom = fixstore->vstore;
// weights need to migrate with atoms
fixstore->disable = 0;
}
/**
* Provide average compute times for the output.
* 1st: time per simple atom
* 2nd: time per complex atom
* 3rd: time per lambda_input calculation
* 4th: time per lambda calculation
*/
double FixApipAtomWeight::compute_vector(int i)
{
if (i < size_vector) return avg_time_atom[i];
return 0;
}
/**
* Set everything to zero.
* @param[in] lmp lammps to get the Pointers class
*/
APIPtimer::APIPtimer(LAMMPS *lmp) : Pointers(lmp)
{
for (int i = 0; i < 4; i++) {
time[i] = 0;
time_interval[i] = 0;
}
}
/**
* Reset times to zero.
*/
void APIPtimer::init()
{
// do not use set_time();
// lammps resets the timers to zero, but the timers are not zero at timer initialisation time
for (int i = 0; i < 4; i++) {
time[i] = 0;
time_interval[i] = 0;
}
}
/**
* Save values of lammps timers.
* @note sets time
*/
void APIPtimer::set_time()
{
time[0] = timer->get_wall(Timer::PAIR);
time[1] = timer->get_wall(Timer::NEIGH);
time[2] = timer->get_wall(Timer::BOND);
time[3] = timer->get_wall(Timer::KSPACE);
}
/**
* Get time work since last call.
* @note sets time and time_interval
* @return work
*/
double APIPtimer::get_work()
{
double work = 0;
// store old times
for (int i = 0; i < 4; i++) time_interval[i] = -time[i];
set_time();
// calculate differences to new times
for (int i = 0; i < 4; i++) {
time_interval[i] += time[i];
work += time_interval[i];
}
// add constant time to prevent balancing with numerically zero
// e.g. (for few atoms per processor and balancing every step)
return work + 0.1;
}

View File

@ -0,0 +1,106 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef FIX_CLASS
// clang-format off
FixStyle(apip_atom_weight,FixApipAtomWeight);
// clang-format on
#else
#ifndef LMP_FIX_APIP_ATOM_WEIGHT_H
#define LMP_FIX_APIP_ATOM_WEIGHT_H
#include "fix.h"
#include "pointers.h"
namespace LAMMPS_NS {
/**
* Small wrapper for the lammps timers to get time intervals.
*/
class APIPtimer : protected Pointers {
public:
APIPtimer(class LAMMPS *);
void init();
double get_work();
private:
double time[4]; ///< value of lammps timers
double time_interval[4]; ///< time interval between two calls
void set_time();
};
/**
* Fix to compute an atomic wegiht that can be used to load-balance an adaptive-precision potential.
*/
class FixApipAtomWeight : public Fix {
public:
FixApipAtomWeight(class LAMMPS *, int, char **);
~FixApipAtomWeight() override;
int setmask() override;
void post_constructor() override;
void init() override;
void end_of_step() override;
void pre_exchange() override;
void setup_pre_exchange() override;
void setup_pre_force(int) override;
double compute_vector(int) override;
private:
bigint last_calc; ///< last timestep in which weights were calculated
// user set variables
// clang-format off
bool rescale_work; ///< rescale total work of all atoms according to our model to the measured total work
char * time_simple_extract_name; ///< argument for pair->extract to get the simple per_atom_time
char * time_complex_extract_name; ///< argument for pair->extract to get the complex per_atom_time
char * time_group_extract_name; ///< argument for pair->extract to get the time independent of lambda
char * time_lambda_extract_name; ///< argument for pair->extract to get the time for lambda max calculation
char * time_group_name; ///< name of group corresponding to time_group_extract_name
int time_group_i; ///< id of group corresponding to time_group_extract_name
int time_group_bit; ///< groupbit corresponding to time_group_extract_name
// clang-format on
// calculated variables
double time_simple_atom; ///< time per particle of simple pair style
double time_complex_atom; ///< time per particle of precise pair style
double time_group_atom; ///< time per particle independent of lambda
double time_lambda_atom; ///< time per atom with non-simple lambda_own
int n_simple; ///< number of simple particles before load balancing z
int n_complex; ///< number of complex particles before load balancing z
int n_group; ///< number of group particles before load balancing z
int n_lambda; ///< numebr of own_complex particles before load balancing z
double avg_time_atom[4]; ///< global average of per atom times per type
// class pointers
class FixStoreAtom *fixstore; ///< per-atom weights are stored in FixStore
class APIPtimer *ap_timer; ///< wrapper for lammps timers
class Fix *fix_lambda; ///< ptr to fix_lambda to extract information
void calc_work_per_particle();
};
} // namespace LAMMPS_NS
#endif
#endif

782
src/APIP/fix_lambda.cpp Normal file
View File

@ -0,0 +1,782 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "fix_lambda.h"
#include "atom.h"
#include "citeme.h"
#include "comm.h"
#include "error.h"
#include "fix_store_atom.h"
#include "force.h"
#include "group.h"
#include "memory.h"
#include "modify.h"
#include "pair.h"
#include "update.h"
using namespace LAMMPS_NS;
using namespace FixConst;
static const char cite_fix_lambda_c[] =
"fix lambda command: doi.org/10.1063/5.0245877\n\n"
"@Article{Immel25,\n"
" author = {Immel, David and Drautz, Ralf and Sutmann, Godehard},\n"
" title = {Adaptive-precision potentials for large-scale atomistic simulations},\n"
" journal = {The Journal of Chemical Physics},\n"
" volume = {162},\n"
" number = {11},\n"
" pages = {114119},\n"
" year = {2025}\n"
"}\n\n";
/* ---------------------------------------------------------------------- */
FixLambda::FixLambda(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg), peratom_stats(nullptr), group_name_simple(nullptr),
group_name_complex(nullptr), group_name_ignore_lambda_input(nullptr), fixstore(nullptr),
fixstore2(nullptr), pair_lambda_input(nullptr), pair_lambda_zone(nullptr)
{
if (lmp->citeme) lmp->citeme->add(cite_fix_lambda_c);
// set defaults
threshold_lo = threshold_hi = threshold_width = -1;
cut_lo = 4.0;
cut_hi = 12.0;
lambda_non_group = 1; // simple
history_last = history2_last = -1;
history_length = history2_length = 100;
history_used = history2_used = 0;
min_delta_lambda = 0;
group_bit_simple = group_bit_complex = group_bit_ignore_lambda_input = 0;
// output
peratom_flag = 0;
peratom_freq = -1; // default from fix.cpp
dump_history_flag = false;
size_peratom_cols = 5;
invoked_history_update = invoked_history2_update = -1;
if (narg < 4) error->all(FLERR, "fix lambda requires two arguments");
threshold_lo = utils::numeric(FLERR, arg[3], false, lmp);
threshold_hi = utils::numeric(FLERR, arg[4], false, lmp);
threshold_width = threshold_hi - threshold_lo;
// parse remaining arguments
for (int iarg = 5; iarg < narg; iarg++) {
if (strcmp(arg[iarg], "time_averaged_zone") == 0) {
if (iarg + 4 >= narg)
error->all(FLERR, "fix lambda: time_averaged_zone requires four arguments");
cut_lo = utils::numeric(FLERR, arg[iarg + 1], false, lmp);
cut_hi = utils::numeric(FLERR, arg[iarg + 2], false, lmp);
history_length = utils::inumeric(FLERR, arg[iarg + 3], false, lmp);
history2_length = utils::inumeric(FLERR, arg[iarg + 4], false, lmp);
iarg += 4;
} else if (strcmp(arg[iarg], "min_delta_lambda") == 0) {
if (iarg + 1 >= narg) error->all(FLERR, "fix lambda: min_delta_lambda requires one argument");
min_delta_lambda = utils::numeric(FLERR, arg[iarg + 1], false, lmp);
iarg += 1;
} else if (strcmp(arg[iarg], "lambda_non_group") == 0) {
if (iarg + 1 >= narg) error->all(FLERR, "fix lambda: lambda_non_group requires an argument");
if (strcmp(arg[iarg + 1], "precise") == 0) {
lambda_non_group = 0;
} else if (strcmp(arg[iarg + 1], "fast") == 0) {
lambda_non_group = 1;
} else {
lambda_non_group = utils::numeric(FLERR, arg[iarg + 1], false, lmp);
if (lambda_non_group < 0 || lambda_non_group > 1)
error->all(FLERR, "fix lambda: Illegal value of lambda_non_group");
}
iarg++;
} else if (strcmp(arg[iarg], "store_atomic_stats") == 0) {
peratom_flag = 1;
} else if (strcmp(arg[iarg], "dump_atomic_history") == 0) {
peratom_flag = 1;
dump_history_flag = true;
} else if (strcmp(arg[iarg], "group_fast") == 0) {
// read name of group
group_name_simple = utils::strdup(arg[iarg + 1]);
int tmp = group->find(group_name_simple);
if (tmp == -1) error->all(FLERR, "fix lambda: group {} does not exist", group_name_simple);
group_bit_simple = group->bitmask[tmp];
iarg++;
} else if (strcmp(arg[iarg], "group_precise") == 0) {
// read name of group
group_name_complex = utils::strdup(arg[iarg + 1]);
int tmp = group->find(group_name_complex);
if (tmp == -1) error->all(FLERR, "fix lambda: group {} does not exist", group_name_complex);
group_bit_complex = group->bitmask[tmp];
iarg++;
} else if (strcmp(arg[iarg], "group_ignore_lambda_input") == 0) {
// read name of group
group_name_ignore_lambda_input = utils::strdup(arg[iarg + 1]);
int tmp = group->find(group_name_ignore_lambda_input);
if (tmp == -1)
error->all(FLERR, "fix lambda: group {} does not exist", group_name_ignore_lambda_input);
group_bit_ignore_lambda_input = group->bitmask[tmp];
iarg++;
} else
error->all(FLERR, "fix lambda: unknown argument {}", arg[iarg]);
}
cut_hi_sq = cut_hi * cut_hi;
cut_width = cut_hi - cut_lo;
// verify arguments
if (threshold_lo > threshold_hi || threshold_lo < 0)
error->all(FLERR, "fix lambda: Illegal or missing threshold values");
if (min_delta_lambda < 0)
error->all(FLERR, "fix lambda: min_delta_lambda >= 0 required instead of {}", min_delta_lambda);
if (cut_lo < 0 || cut_hi < cut_lo) error->all(FLERR, "fix lambda: Illegal cutoff values");
if (history_length < 2 || history2_length < 2)
error->all(FLERR, "fix lambda: history_length > 1 required");
if (comm->me == 0 && ((group_bit_simple != 0) || (group_bit_complex != 0)) &&
group_bit_ignore_lambda_input == 0)
error->warning(FLERR,
"group_ignore_lambda_input should be used to prevent the calculation of "
"lambda_input for atoms that are in the groups group_fast and group_precise.");
if (!atom->lambda_const_flag) {
error->all(FLERR, "fix lambda requires atomic style with lambda_const.");
}
if (!atom->lambda_flag) { error->all(FLERR, "fix lambda requires atomic style with lambda."); }
if (!atom->lambda_input_flag) {
error->all(FLERR, "fix lambda requires atomic style with lambda_input.");
}
comm_forward = 2; // up to two doubles per atom
comm_forward_flag = FORWARD_TA;
restart_global = 1;
if (peratom_flag) {
if (dump_history_flag) size_peratom_cols += history_length + history2_length + 2;
peratom_freq = 1;
// zero the array since dump may access it on timestep 0
// zero the array since a variable may access it before first run
nmax_stats = atom->nmax;
memory->create(peratom_stats, nmax_stats, size_peratom_cols, "lambda:peratom_stats");
array_atom = peratom_stats;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++)
for (int j = 0; j < size_peratom_cols; j++) peratom_stats[i][j] = 0;
}
}
/* ----------------------------------------------------------------------
modify cutoff setings
------------------------------------------------------------------------- */
int FixLambda::modify_param(int narg, char **arg)
{
cut_lo = utils::numeric(FLERR, arg[0], false, lmp);
cut_hi = utils::numeric(FLERR, arg[1], false, lmp);
cut_hi_sq = cut_hi * cut_hi;
cut_width = cut_hi - cut_lo;
if (cut_lo < 0 || cut_hi < cut_lo) error->all(FLERR, "fix lambda: Illegal cutoff values");
if (force->pair->cutforce < cut_hi)
error->all(FLERR, "fix lambda: cutoff of potential smaller than cutoff of switching region");
return 2;
}
/* ---------------------------------------------------------------------- */
FixLambda::~FixLambda()
{
// check nfix in case all fixes have already been deleted
if (fixstore && modify->nfix) modify->delete_fix(fixstore->id);
if (fixstore2 && modify->nfix) modify->delete_fix(fixstore2->id);
fixstore = fixstore2 = nullptr;
memory->destroy(peratom_stats);
delete[] group_name_simple;
delete[] group_name_complex;
delete[] group_name_ignore_lambda_input;
}
/* ---------------------------------------------------------------------- */
int FixLambda::setmask()
{
int mask = 0;
mask |= POST_INTEGRATE;
mask |= PRE_FORCE; // for setup_pre_force only
if (peratom_flag) mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixLambda::init()
{
if (force->pair == nullptr) error->all(FLERR, "Fix lambda requires a pair style be defined");
// only one fix lambda
int count = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "lambda") == 0) count++;
}
if (count > 1) error->all(FLERR, "More than one fix lambda.");
// warn if there is no fix lambda_thermostat
if (comm->me == 0 && modify->get_fix_by_style("lambda_thermostat").size() == 0)
error->warning(
FLERR,
"The energy is not conserved when lambda changes as fix lambda_thermostat is not used.");
Pair *pair_tmp;
// lambda_input
pair_tmp = force->pair_match("lambda_input/", 0);
if (!pair_tmp) error->all(FLERR, "fix lambda requires a `pair lambda_input`");
pair_lambda_input = (PairLambdaInput *) pair_tmp;
// lambda/zone
pair_tmp = force->pair_match("lambda/zone", 1);
if (!pair_tmp) error->all(FLERR, "fix lambda requires a `pair lambda`");
pair_lambda_zone = (PairLambdaZone *) pair_tmp;
if (force->pair->cutforce < cut_hi)
error->all(FLERR, "fix lambda: cutoff of potential smaller than cutoff of switching region");
if (strcmp(atom->atom_style, "apip")) error->all(FLERR, "fix lambda requires atom style apip");
// check that groups have not been deleted
if (group_name_simple) {
int tmp = group->find(group_name_simple);
if (tmp == -1) error->all(FLERR, "fix lambda: group {} does not exist", group_name_simple);
group_bit_simple = group->bitmask[tmp];
}
if (group_name_complex) {
int tmp = group->find(group_name_complex);
if (tmp == -1) error->all(FLERR, "fix lambda: group {} does not exist", group_name_complex);
group_bit_complex = group->bitmask[tmp];
}
if (group_name_ignore_lambda_input) {
int tmp = group->find(group_name_ignore_lambda_input);
if (tmp == -1)
error->all(FLERR, "fix lambda: group {} does not exist", group_name_ignore_lambda_input);
group_bit_ignore_lambda_input = group->bitmask[tmp];
}
}
/**
* allocate per-particle storage for past cs values via FixStoreAtom
* fix could already be allocated if fix lambda is re-specified
*/
void FixLambda::post_constructor()
{
std::string cmd, cmd2;
cmd = id;
cmd2 = id;
cmd += "LAMBDA_INPUT_HISTORY";
cmd2 += "LAMBDA_HISTORY";
// delete existing fix store if existing
fixstore = dynamic_cast<FixStoreAtom *>(modify->get_fix_by_id(cmd));
fixstore2 = dynamic_cast<FixStoreAtom *>(modify->get_fix_by_id(cmd2));
// check nfix in case all fixes have already been deleted
if (fixstore && modify->nfix) modify->delete_fix(fixstore->id);
if (fixstore2 && modify->nfix) modify->delete_fix(fixstore2->id);
fixstore = nullptr;
// create new FixStoreAtom
// store history_length of last values and the sum over all values
char history_length_str[40], history2_length_str[40];
sprintf(history_length_str, "%d", history_length + 2); // lambda_input
sprintf(history2_length_str, "%d", history2_length + 1); // lambda
// arguments of peratom:
// first: 1 -> store in restart file
// second: number of doubles to store per atom
cmd += " all STORE/ATOM ";
cmd2 += " all STORE/ATOM ";
cmd += history_length_str; // n1
cmd2 += history2_length_str; // n1
cmd += " 0 0 1"; // n2 gflag rflag
cmd2 += " 0 0 1"; // n2 gflag rflag
fixstore = dynamic_cast<FixStoreAtom *>(modify->add_fix(cmd));
fixstore2 = dynamic_cast<FixStoreAtom *>(modify->add_fix(cmd2));
// carry weights with atoms during normal atom migration
fixstore->disable = 0;
fixstore2->disable = 0;
}
/**
* Calculate lambda for initial atoms if required.
* If required, this includes communication.
*/
void FixLambda::setup_pre_force(int /*vflag*/)
{
// lambda, lambda_input, lambda_input_ta and lambda_const are written to restart files.
// Calculate lambda_input in pair style.
pair_lambda_input->calculate_lambda_input();
// Update lambda_input_history with lambda_input.
update_lambda_input_history();
// calculate and communicate lambda_input_ta to neighbours
communicate_lambda_input_ta();
// calculate lambda max with lambda_input_ta of own and ngh atoms
pair_lambda_zone->calculate_lambda();
// update lambda_history with calculated lambda_input_ta, set lambda, set lambda_input_ta to lambda(own ta lambda_input)
post_integrate();
// set initial lambda_const if required, communicate lambda and lambda_const to neighbours
comm_forward_lambda();
// pair_lambda_zone->calculate_lambda is again called as default setup force calculation
// -> communicate lambda_input_ta again to have an appropriate input for this "force" calculation
// increase invoked update to prevent double values
communicate_lambda_input_ta();
// just to be sure
write_peratom_stats();
}
/**
* The new lambda is stored for own atoms in lambda_input_ta since the last force calculation.
* Update lambda and lambda_const with the running average including the new lambda.
*/
void FixLambda::post_integrate()
{
double *lambda, *lambda_const, *lambda_input_ta;
lambda = atom->lambda;
lambda_const = atom->lambda_const;
lambda_input_ta = atom->lambda_input_ta;
update_lambda_history(); // update running average of lambda with lambda_input_ta
get_lambda_average(); // and copy running average to lambda_input_ta
// use lambda_input_ta to set lambda max
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
lambda_const[i] = lambda[i];
lambda[i] = lambda_input_ta[i];
// prevent useless fluctuations in the switching zone just due to atomic fluctuations
// the values 0 and 1 are always permitted to prevent atoms from keeping a lambda of epsilon forever
if (fabs(lambda[i] - lambda_const[i]) < min_delta_lambda && lambda[i] != 1 && lambda[i] != 0)
lambda[i] = lambda_const[i];
}
// set lambda_input_ta to own lambda for new lambda calculation with pair_lambda_zone.cpp
calculate_lambda_input_ta();
}
/**
* write stats at end of step
*/
void FixLambda::end_of_step()
{
write_peratom_stats();
}
/**
* The new calculated lambda is stored in lambda.
* Exchange lambda and lambda_const with neighbours.
* Use only in setup_pre_force.
*/
void FixLambda::comm_forward_lambda()
{
if (history2_used == 1) {
// There is only one one calculated lambda.
// -> This is the first lambda calculation.
// -> set lambda_const to lambda since there is no previous lambda
double *lambda, *lambda_const;
int nlocal;
lambda = atom->lambda;
lambda_const = atom->lambda_const;
nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) { lambda_const[i] = lambda[i]; }
}
comm_forward_flag = FORWARD_MAX;
comm->forward_comm(this);
}
/**
* write per atom stats
*/
void FixLambda::write_peratom_stats()
{
if (!peratom_flag) return;
int i, j, nlocal;
double **lambda_input_history, **lambda_history;
nlocal = atom->nlocal;
lambda_input_history = fixstore->astore;
lambda_history = fixstore2->astore;
// grow stats array if required
if (atom->nmax > nmax_stats) {
memory->destroy(peratom_stats);
nmax_stats = atom->nmax;
memory->create(peratom_stats, nmax_stats, size_peratom_cols, "lambda:peratom_stats");
array_atom = peratom_stats;
}
for (i = 0; i < nlocal; i++) {
peratom_stats[i][0] = lambda_input_history[i][history_last]; // lambda_input now
peratom_stats[i][1] =
lambda_input_history[i][history_length] / history_used; // lambda_input averaged
peratom_stats[i][2] =
lambda_input_history[i][history_length + 1]; // lambda of own ta lambda_input
peratom_stats[i][3] = lambda_history[i][history2_last]; // lambda max now
peratom_stats[i][4] =
lambda_history[i][history2_length] / history2_used; // lambda max averaged
}
if (dump_history_flag) {
for (i = 0; i < nlocal; i++) {
// include lambda_input sum -> <= history_length
for (j = 0; j <= history_length; j++) {
peratom_stats[i][j + 5] = lambda_input_history[i][j]; // lambda_input history
}
for (j = 0; j <= history2_length; j++) {
peratom_stats[i][j + 6 + history_length] = lambda_history[i][j]; // lambda_input history
}
}
}
}
/**
* Update running average of lambda_input.
*/
void FixLambda::update_lambda_input_history()
{
if (invoked_history_update == update->ntimestep) return;
invoked_history_update = update->ntimestep;
double *lambda_input, **lambda_input_history;
int *mask;
int nlocal;
lambda_input = atom->lambda_input;
mask = atom->mask;
lambda_input_history = fixstore->astore;
nlocal = atom->nlocal;
// update stats about written values
history_last = (history_last + 1) % history_length;
history_used = std::min(history_used + 1, history_length);
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) {
lambda_input_history[i][history_length + 1] = lambda_non_group;
continue;
}
// lambda_input_history[particle_number][history_number]
// history_number:
// 0 - history_length - 1 : single lambda_input of past time step
// history_length : sum of all stored past lambda_input values
// history_length + 1 : lambda(time averaged lambda_input)
// subtract the lambda_input to be overwritten from sum
lambda_input_history[i][history_length] -= lambda_input_history[i][history_last];
// store new lambda_input value
lambda_input_history[i][history_last] = lambda_input[i];
// add the new lambda_input to sum
lambda_input_history[i][history_length] += lambda_input_history[i][history_last];
// calculate lambda of new time average
if (group_name_complex && (mask[i] & group_bit_complex)) {
// hard code complex with highest priority
lambda_input_history[i][history_length + 1] = 0;
} else if (group_name_simple && (mask[i] & group_bit_simple)) {
// hard code simple with second highest priority
lambda_input_history[i][history_length + 1] = 1;
} else if ((mask[i] & groupbit) &&
!(group_name_ignore_lambda_input && (mask[i] & group_bit_ignore_lambda_input))) {
// calculate lambda based on lambda_input
lambda_input_history[i][history_length + 1] =
switching_function_poly(lambda_input_history[i][history_length] / history_used);
} else {
lambda_input_history[i][history_length + 1] = lambda_non_group;
}
}
}
/**
* Update running average of lambda with lambda_input_ta.
*/
void FixLambda::update_lambda_history()
{
if (invoked_history2_update == update->ntimestep) return;
invoked_history2_update = update->ntimestep;
double *lambda_input_ta, **lambda_history, **lambda_input_history;
int *mask;
int nlocal;
lambda_input_ta = atom->lambda_input_ta;
mask = atom->mask;
lambda_history = fixstore2->astore;
lambda_input_history = fixstore->astore;
nlocal = atom->nlocal;
// update stats about written values
history2_last = (history2_last + 1) % history2_length;
history2_used = std::min(history2_used + 1, history2_length);
int tmp = 0;
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) continue;
// lambda_history[particle_number][history2_number]
// history2_number:
// 0 - history2_length - 1 : lambda of past time step
// history2_length : sum of all stored past lambda values
// subtract the lambda to be overwritten from sum
lambda_history[i][history2_length] -= lambda_history[i][history2_last];
// store new lambda_input value
lambda_history[i][history2_last] = lambda_input_ta[i];
// add the new lambda_input to sum
lambda_history[i][history2_length] += lambda_history[i][history2_last];
}
}
/**
* Copy running average of lambda to lambda_input_ta.
*/
void FixLambda::get_lambda_average()
{
double *lambda_input_ta, **lambda_history, avg;
int *mask, nlocal;
mask = atom->mask;
nlocal = atom->nlocal;
lambda_history = fixstore2->astore;
lambda_input_ta = atom->lambda_input_ta;
// recalculate history sum to limit floating point issues since only changes of the sum are tracked
if (history2_last == history2_length - 1) {
double sum;
for (int i = 0; i < nlocal; i++) {
sum = 0;
for (int j = 0; j < history2_length; j++) sum += lambda_history[i][j];
lambda_history[i][history2_length] = sum;
}
}
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) {
// avg is a division and not exactly 1 or 0
// exact values are required otherwise many useless complex calculations follow since avg = 1 - epsilon < 1
// -> hard code zero and one if required
avg = lambda_history[i][history2_length] / history2_used;
if (avg > 0.9999)
lambda_input_ta[i] = 1; // simple
else if (avg < 0.0001)
lambda_input_ta[i] = 0; // complex
else
lambda_input_ta[i] = avg; // switching
}
}
}
/**
* calculate lambda_input_ta for own atoms
*/
void FixLambda::calculate_lambda_input_ta()
{
int i, nlocal;
int *mask;
double *lambda_input_ta = atom->lambda_input_ta;
nlocal = atom->nlocal;
mask = atom->mask;
// store time averaged lambda_input for own atoms
double **lambda_input_history = fixstore->astore;
for (i = 0; i < nlocal; i++) { lambda_input_ta[i] = lambda_input_history[i][history_length + 1]; }
}
/* ----------------------------------------------------------------------
Compute temporary lambda for owned atoms based on lambda_input_history of owned atoms.
Save this temporary lambda as lambda_input_ta and send it to neighbours.
------------------------------------------------------------------------- */
void FixLambda::communicate_lambda_input_ta()
{
calculate_lambda_input_ta();
// lambda_input_ta is known only for own atoms
// -> exchange lambda_input_ta
comm_forward_flag = FORWARD_TA;
comm->forward_comm(this);
}
// helper function
// similar to cutoff_func_poly in ace_radial.cpp
// compare Phys Rev Mat 6, 013804 (2022) APPENDIX C: RADIAL AND CUTOFF FUNCTIONS 2. Cutoff function
// the first two derivatives of the switching function lambda vanishes at the boundaries of the switching region
double FixLambda::switching_function_poly(double input)
{
// calculate lambda
if (input <= threshold_lo) {
return 1;
} else if (input >= threshold_hi) {
return 0;
} else {
double deltatmp = 1 - 2 * (1 + (input - threshold_hi) / (threshold_width));
return 0.5 + 7.5 / 2. * (deltatmp / 4. - pow(deltatmp, 3) / 6. + pow(deltatmp, 5) / 20.);
}
}
/**
* Send lambda to neighbours.
*/
int FixLambda::pack_forward_comm(int n, int *list, double *buf, int /*pbc_flag*/, int * /*pbc*/)
{
int i, j, m;
double *lambda_input_ta = atom->lambda_input_ta;
m = 0;
if (comm_forward_flag == FORWARD_TA) {
for (i = 0; i < n; i++) {
j = list[i];
buf[m++] = lambda_input_ta[j];
}
} else if (comm_forward_flag == FORWARD_MAX) {
for (i = 0; i < n; i++) {
j = list[i];
buf[m++] = atom->lambda[j];
buf[m++] = atom->lambda_const[j];
}
}
return m;
}
/**
* Recv lambda from neighbours.
*/
void FixLambda::unpack_forward_comm(int n, int first, double *buf)
{
int i, m, last;
double *lambda_input_ta = atom->lambda_input_ta;
m = 0;
last = first + n;
if (comm_forward_flag == FORWARD_TA) {
for (i = first; i < last; i++) { lambda_input_ta[i] = buf[m++]; }
} else if (comm_forward_flag == FORWARD_MAX) {
for (i = first; i < last; i++) {
atom->lambda[i] = buf[m++];
atom->lambda_const[i] = buf[m++];
}
}
}
/**
* store scalar history information
*/
void FixLambda::write_restart(FILE *fp)
{
int timesteps_since_invoked_history_update = update->ntimestep - invoked_history_update;
int timesteps_since_invoked_history2_update = update->ntimestep - invoked_history2_update;
int n = 0;
double list[8];
list[n++] = history_length;
list[n++] = history_used;
list[n++] = history_last;
list[n++] = timesteps_since_invoked_history_update;
list[n++] = history2_length;
list[n++] = history2_used;
list[n++] = history2_last;
list[n++] = timesteps_since_invoked_history2_update;
if (comm->me == 0) {
int size = n * sizeof(double);
fwrite(&size, sizeof(int), 1, fp);
fwrite(list, sizeof(double), n, fp);
}
}
/* ----------------------------------------------------------------------
use state info from restart file to restart the Fix
------------------------------------------------------------------------- */
void FixLambda::restart(char *buf)
{
int timesteps_since_invoked_history_update, history_length_br,
timesteps_since_invoked_history2_update, history2_length_br;
bigint next_file_write_calculated;
int n = 0;
auto list = (double *) buf;
history_length_br = static_cast<int>(list[n++]);
history_used = static_cast<int>(list[n++]);
history_last = static_cast<int>(list[n++]);
invoked_history_update = update->ntimestep - (static_cast<int>(list[n++]));
history2_length_br = static_cast<int>(list[n++]);
history2_used = static_cast<int>(list[n++]);
history2_last = static_cast<int>(list[n++]);
invoked_history2_update = update->ntimestep - (static_cast<int>(list[n++]));
// simple comparisons first
if (history_length != history_length_br)
error->all(FLERR, "fix lambda: history_length = {} != {} = history_length_before_restart",
history_length, history_length_br);
if (history2_length != history2_length_br)
error->all(FLERR, "fix lambda: history2_length = {} != {} = history2_length_before_restart",
history2_length, history2_length_br);
}
/**
* extract lambda(time averaged lambda_input) and lambda_input_history_len
*/
void *FixLambda::extract(const char *str, int &dim)
{
dim = 2;
if (strcmp(str, "fix_lambda:lambda_input_history") == 0 && fixstore) { return fixstore->astore; }
dim = 0;
if (strcmp(str, "fix_lambda:lambda_input_history_len") == 0) { return &history_length; }
if (strcmp(str, "fix_lambda:cut_lo") == 0) { return &cut_lo; }
if (strcmp(str, "fix_lambda:cut_hi") == 0) { return &cut_hi; }
if (strcmp(str, "fix_lambda:cut_hi_sq") == 0) { return &cut_hi_sq; }
if (strcmp(str, "fix_lambda:cut_width") == 0) { return &cut_width; }
if (strcmp(str, "fix_lambda:lambda_non_group") == 0) { return &lambda_non_group; }
return nullptr;
}

108
src/APIP/fix_lambda.h Normal file
View File

@ -0,0 +1,108 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef FIX_CLASS
// clang-format off
FixStyle(lambda,FixLambda);
// clang-format on
#else
#ifndef LMP_FIX_LAMBDA_H
#define LMP_FIX_LAMBDA_H
#include "fix.h"
#include "pair_lambda_input.h"
#include "pair_lambda_zone.h"
namespace LAMMPS_NS {
class FixLambda : public Fix {
friend class PairLambdaInput;
friend class PairLambdaZone;
public:
FixLambda(class LAMMPS *, int, char **);
~FixLambda() override;
int modify_param(int, char **) override;
void init() override;
int setmask() override;
void post_constructor() override;
void setup_pre_force(int) override;
void post_integrate() override;
void end_of_step() override;
int pack_forward_comm(int, int *, double *, int, int *) override;
void unpack_forward_comm(int, int, double *) override;
void write_restart(FILE *) override;
void restart(char *) override;
void *extract(const char *, int &) override;
private:
enum { FORWARD_MAX, FORWARD_TA };
int comm_forward_flag; // flag that determines which variables are communicated in comm forward
class PairLambdaInput *pair_lambda_input;
class PairLambdaZone *pair_lambda_zone;
double cut_lo; ///< distance at which the cutoff function of the transition zone decays from 1
double cut_hi; ///< distance at which the cutoff function of the transition zone is 0
double cut_width; ///< cut_hi - cut_lo
double cut_hi_sq; ///< cut_hi_sq * cut_hi_sq
double threshold_lo; ///< threshold above which the fast potential starts to be turned off
double threshold_hi; ///< threshold above which the fast potential is turned off completely
double threshold_width; ///< threshold_hi - threshold_lo
double min_delta_lambda; ///< minimum steps size of lambda
double lambda_non_group; ///< lambda for atoms that are not in the group of this fix
int history_length; ///< number of steps of which lambda_input is sterd at max
int history_used; ///< number of steps of which lambda_input is currently stored
int history_last; ///< index of last written lambda_input value
class FixStoreAtom *fixstore; ///< ptr to stored lambda_input values
int history2_length; ///< number of steps of which lambda is stored at max
int history2_used; ///< number of steps of which lambda is currently stored
int history2_last; ///< index of last written lambda value
class FixStoreAtom *fixstore2; ///< ptr to stored lambda values
double switching_function_poly(double);
bigint invoked_history_update; ///< last timestep with stored history
bigint invoked_history2_update; ///< last timestep with stored history
bool dump_history_flag; ///< dump whole stored history (for debugging)
int group_bit_simple; ///< hard coded simple atoms
int group_bit_complex; ///< hard coded complex atoms
int group_bit_ignore_lambda_input; ///< ignore lambda_input of this group
char *group_name_simple; ///< hard coded simple atoms
char *group_name_complex; ///< hard coded complex atoms
char *group_name_ignore_lambda_input; ///< ignore lambda_input of this group
int nmax_stats; ///< number of allocated atoms for peratom_stats
double **peratom_stats; ///< returned peratom vector
void calculate_lambda_input_ta();
void comm_forward_lambda();
void write_peratom_stats();
void update_lambda_input_history();
void communicate_lambda_input_ta();
void get_lambda_average();
void update_lambda_history();
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,630 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "fix_lambda_thermostat.h"
#include "atom.h"
#include "comm.h"
#include "error.h"
#include "force.h"
#include "memory.h"
#include "modify.h"
#include "my_page.h"
#include "neigh_list.h"
#include "neighbor.h"
#include "random_park.h"
#include "update.h"
#include <algorithm>
using namespace LAMMPS_NS;
using namespace FixConst;
#define PGDELTA 1
/* ---------------------------------------------------------------------- */
FixLambdaThermostat::FixLambdaThermostat(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg), list(nullptr), energy_change_atom(nullptr), peratom_stats(nullptr),
local_numneigh(nullptr), local_firstneigh(nullptr), ipage(nullptr), jlist_copy(nullptr)
{
// set global options for fix class
vector_flag = 1;
size_vector = 6;
extvector = 0;
// set default vaues
dtf = 0;
update_stats = false;
int seed = 42;
rescaling_N_neighbours = 200;
// output
peratom_flag = 0;
peratom_freq = -1; // default from fix.cpp
size_peratom_cols = 4;
// parse arguments
for (int iarg = 3; iarg < narg; iarg++) {
if (strcmp(arg[iarg], "seed") == 0) {
if (iarg + 1 >= narg) error->all(FLERR, "fix lambda_thermostat: seed requires one argument");
seed = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
if (seed <= 0) { error->all(FLERR, "fix lambda_thermostat seed <= 0"); }
iarg++;
} else if (strcmp(arg[iarg], "store_atomic_forces") == 0) {
if (iarg + 1 >= narg)
error->all(FLERR, "fix lambda_thermostat: store_atomic_forces requires one argument");
peratom_flag = 1;
peratom_freq = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
if (peratom_freq < 1)
error->all(FLERR, "fix lambda_thermostat: frequency of store_atomic_forces < 1");
iarg++;
} else if (strcmp(arg[iarg], "N_rescaling") == 0) {
if (iarg + 1 >= narg)
error->all(FLERR, "fix lambda_thermostat: mode number requires one argument");
rescaling_N_neighbours = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
iarg += 1;
} else
error->all(FLERR, "fix lambda_thermostat: unknown argument {}", arg[iarg]);
}
// error checks
if (!atom->e_simple_flag) {
error->all(FLERR, "fix lambda_thermostat requires atomic style with e_simple.");
}
if (!atom->e_complex_flag) {
error->all(FLERR, "fix lambda_thermostat requires atomic style with e_complex.");
}
if (!atom->lambda_const_flag) {
error->all(FLERR, "fix lambda_thermostat requires atomic style with lambda_const.");
}
if (!atom->lambda_flag) {
error->all(FLERR, "fix lambda_thermostat requires atomic style with lambda.");
}
if (!atom->f_const_lambda_flag) {
error->all(FLERR, "fix lambda_thermostat requires atomic style with f_const_lambda.");
}
if (!atom->f_dyn_lambda_flag) {
error->all(FLERR, "fix lambda_thermostat requires atomic style with f_dyn_lambda.");
}
if (rescaling_N_neighbours <= 1)
error->all(FLERR, "fix lambda_thermostat: rescaling_N_neighbours <= 1");
// rng for shuffle
random_mt = std::mt19937(seed);
// init output values
energy_change_kin = energy_change_pot = 0;
nmax_energy = 0;
reduceflag = 0;
for (int i = 0; i < size_vector; i++) { outvec[i] = 0; }
sum_energy_change = sum_energy_violation = 0;
n_energy_violation = n_energy_differences = 0;
if (peratom_flag) {
// zero the array since dump may access it on timestep 0
// zero the array since a variable may access it before first run
nmax_stats = atom->nmax;
memory->create(peratom_stats, nmax_stats, size_peratom_cols, "lambda_thermostat:peratom_stats");
array_atom = peratom_stats;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++)
for (int j = 0; j < size_peratom_cols; j++) peratom_stats[i][j] = 0;
} else {
nmax_stats = 0;
}
nmax_list = 0;
pgsize = oneatom = 0;
}
/* ---------------------------------------------------------------------- */
FixLambdaThermostat::~FixLambdaThermostat()
{
memory->destroy(energy_change_atom);
memory->destroy(peratom_stats);
memory->destroy(local_numneigh);
memory->sfree(local_firstneigh);
memory->destroy(jlist_copy);
delete[] ipage;
}
/* ---------------------------------------------------------------------- */
int FixLambdaThermostat::setmask()
{
int mask = 0;
mask |= POST_FORCE;
mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostat::init()
{
dtf = 0.5 * update->dt * force->ftm2v;
// full neighbour list for thermostating
neighbor->add_request(this, NeighConst::REQ_FULL);
int counter = 0;
for (int i = 0; i < modify->nfix; i++)
if (strcmp(modify->fix[i]->style, "lambda_thermostat") == 0) counter++;
if (counter > 1) error->all(FLERR, "fix lambda_thermostat: more than one fix lambda_thermostat");
// local neighbor list
// create pages if first time or if neighbor pgsize/oneatom has changed
int create = 0;
if (ipage == nullptr) create = 1;
if (pgsize != neighbor->pgsize) create = 1;
if (oneatom != neighbor->oneatom) create = 1;
if (oneatom != neighbor->oneatom || ipage == nullptr) {
// allocate memory for copy of one ngh list
memory->destroy(jlist_copy);
memory->create(jlist_copy, neighbor->oneatom, "lambda_thermostat:jlist_copy");
}
if (create) {
delete[] ipage;
pgsize = neighbor->pgsize;
oneatom = neighbor->oneatom;
int nmypage = comm->nthreads;
ipage = new MyPage<int>[nmypage];
for (int i = 0; i < nmypage; i++) ipage[i].init(oneatom, pgsize, PGDELTA);
}
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostat::init_list(int /*id*/, NeighList *ptr)
{
list = ptr;
}
/* ----------------------------------------------------------------------
Create neighbor list from main neighbor list with local atoms only.
The velocity updates are dependent on the velocity.
Thus, one would need to communicate a velocity change of a ghost atom directly to all neighbouring processors.
This communication would kill the performance.
Thus, update only local particles.
------------------------------------------------------------------------- */
void FixLambdaThermostat::local_neighbour_list()
{
int i, j, ii, jj, n, inum, jnum, nlocal;
int *ilist, *jlist, *numneigh, **firstneigh;
int *neighptr;
int *mask = atom->mask;
if (atom->nmax > nmax_list) {
nmax_list = atom->nmax;
memory->destroy(local_numneigh);
memory->sfree(local_firstneigh);
memory->create(local_numneigh, nmax_list, "lambda_thermostat:numneigh");
local_firstneigh =
(int **) memory->smalloc(nmax_list * sizeof(int *), "lambda_thermostat:firstneigh");
}
nlocal = atom->nlocal;
inum = list->inum;
ilist = list->ilist;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
// store all local neighbours of local atoms
// scan full neighbor list of I
ipage->reset();
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
n = 0;
neighptr = ipage->vget();
jlist = firstneigh[i];
jnum = numneigh[i];
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
if (j < nlocal && mask[j] & groupbit) neighptr[n++] = j;
}
local_firstneigh[i] = neighptr;
local_numneigh[i] = n;
ipage->vgot(n);
if (ipage->status()) error->one(FLERR, "Neighbor list overflow, boost neigh_modify one");
}
}
/* ---------------------------------------------------------------------- */
double FixLambdaThermostat::calculate_kinetic_energy(int i)
{
double *v = atom->v[i];
double m = (atom->rmass ? atom->rmass[i] : atom->mass[atom->type[i]]);
return 0.5 * force->mvv2e * m * (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostat::post_force(int /*vflag*/)
{
init_peratom_stats();
calculate_energy_change();
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostat::end_of_step()
{
apply_thermostat();
}
/**
* Init per-atom array with zeros.
*/
void FixLambdaThermostat::init_peratom_stats()
{
if ((!peratom_flag) || (update->ntimestep % peratom_freq != 0)) {
update_stats = false;
return;
}
update_stats = true;
int nlocal;
nlocal = atom->nlocal;
// grow stats array if required
if (atom->nmax > nmax_stats) {
memory->destroy(peratom_stats);
nmax_stats = atom->nmax;
memory->create(peratom_stats, nmax_stats, size_peratom_cols, "lambda_thermostat:peratom_stats");
array_atom = peratom_stats;
}
for (int i = 0; i < nlocal; i++)
peratom_stats[i][0] = peratom_stats[i][1] = peratom_stats[i][2] = peratom_stats[i][3] = 0;
}
/**
* Calculate the energy difference of atoms that needs to be corrected
* by the local thermostat.
*/
void FixLambdaThermostat::calculate_energy_change()
{
double **v = atom->v;
double *rmass = atom->rmass;
double *mass = atom->mass;
int *type = atom->type;
int *mask = atom->mask;
int nlocal = atom->nlocal;
if (igroup == atom->firstgroup) nlocal = atom->nfirst;
double *e_simple = atom->e_simple;
double *e_complex = atom->e_complex;
double *lambda_const = atom->lambda_const;
double *lambda = atom->lambda;
double **f_const_lambda = atom->f_const_lambda;
double **f_dyn_lambda = atom->f_dyn_lambda;
double masstmp, dtfm, changetmp;
// allocate memory for energy change
if (atom->nmax > nmax_energy) {
memory->destroy(energy_change_atom);
nmax_energy = atom->nmax;
memory->create(energy_change_atom, nmax_energy, "lambda_thermostat:energy_change_atom");
}
// reset calculated changes
energy_change_pot = 0;
energy_change_kin = 0;
for (int i = 0; i < nlocal; i++) { energy_change_atom[i] = 0; }
// calculate potential energy differences
for (int i = 0; i < nlocal; i++) {
if ((!(mask[i] & groupbit)) || lambda_const[i] == lambda[i]) { continue; }
if (e_simple[i] == 0 || e_complex[i] == 0)
error->one(FLERR, "lambda = {} != {} = lambda_const and e_simple = {} and e_complex = {}",
lambda[i], lambda_const[i], e_simple[i], e_complex[i]);
changetmp =
(lambda_const[i] - lambda[i]) * e_simple[i] + (lambda[i] - lambda_const[i]) * e_complex[i];
energy_change_atom[i] += changetmp;
energy_change_pot += changetmp;
}
// calculate kinetic energy difference
// consider all local atoms
for (int i = 0; i < nlocal; i++) {
if (!(mask[i] & groupbit)) { continue; }
masstmp = (rmass ? rmass[i] : mass[type[i]]);
dtfm = dtf / masstmp;
// dtfm = Delta t / 2 (in corresponding units)
changetmp = (v[i][0] * 2 * dtfm * (f_const_lambda[i][0] - f_dyn_lambda[i][0]) +
dtfm * dtfm *
(f_const_lambda[i][0] * f_const_lambda[i][0] -
f_dyn_lambda[i][0] * f_dyn_lambda[i][0]) +
v[i][1] * 2 * dtfm * (f_const_lambda[i][1] - f_dyn_lambda[i][1]) +
dtfm * dtfm *
(f_const_lambda[i][1] * f_const_lambda[i][1] -
f_dyn_lambda[i][1] * f_dyn_lambda[i][1]) +
v[i][2] * 2 * dtfm * (f_const_lambda[i][2] - f_dyn_lambda[i][2]) +
dtfm * dtfm *
(f_const_lambda[i][2] * f_const_lambda[i][2] -
f_dyn_lambda[i][2] * f_dyn_lambda[i][2])) *
masstmp * force->mvv2e * 0.5;
energy_change_atom[i] += changetmp;
energy_change_kin += changetmp;
}
if (update_stats) {
for (int i = 0; i < nlocal; i++) peratom_stats[i][4] = energy_change_atom[i];
}
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostat::apply_thermostat()
{
double xtmp, ytmp, ztmp, delx, dely, delz, delvx, delvy, delvz, r, dotvu, masstmp, massj, muij,
delv, delv_m, epsilon, epsilonsq, deltaK, rinv;
double m_cm, v_cm[3], beta_rescaling, k_rel, v_rel[3], radicand;
int i, ii, inum, *ilist;
int j, jj, jnum, *jlist;
int nlocal = atom->nlocal;
if (igroup == atom->firstgroup) nlocal = atom->nfirst;
double **x = atom->x;
double **v = atom->v;
double *rmass = atom->rmass;
double *mass = atom->mass;
int *type = atom->type;
int *mask = atom->mask;
double *e_simple = atom->e_simple;
double *e_complex = atom->e_complex;
double *lambda_const = atom->lambda_const;
double *lambda = atom->lambda;
// calculate local neighbour list without ghost atoms
local_neighbour_list();
inum = list->inum;
ilist = list->ilist;
// reset stats
// outvec has to be calculated again
reduceflag = 1;
sum_energy_change = 0;
n_energy_differences = 0;
// perform bath collisions
// consider all local atoms
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
if (!(mask[i] & groupbit)) { continue; }
if (energy_change_atom[i] == 0) { continue; }
// update stats
n_energy_differences++;
// store constant information of target atom
masstmp = (rmass ? rmass[i] : mass[type[i]]);
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
// get neighbour list
jlist = local_firstneigh[i];
jnum = local_numneigh[i];
if (jnum == 0)
error->one(FLERR,
"fix lambda_thermostat: thermostating required for particle with no local "
"particles in neighbour list particle: {} {} {} groupbit {}\n",
xtmp, ytmp, ztmp, mask[i] & groupbit);
int i_collisions = 0;
double e_remain = energy_change_atom[i];
// copy ngh list
for (jj = 0; jj < jnum; jj++) jlist_copy[jj] = jlist[jj];
// shuffle neighbour list for random rescaling set
std::shuffle(jlist_copy, jlist_copy + jnum, random_mt);
// rescale velocities relative to centre of mass velocity
const int n_ngh = MIN(rescaling_N_neighbours, jnum);
if (n_ngh < 2)
error->one(FLERR, "fix lambda_thermostat: rescaling not possible for local ngh list size {}",
jnum);
// 1. calculate centre of mass velocity
// start with own particle ...
m_cm = masstmp;
v_cm[0] = masstmp * v[i][0];
v_cm[1] = masstmp * v[i][1];
v_cm[2] = masstmp * v[i][2];
// ... and include neighbours
for (jj = 0; jj < n_ngh; jj++) {
j = jlist_copy[jj];
j &= NEIGHMASK;
massj = (rmass ? rmass[j] : mass[type[j]]);
m_cm += massj;
v_cm[0] += massj * v[j][0];
v_cm[1] += massj * v[j][1];
v_cm[2] += massj * v[j][2];
}
// normalisation
v_cm[0] /= m_cm;
v_cm[1] /= m_cm;
v_cm[2] /= m_cm;
// 2. calculate beta_rescaling
// calculate kinetic energy of relative velocity for own particle ...
v_rel[0] = v[i][0] - v_cm[0];
v_rel[1] = v[i][1] - v_cm[1];
v_rel[2] = v[i][2] - v_cm[2];
k_rel = masstmp * (v_rel[0] * v_rel[0] + v_rel[1] * v_rel[1] + v_rel[2] * v_rel[2]);
// ... and include neighbours
for (jj = 0; jj < n_ngh; jj++) {
j = jlist_copy[jj];
j &= NEIGHMASK;
massj = (rmass ? rmass[j] : mass[type[j]]);
v_rel[0] = v[j][0] - v_cm[0];
v_rel[1] = v[j][1] - v_cm[1];
v_rel[2] = v[j][2] - v_cm[2];
k_rel += massj * (v_rel[0] * v_rel[0] + v_rel[1] * v_rel[1] + v_rel[2] * v_rel[2]);
}
// normalisation
k_rel *= force->mvv2e / 2.0;
radicand = e_remain / k_rel + 1;
if (radicand < 0) {
// cooling is not possible
// e_remain is the requested energy change
// radicand = 0 <=> e_remain = -k_rel
// -> save the energy error
sum_energy_violation += (-e_remain - k_rel); // > 0
n_energy_violation++;
e_remain += k_rel; // save corrected part
// use smallest possible radicand
radicand = 0;
}
beta_rescaling = sqrt(radicand) - 1;
// 3. apply velocity changes
// start with own particle ...
v_rel[0] = beta_rescaling * (v[i][0] - v_cm[0]);
v_rel[1] = beta_rescaling * (v[i][1] - v_cm[1]);
v_rel[2] = beta_rescaling * (v[i][2] - v_cm[2]);
v[i][0] += v_rel[0];
v[i][1] += v_rel[1];
v[i][2] += v_rel[2];
if (update_stats) {
// forces
peratom_stats[i][0] += v_rel[0] * masstmp / dtf;
peratom_stats[i][1] += v_rel[1] * masstmp / dtf;
peratom_stats[i][2] += v_rel[2] * masstmp / dtf;
}
// ... continue with neighbours
for (jj = 0; jj < n_ngh; jj++) {
j = jlist_copy[jj];
j &= NEIGHMASK;
v_rel[0] = beta_rescaling * (v[j][0] - v_cm[0]);
v_rel[1] = beta_rescaling * (v[j][1] - v_cm[1]);
v_rel[2] = beta_rescaling * (v[j][2] - v_cm[2]);
v[j][0] += v_rel[0];
v[j][1] += v_rel[1];
v[j][2] += v_rel[2];
if (update_stats) {
// forces
massj = (rmass ? rmass[j] : mass[type[j]]);
peratom_stats[j][0] += v_rel[0] * massj / dtf;
peratom_stats[j][1] += v_rel[1] * massj / dtf;
peratom_stats[j][2] += v_rel[2] * massj / dtf;
}
}
sum_energy_change += fabs(e_remain);
}
// lambda_const is used -> reset to lambda
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
if (mask[i] & groupbit) lambda_const[i] = lambda[i];
}
}
/* ---------------------------------------------------------------------- */
double FixLambdaThermostat::compute_vector(int i)
{
// 0 # atoms with energy differences compared to lambda_const
// 1 # bath collisions
// 2 total change of potential energy compared to lambda_const (sum over all atoms)
// 3 total change of kinetic energy compared to lambda_const (sum over all atoms)
// 4 total energy change due to thermostat
if (reduceflag) {
// perform reduction only once per step (if at all)
outvec[0] = n_energy_differences;
outvec[1] = energy_change_pot;
outvec[2] = energy_change_kin;
outvec[3] = sum_energy_change;
outvec[4] = sum_energy_violation;
outvec[5] = n_energy_violation;
MPI_Allreduce(MPI_IN_PLACE, &outvec, size_vector, MPI_DOUBLE, MPI_SUM, world);
reduceflag = 0;
}
if (i < size_vector) return outvec[i];
return 0;
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostat::reset_dt()
{
dtf = 0.5 * update->dt * force->ftm2v;
}
/* ----------------------------------------------------------------------
memory usage of local atom-based arrays
------------------------------------------------------------------------- */
double FixLambdaThermostat::memory_usage()
{
double bytes = 0;
bytes += (double) nmax_energy * sizeof(double);
bytes += (double) nmax_stats * size_peratom_cols * sizeof(double);
bytes += (double) nmax_list * sizeof(int);
bytes += (double) nmax_list * sizeof(int *);
for (int i = 0; i < comm->nthreads; i++) bytes += ipage[i].size();
return bytes;
}

View File

@ -0,0 +1,88 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef FIX_CLASS
// clang-format off
FixStyle(lambda_thermostat,FixLambdaThermostat);
// clang-format on
#else
#ifndef LMP_FIX_LAMBDA_THERMOSTAT_H
#define LMP_FIX_LAMBDA_THERMOSTAT_H
#include "fix.h"
#include <random>
namespace LAMMPS_NS {
class FixLambdaThermostat : public Fix {
public:
FixLambdaThermostat(class LAMMPS *, int, char **);
~FixLambdaThermostat() override;
int setmask() override;
void init() override;
void init_list(int, class NeighList *) override;
void post_force(int) override;
void end_of_step() override;
double memory_usage() override;
void reset_dt() override;
double compute_vector(int) override;
protected:
void apply_thermostat();
void calculate_energy_change();
double calculate_kinetic_energy(int);
void local_neighbour_list();
void init_peratom_stats();
double dtf; // constant for time integration
class NeighList *list;
double *energy_change_atom; // energy violation compared to const lambda case
double **peratom_stats; // peratom output
int nmax_energy; // number of atoms for which energy_change_atom is allocated
int nmax_stats; // number of atoms for which peratom_stats is allocated
// own neighbour list
int pgsize; // size of neighbor page
int oneatom; // max # of neighbors for one atom
int nmax_list; // size of numneigh, firstneigh arrays
int *local_numneigh; // # of pair neighbors for each atom
int **local_firstneigh; // ptr to 1st neighbor of each atom
MyPage<int> *ipage; // neighbor list pages
int *jlist_copy; // jlist for one atom
int reduceflag; // 1/0 calculation of compute_vector required/not required
double outvec[6]; // vector returned by compute_vector
double energy_change_pot; // energy conservation violation of all atoms
double energy_change_kin; // energy conservation violation of all atoms
int n_energy_differences; // number of atoms whose energy has changed compared to the constant lambda case
double sum_energy_change; // absolute value of all energy changes due to rescaling
double sum_energy_violation; // energy that could not be compensated, accumulated over time
int n_energy_violation; // number of atoms whose energy could not be compensated, accumulated over time
int rescaling_N_neighbours; // requested neighbour list size used for rescaling
std::mt19937 random_mt; // mersenne twister for shuffle
bool update_stats; // true(false) peratom output needs(not) to be calculated
};
} // namespace LAMMPS_NS
#endif
#endif

978
src/APIP/pair_eam_apip.cpp Normal file
View File

@ -0,0 +1,978 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing authors: Stephen Foiles (SNL), Murray Daw (SNL) (EAM)
David Immel (d.immel@fz-juelich.de, FZJ, Germany) for APIP
------------------------------------------------------------------------- */
#include "pair_eam_apip.h"
#include "atom.h"
#include "atom_vec_apip.h"
#include "comm.h"
#include "error.h"
#include "force.h"
#include "memory.h"
#include "modify.h"
#include "neigh_list.h"
#include "neighbor.h"
#include "potential_file_reader.h"
#include "update.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairEAMapip::PairEAMapip(LAMMPS *lmp) : Pair(lmp)
{
restartinfo = 0;
manybody_flag = 1;
unit_convert_flag = utils::get_supported_conversions(utils::ENERGY);
nmax = 0;
rho = nullptr;
fp = nullptr;
numforce = nullptr;
type2frho = nullptr;
nfuncfl = 0;
funcfl = nullptr;
setfl = nullptr;
fs = nullptr;
frho = nullptr;
rhor = nullptr;
z2r = nullptr;
scale = nullptr;
rhomax = rhomin = 0.0;
frho_spline = nullptr;
rhor_spline = nullptr;
z2r_spline = nullptr;
n_non_complex_accumulated = 0;
time_per_atom = -1;
time_wall_accumulated = 0;
lambda_thermostat = true;
}
/* ----------------------------------------------------------------------
check if allocated, since class can be destructed when incomplete
------------------------------------------------------------------------- */
PairEAMapip::~PairEAMapip()
{
if (copymode) return;
memory->destroy(rho);
memory->destroy(fp);
memory->destroy(numforce);
if (allocated) {
memory->destroy(setflag);
memory->destroy(cutsq);
delete[] type2frho;
type2frho = nullptr;
memory->destroy(type2rhor);
memory->destroy(type2z2r);
memory->destroy(scale);
}
if (funcfl) {
for (int i = 0; i < nfuncfl; i++) {
delete[] funcfl[i].file;
memory->destroy(funcfl[i].frho);
memory->destroy(funcfl[i].rhor);
memory->destroy(funcfl[i].zr);
}
memory->sfree(funcfl);
funcfl = nullptr;
}
if (setfl) {
for (int i = 0; i < setfl->nelements; i++) delete[] setfl->elements[i];
delete[] setfl->elements;
memory->destroy(setfl->mass);
memory->destroy(setfl->frho);
memory->destroy(setfl->rhor);
memory->destroy(setfl->z2r);
delete setfl;
setfl = nullptr;
}
if (fs) {
for (int i = 0; i < fs->nelements; i++) delete[] fs->elements[i];
delete[] fs->elements;
memory->destroy(fs->mass);
memory->destroy(fs->frho);
memory->destroy(fs->rhor);
memory->destroy(fs->z2r);
delete fs;
fs = nullptr;
}
memory->destroy(frho);
memory->destroy(rhor);
memory->destroy(z2r);
memory->destroy(frho_spline);
memory->destroy(rhor_spline);
memory->destroy(z2r_spline);
}
/* ---------------------------------------------------------------------- */
void PairEAMapip::compute(int eflag, int vflag)
{
// start timers
double time_wall_start = platform::walltime();
int n_non_complex = 0;
int i, j, ii, jj, m, inum, jnum, itype, jtype;
double xtmp, ytmp, ztmp, delx, dely, delz, evdwl, fpair, lambda_ij, fpair_cl;
double rsq, r, p, rhoip, rhojp, z2, z2p, recip, phip, psip, phi, psip_cl;
double *coeff;
int *ilist, *jlist, *numneigh, **firstneigh;
evdwl = 0.0;
ev_init(eflag, vflag);
// grow energy and fp arrays if necessary
// need to be atom->nmax in length
if (atom->nmax > nmax) {
memory->destroy(rho);
memory->destroy(fp);
memory->destroy(numforce);
nmax = atom->nmax;
memory->create(rho, nmax, "pair:rho");
memory->create(fp, nmax, "pair:fp");
memory->create(numforce, nmax, "pair:numforce");
}
double **x = atom->x;
double **f = atom->f;
double *lambda = atom->lambda;
int *lambda_required = atom->lambda_required;
double **f_const_lambda = nullptr;
double **f_dyn_lambda = nullptr;
double *e_simple = nullptr;
double *lambda_const = nullptr;
if (lambda_thermostat) {
f_const_lambda = atom->f_const_lambda;
f_dyn_lambda = atom->f_dyn_lambda;
e_simple = atom->e_simple;
lambda_const = atom->lambda_const;
}
int *type = atom->type;
int nlocal = atom->nlocal;
int nall = nlocal + atom->nghost;
int newton_pair = force->newton_pair;
inum = list->inum;
ilist = list->ilist;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
// zero out density
for (i = 0; i < nlocal; i++) rho[i] = 0.0;
// rho = density at each atom
// loop over neighbors of my atoms
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
itype = type[i];
jlist = firstneigh[i];
jnum = numneigh[i];
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
// avoid double counting due to full neighbour list for two local particles
// j < i implies j < nlocal
if (j < i && i < nlocal) { continue; }
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
rsq = delx * delx + dely * dely + delz * delz;
if (rsq < cutforcesq) {
jtype = type[j];
p = sqrt(rsq) * rdr + 1.0;
m = static_cast<int>(p);
m = MIN(m, nr - 1);
p -= m;
p = MIN(p, 1.0);
coeff = rhor_spline[type2rhor[jtype][itype]][m];
rho[i] += ((coeff[3] * p + coeff[4]) * p + coeff[5]) * p + coeff[6];
// do not calculate rho for ghost atoms
if (j < nlocal) {
coeff = rhor_spline[type2rhor[itype][jtype]][m];
rho[j] += ((coeff[3] * p + coeff[4]) * p + coeff[5]) * p + coeff[6];
}
}
}
}
// fp = derivative of embedding energy at each atom
// phi = embedding energy at each atom
// if rho > rhomax (e.g. due to close approach of two atoms),
// will exceed table, so add linear term to conserve energy
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
p = rho[i] * rdrho + 1.0;
m = static_cast<int>(p);
m = MAX(1, MIN(m, nrho - 1));
p -= m;
p = MIN(p, 1.0);
coeff = frho_spline[type2frho[type[i]]][m];
fp[i] = (coeff[0] * p + coeff[1]) * p + coeff[2];
if (eflag || e_simple) {
phi = ((coeff[3] * p + coeff[4]) * p + coeff[5]) * p + coeff[6];
if (rho[i] > rhomax) phi += fp[i] * (rho[i] - rhomax);
phi *= scale[type[i]][type[i]];
ev_tally_full(i, 2.0 * lambda[i] * phi, 0.0, 0.0, 0.0, 0.0, 0.0);
if (e_simple) { e_simple[i] = phi; }
}
}
// compute forces on each atom
// loop over neighbors of my atoms
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
itype = type[i];
jlist = firstneigh[i];
jnum = numneigh[i];
numforce[i] = 0;
// The distances between atoms are not calculated.
// The neighbour list contains more than the cutoff atoms.
// The calculation of distances is probably not worth the compute time.
// lambda_required is used to compute the weight of atoms for load balancing.
// -> store information about required calculations
if (lambda_required[i] & LambdaRequired::NO_SIMPLE)
continue;
else if (!(lambda_required[i] & LambdaRequired::SIMPLE)) {
// neither SIMPLE nor NO_SIMPLE set
// check own atom
if (lambda[i] != 0 || (lambda_thermostat && lambda_const[i] != 0)) {
// set own atom
lambda_required[i] |= LambdaRequired::SIMPLE;
// set neighbour list
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
if (j < nlocal) lambda_required[j] |= LambdaRequired::SIMPLE;
}
} else {
// check neighbour list
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
if (lambda[j] != 0 || (lambda_thermostat && lambda_const[j] != 0)) {
lambda_required[i] |= LambdaRequired::SIMPLE;
// set lambda also for non-ghost j
if (j < nlocal) lambda_required[j] |= LambdaRequired::SIMPLE;
break;
}
}
}
// SIMPLE not set -> set NO_SIMPLE
if (!(lambda_required[i] & LambdaRequired::SIMPLE)) {
// go to next atom
lambda_required[i] |= LambdaRequired::NO_SIMPLE;
continue;
}
}
n_non_complex++;
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
// avoid double counting due to full neighbour list for two local particles
// j < i implies j < nlocal
if (j < i && i < nlocal) { continue; }
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
rsq = delx * delx + dely * dely + delz * delz;
if (rsq < cutforcesq) {
++numforce[i];
jtype = type[j];
r = sqrt(rsq);
p = r * rdr + 1.0;
m = static_cast<int>(p);
m = MIN(m, nr - 1);
p -= m;
p = MIN(p, 1.0);
// rhoip = derivative of (density at atom j due to atom i)
// rhojp = derivative of (density at atom i due to atom j)
// phi = pair potential energy
// phip = phi'
// z2 = phi * r
// z2p = (phi * r)' = (phi' r) + phi
// psip needs both fp[i] and fp[j] terms since r_ij appears in two
// terms of embed eng: Fi(sum rho_ij) and Fj(sum rho_ji)
// hence embed' = Fi(sum rho_ij) rhojp + Fj(sum rho_ji) rhoip
// scale factor can be applied by thermodynamic integration
coeff = rhor_spline[type2rhor[jtype][itype]][m];
rhojp = (coeff[0] * p + coeff[1]) * p + coeff[2];
coeff = z2r_spline[type2z2r[itype][jtype]][m];
z2p = (coeff[0] * p + coeff[1]) * p + coeff[2];
z2 = ((coeff[3] * p + coeff[4]) * p + coeff[5]) * p + coeff[6];
recip = 1.0 / r;
phi = z2 * recip;
phip = z2p * recip - phi * recip;
if (j < nlocal) {
coeff = rhor_spline[type2rhor[itype][jtype]][m];
rhoip = (coeff[0] * p + coeff[1]) * p + coeff[2];
psip = lambda[i] * fp[i] * rhojp + lambda[j] * fp[j] * rhoip +
phip * (lambda[i] + lambda[j]) / 2;
} else {
// processor of j calculates the remaining terms (compared to psip in if case)
psip = lambda[i] * fp[i] * rhojp + phip * 0.5 * lambda[i];
}
fpair = -scale[itype][jtype] * psip * recip;
f[i][0] += delx * fpair;
f[i][1] += dely * fpair;
f[i][2] += delz * fpair;
f[j][0] -= delx * fpair;
f[j][1] -= dely * fpair;
f[j][2] -= delz * fpair;
if (lambda_thermostat) {
f_dyn_lambda[i][0] += delx * fpair;
f_dyn_lambda[i][1] += dely * fpair;
f_dyn_lambda[i][2] += delz * fpair;
f_dyn_lambda[j][0] -= delx * fpair;
f_dyn_lambda[j][1] -= dely * fpair;
f_dyn_lambda[j][2] -= delz * fpair;
// psip_const
if (j < nlocal) {
psip_cl = lambda_const[i] * fp[i] * rhojp + lambda_const[j] * fp[j] * rhoip +
phip * (lambda_const[i] + lambda_const[j]) / 2;
} else {
psip_cl = lambda_const[i] * fp[i] * rhojp + phip * 0.5 * lambda_const[i];
}
// calculate fpair_const
fpair_cl = -scale[itype][jtype] * psip_cl * recip;
// update f_const_lambda with fpair_const
f_const_lambda[i][0] += delx * fpair_cl;
f_const_lambda[i][1] += dely * fpair_cl;
f_const_lambda[i][2] += delz * fpair_cl;
f_const_lambda[j][0] -= delx * fpair_cl;
f_const_lambda[j][1] -= dely * fpair_cl;
f_const_lambda[j][2] -= delz * fpair_cl;
}
if (eflag || e_simple) {
evdwl = scale[itype][jtype] * phi;
if (e_simple) {
e_simple[i] += 0.5 * evdwl;
if (j < nlocal) e_simple[j] += 0.5 * evdwl;
}
ev_tally_full(i, lambda[i] * evdwl, 0.0, 0.0, 0.0, 0.0, 0.0);
if (j < nlocal) ev_tally_full(j, lambda[j] * evdwl, 0.0, 0.0, 0.0, 0.0, 0.0);
}
if (vflag) ev_tally(i, j, nlocal, newton_pair, 0.0, 0.0, fpair, delx, dely, delz);
}
}
}
if (vflag_fdotr) virial_fdotr_compute();
// stop timers
time_wall_accumulated += platform::walltime() - time_wall_start;
n_non_complex_accumulated += n_non_complex;
}
/* ----------------------------------------------------------------------
allocate all arrays
------------------------------------------------------------------------- */
void PairEAMapip::allocate()
{
allocated = 1;
int n = atom->ntypes;
memory->create(setflag, n + 1, n + 1, "pair:setflag");
for (int i = 1; i <= n; i++)
for (int j = i; j <= n; j++) setflag[i][j] = 0;
memory->create(cutsq, n + 1, n + 1, "pair:cutsq");
delete[] map;
map = new int[n + 1];
for (int i = 1; i <= n; i++) map[i] = -1;
type2frho = new int[n + 1];
memory->create(type2rhor, n + 1, n + 1, "pair:type2rhor");
memory->create(type2z2r, n + 1, n + 1, "pair:type2z2r");
memory->create(scale, n + 1, n + 1, "pair:scale");
}
/* ----------------------------------------------------------------------
global settings
------------------------------------------------------------------------- */
void PairEAMapip::settings(int narg, char ** /*arg*/)
{
if (narg > 0) error->all(FLERR, "Illegal pair_style command");
}
/* ----------------------------------------------------------------------
set coeffs for one or more type pairs
read DYNAMO funcfl file
------------------------------------------------------------------------- */
void PairEAMapip::coeff(int narg, char **arg)
{
if (!allocated) allocate();
if (narg != 3) error->all(FLERR, "Incorrect args for pair coefficients");
// parse pair of atom types
int ilo, ihi, jlo, jhi;
utils::bounds(FLERR, arg[0], 1, atom->ntypes, ilo, ihi, error);
utils::bounds(FLERR, arg[1], 1, atom->ntypes, jlo, jhi, error);
// read funcfl file if hasn't already been read
// store filename in Funcfl data struct
int ifuncfl;
for (ifuncfl = 0; ifuncfl < nfuncfl; ifuncfl++)
if (strcmp(arg[2], funcfl[ifuncfl].file) == 0) break;
if (ifuncfl == nfuncfl) {
nfuncfl++;
funcfl = (Funcfl *) memory->srealloc(funcfl, nfuncfl * sizeof(Funcfl), "pair:funcfl");
read_file(arg[2]);
funcfl[ifuncfl].file = utils::strdup(arg[2]);
}
// set setflag and map only for i,i type pairs
// set mass of atom type if i = j
int count = 0;
for (int i = ilo; i <= ihi; i++) {
for (int j = MAX(jlo, i); j <= jhi; j++) {
if (i == j) {
setflag[i][i] = 1;
map[i] = ifuncfl;
atom->set_mass(FLERR, i, funcfl[ifuncfl].mass);
count++;
}
scale[i][j] = 1.0;
}
}
if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients");
}
/* ----------------------------------------------------------------------
init specific to this pair style
------------------------------------------------------------------------- */
void PairEAMapip::init_style()
{
// convert read-in file(s) to arrays and spline them
if (!atom->lambda_flag)
error->all(FLERR, "Pair style eam/apip requires an atom style with lambda");
if (force->newton_pair == 0) error->all(FLERR, "Pair style eam/apip requires newton pair on");
if (!atom->lambda_required_flag)
error->all(FLERR, "pair style eam/apip requires an atom style with lambda_required.");
file2array();
array2spline();
// communication during computation should be avoided
// -> do not exchange the derivative of the embedding function by default
neighbor->add_request(this, NeighConst::REQ_FULL);
}
/* ----------------------------------------------------------------------
init for one type pair i,j and corresponding j,i
------------------------------------------------------------------------- */
double PairEAMapip::init_one(int i, int j)
{
// single global cutoff = max of cut from all files read in
// for funcfl could be multiple files
// for setfl or fs, just one file
if (setflag[i][j] == 0) scale[i][j] = 1.0;
scale[j][i] = scale[i][j];
if (funcfl) {
cutmax = 0.0;
for (int m = 0; m < nfuncfl; m++) cutmax = MAX(cutmax, funcfl[m].cut);
} else if (setfl)
cutmax = setfl->cut;
else if (fs)
cutmax = fs->cut;
cutforcesq = cutmax * cutmax;
return cutmax;
}
/**
* setup specific to this pair style
* Determine whether there is a fix lambda_thermostat or not and set
* lambda_thermostat.
*/
void PairEAMapip::setup()
{
if (modify->get_fix_by_style("^lambda_thermostat$").size() == 0) {
lambda_thermostat = false;
} else {
lambda_thermostat = true;
if (!atom->lambda_const_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with lambda_const for a local thermostat.");
if (!atom->e_simple_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with e_simple for a local thermostat.");
if (!atom->f_const_lambda_flag)
error->all(FLERR,
"Pair style pace/apip requires an atom style with f_const_lambda for a local "
"thermostat.");
if (!atom->f_dyn_lambda_flag)
error->all(FLERR,
"Pair style pace/apip requires an atom style with f_const_lambda for a local "
"thermostat.");
}
}
/* ----------------------------------------------------------------------
read potential values from a DYNAMO single element funcfl file
------------------------------------------------------------------------- */
void PairEAMapip::read_file(char *filename)
{
Funcfl *file = &funcfl[nfuncfl - 1];
// read potential file
if (comm->me == 0) {
PotentialFileReader reader(lmp, filename, "eam", unit_convert_flag);
// transparently convert units for supported conversions
int unit_convert = reader.get_unit_convert();
double conversion_factor = utils::get_conversion_factor(utils::ENERGY, unit_convert);
try {
reader.skip_line();
ValueTokenizer values = reader.next_values(2);
values.next_int(); // ignore
file->mass = values.next_double();
values = reader.next_values(5);
file->nrho = values.next_int();
file->drho = values.next_double();
file->nr = values.next_int();
file->dr = values.next_double();
file->cut = values.next_double();
if ((file->nrho <= 0) || (file->nr <= 0) || (file->dr <= 0.0))
error->one(FLERR, "Invalid EAM potential file");
memory->create(file->frho, (file->nrho + 1), "pair:frho");
memory->create(file->rhor, (file->nr + 1), "pair:rhor");
memory->create(file->zr, (file->nr + 1), "pair:zr");
reader.next_dvector(&file->frho[1], file->nrho);
reader.next_dvector(&file->zr[1], file->nr);
reader.next_dvector(&file->rhor[1], file->nr);
if (unit_convert) {
const double sqrt_conv = sqrt(conversion_factor);
for (int i = 1; i <= file->nrho; ++i) file->frho[i] *= conversion_factor;
for (int j = 1; j <= file->nr; ++j) file->zr[j] *= sqrt_conv;
}
} catch (TokenizerException &e) {
error->one(FLERR, e.what());
}
}
MPI_Bcast(&file->mass, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->nrho, 1, MPI_INT, 0, world);
MPI_Bcast(&file->drho, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->nr, 1, MPI_INT, 0, world);
MPI_Bcast(&file->dr, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->cut, 1, MPI_DOUBLE, 0, world);
if (comm->me != 0) {
memory->create(file->frho, (file->nrho + 1), "pair:frho");
memory->create(file->rhor, (file->nr + 1), "pair:rhor");
memory->create(file->zr, (file->nr + 1), "pair:zr");
}
MPI_Bcast(&file->frho[1], file->nrho, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->zr[1], file->nr, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->rhor[1], file->nr, MPI_DOUBLE, 0, world);
}
/* ----------------------------------------------------------------------
convert read-in funcfl potential(s) to standard array format
interpolate all file values to a single grid and cutoff
------------------------------------------------------------------------- */
void PairEAMapip::file2array()
{
int i, j, k, m, n;
int ntypes = atom->ntypes;
double sixth = 1.0 / 6.0;
// determine max function params from all active funcfl files
// active means some element is pointing at it via map
int active;
double rmax;
dr = drho = rmax = rhomax = 0.0;
for (int i = 0; i < nfuncfl; i++) {
active = 0;
for (j = 1; j <= ntypes; j++)
if (map[j] == i) active = 1;
if (active == 0) continue;
Funcfl *file = &funcfl[i];
dr = MAX(dr, file->dr);
drho = MAX(drho, file->drho);
rmax = MAX(rmax, (file->nr - 1) * file->dr);
rhomax = MAX(rhomax, (file->nrho - 1) * file->drho);
}
// set nr,nrho from cutoff and spacings
// 0.5 is for round-off in divide
nr = static_cast<int>(rmax / dr + 0.5);
nrho = static_cast<int>(rhomax / drho + 0.5);
// ------------------------------------------------------------------
// setup frho arrays
// ------------------------------------------------------------------
// allocate frho arrays
// nfrho = # of funcfl files + 1 for zero array
nfrho = nfuncfl + 1;
memory->destroy(frho);
memory->create(frho, nfrho, nrho + 1, "pair:frho");
// interpolate each file's frho to a single grid and cutoff
double r, p, cof1, cof2, cof3, cof4;
n = 0;
for (i = 0; i < nfuncfl; i++) {
Funcfl *file = &funcfl[i];
for (m = 1; m <= nrho; m++) {
r = (m - 1) * drho;
p = r / file->drho + 1.0;
k = static_cast<int>(p);
k = MIN(k, file->nrho - 2);
k = MAX(k, 2);
p -= k;
p = MIN(p, 2.0);
cof1 = -sixth * p * (p - 1.0) * (p - 2.0);
cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
cof3 = -0.5 * p * (p + 1.0) * (p - 2.0);
cof4 = sixth * p * (p * p - 1.0);
frho[n][m] = cof1 * file->frho[k - 1] + cof2 * file->frho[k] + cof3 * file->frho[k + 1] +
cof4 * file->frho[k + 2];
}
n++;
}
// add extra frho of zeroes for non-EAM types to point to (pair hybrid)
// this is necessary b/c fp is still computed for non-EAM atoms
for (m = 1; m <= nrho; m++) frho[nfrho - 1][m] = 0.0;
// type2frho[i] = which frho array (0 to nfrho-1) each atom type maps to
// if atom type doesn't point to file (non-EAM atom in pair hybrid)
// then map it to last frho array of zeroes
for (i = 1; i <= ntypes; i++)
if (map[i] >= 0)
type2frho[i] = map[i];
else
type2frho[i] = nfrho - 1;
// ------------------------------------------------------------------
// setup rhor arrays
// ------------------------------------------------------------------
// allocate rhor arrays
// nrhor = # of funcfl files
nrhor = nfuncfl;
memory->destroy(rhor);
memory->create(rhor, nrhor, nr + 1, "pair:rhor");
// interpolate each file's rhor to a single grid and cutoff
n = 0;
for (i = 0; i < nfuncfl; i++) {
Funcfl *file = &funcfl[i];
for (m = 1; m <= nr; m++) {
r = (m - 1) * dr;
p = r / file->dr + 1.0;
k = static_cast<int>(p);
k = MIN(k, file->nr - 2);
k = MAX(k, 2);
p -= k;
p = MIN(p, 2.0);
cof1 = -sixth * p * (p - 1.0) * (p - 2.0);
cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
cof3 = -0.5 * p * (p + 1.0) * (p - 2.0);
cof4 = sixth * p * (p * p - 1.0);
rhor[n][m] = cof1 * file->rhor[k - 1] + cof2 * file->rhor[k] + cof3 * file->rhor[k + 1] +
cof4 * file->rhor[k + 2];
}
n++;
}
// type2rhor[i][j] = which rhor array (0 to nrhor-1) each type pair maps to
// for funcfl files, I,J mapping only depends on I
// OK if map = -1 (non-EAM atom in pair hybrid) b/c type2rhor not used
for (i = 1; i <= ntypes; i++)
for (j = 1; j <= ntypes; j++) type2rhor[i][j] = map[i];
// ------------------------------------------------------------------
// setup z2r arrays
// ------------------------------------------------------------------
// allocate z2r arrays
// nz2r = N*(N+1)/2 where N = # of funcfl files
nz2r = nfuncfl * (nfuncfl + 1) / 2;
memory->destroy(z2r);
memory->create(z2r, nz2r, nr + 1, "pair:z2r");
// create a z2r array for each file against other files, only for I >= J
// interpolate zri and zrj to a single grid and cutoff
// final z2r includes unit conversion of 27.2 eV/Hartree and 0.529 Ang/Bohr
double zri, zrj;
n = 0;
for (i = 0; i < nfuncfl; i++) {
Funcfl *ifile = &funcfl[i];
for (j = 0; j <= i; j++) {
Funcfl *jfile = &funcfl[j];
for (m = 1; m <= nr; m++) {
r = (m - 1) * dr;
p = r / ifile->dr + 1.0;
k = static_cast<int>(p);
k = MIN(k, ifile->nr - 2);
k = MAX(k, 2);
p -= k;
p = MIN(p, 2.0);
cof1 = -sixth * p * (p - 1.0) * (p - 2.0);
cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
cof3 = -0.5 * p * (p + 1.0) * (p - 2.0);
cof4 = sixth * p * (p * p - 1.0);
zri = cof1 * ifile->zr[k - 1] + cof2 * ifile->zr[k] + cof3 * ifile->zr[k + 1] +
cof4 * ifile->zr[k + 2];
p = r / jfile->dr + 1.0;
k = static_cast<int>(p);
k = MIN(k, jfile->nr - 2);
k = MAX(k, 2);
p -= k;
p = MIN(p, 2.0);
cof1 = -sixth * p * (p - 1.0) * (p - 2.0);
cof2 = 0.5 * (p * p - 1.0) * (p - 2.0);
cof3 = -0.5 * p * (p + 1.0) * (p - 2.0);
cof4 = sixth * p * (p * p - 1.0);
zrj = cof1 * jfile->zr[k - 1] + cof2 * jfile->zr[k] + cof3 * jfile->zr[k + 1] +
cof4 * jfile->zr[k + 2];
z2r[n][m] = 27.2 * 0.529 * zri * zrj;
}
n++;
}
}
// type2z2r[i][j] = which z2r array (0 to nz2r-1) each type pair maps to
// set of z2r arrays only fill lower triangular Nelement matrix
// value = n = sum over rows of lower-triangular matrix until reach irow,icol
// swap indices when irow < icol to stay lower triangular
// if map = -1 (non-EAM atom in pair hybrid):
// type2z2r is not used by non-opt
// but set type2z2r to 0 since accessed by opt
int irow, icol;
for (i = 1; i <= ntypes; i++) {
for (j = 1; j <= ntypes; j++) {
irow = map[i];
icol = map[j];
if (irow == -1 || icol == -1) {
type2z2r[i][j] = 0;
continue;
}
if (irow < icol) {
irow = map[j];
icol = map[i];
}
n = 0;
for (m = 0; m < irow; m++) n += m + 1;
n += icol;
type2z2r[i][j] = n;
}
}
}
/* ---------------------------------------------------------------------- */
void PairEAMapip::array2spline()
{
rdr = 1.0 / dr;
rdrho = 1.0 / drho;
memory->destroy(frho_spline);
memory->destroy(rhor_spline);
memory->destroy(z2r_spline);
memory->create(frho_spline, nfrho, nrho + 1, 7, "pair:frho");
memory->create(rhor_spline, nrhor, nr + 1, 7, "pair:rhor");
memory->create(z2r_spline, nz2r, nr + 1, 7, "pair:z2r");
for (int i = 0; i < nfrho; i++) interpolate(nrho, drho, frho[i], frho_spline[i]);
for (int i = 0; i < nrhor; i++) interpolate(nr, dr, rhor[i], rhor_spline[i]);
for (int i = 0; i < nz2r; i++) interpolate(nr, dr, z2r[i], z2r_spline[i]);
}
/* ---------------------------------------------------------------------- */
void PairEAMapip::interpolate(int n, double delta, double *f, double **spline)
{
for (int m = 1; m <= n; m++) spline[m][6] = f[m];
spline[1][5] = spline[2][6] - spline[1][6];
spline[2][5] = 0.5 * (spline[3][6] - spline[1][6]);
spline[n - 1][5] = 0.5 * (spline[n][6] - spline[n - 2][6]);
spline[n][5] = spline[n][6] - spline[n - 1][6];
for (int m = 3; m <= n - 2; m++)
spline[m][5] =
((spline[m - 2][6] - spline[m + 2][6]) + 8.0 * (spline[m + 1][6] - spline[m - 1][6])) /
12.0;
for (int m = 1; m <= n - 1; m++) {
spline[m][4] = 3.0 * (spline[m + 1][6] - spline[m][6]) - 2.0 * spline[m][5] - spline[m + 1][5];
spline[m][3] = spline[m][5] + spline[m + 1][5] - 2.0 * (spline[m + 1][6] - spline[m][6]);
}
spline[n][4] = 0.0;
spline[n][3] = 0.0;
for (int m = 1; m <= n; m++) {
spline[m][2] = spline[m][5] / delta;
spline[m][1] = 2.0 * spline[m][4] / delta;
spline[m][0] = 3.0 * spline[m][3] / delta;
}
}
/* ----------------------------------------------------------------------
memory usage of local atom-based arrays
------------------------------------------------------------------------- */
double PairEAMapip::memory_usage()
{
double bytes = (double) maxeatom * sizeof(double);
bytes += (double) maxvatom * 6 * sizeof(double);
bytes += (double) 2 * nmax * sizeof(double);
return bytes;
}
/* ----------------------------------------------------------------------
swap fp array with one passed in by caller
------------------------------------------------------------------------- */
void PairEAMapip::swap_eam(double *fp_caller, double **fp_caller_hold)
{
double *tmp = fp;
fp = fp_caller;
*fp_caller_hold = tmp;
}
/* ----------------------------------------------------------------------
set return values for timers and counted particles
------------------------------------------------------------------------- */
void PairEAMapip::calculate_time_per_atom()
{
if (n_non_complex_accumulated > 0)
time_per_atom = time_wall_accumulated / n_non_complex_accumulated;
else
time_per_atom = -1;
// reset
time_wall_accumulated = 0;
n_non_complex_accumulated = 0;
}
/* ---------------------------------------------------------------------- */
void *PairEAMapip::extract(const char *str, int &dim)
{
dim = 2;
if (strcmp(str, "scale") == 0) return (void *) scale;
dim = 0;
if (strcmp(str, "eam/apip:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
return nullptr;
}

123
src/APIP/pair_eam_apip.h Normal file
View File

@ -0,0 +1,123 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing authors: Stephen Foiles (SNL), Murray Daw (SNL) (EAM)
David Immel (d.immel@fz-juelich.de, FZJ, Germany) (APIP)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(eam/apip,PairEAMapip);
// clang-format on
#else
#ifndef LMP_PAIR_EAM_APIP_H
#define LMP_PAIR_EAM_APIP_H
#include "pair.h"
namespace LAMMPS_NS {
class PairEAMapip : public Pair {
public:
friend class FixSemiGrandCanonicalMC; // Alex Stukowski option
// public variables so ATC package can access them
double cutmax;
// potentials as array data
int nrho, nr;
int nfrho, nrhor, nz2r;
double **frho, **rhor, **z2r;
int *type2frho, **type2rhor, **type2z2r;
// potentials in spline form used for force computation
double dr, rdr, drho, rdrho, rhomax, rhomin;
double ***rhor_spline, ***frho_spline, ***z2r_spline;
PairEAMapip(class LAMMPS *);
~PairEAMapip() override;
void compute(int, int) override;
void settings(int, char **) override;
void setup() override;
void coeff(int, char **) override;
void init_style() override;
double init_one(int, int) override;
void *extract(const char *, int &) override;
double memory_usage() override;
void swap_eam(double *, double **) override;
protected:
int nmax; // allocated size of per-atom arrays
double cutforcesq;
double **scale;
// per-atom arrays
double *rho, *fp;
int *numforce;
// potentials as file data
struct Funcfl {
char *file;
int nrho, nr;
double drho, dr, cut, mass;
double *frho, *rhor, *zr;
};
Funcfl *funcfl;
int nfuncfl;
struct Setfl {
char **elements;
int nelements, nrho, nr;
double drho, dr, cut;
double *mass;
double **frho, **rhor, ***z2r;
};
Setfl *setfl;
struct Fs {
char **elements;
int nelements, nrho, nr;
double drho, dr, cut;
double *mass;
double **frho, ***rhor, ***z2r;
};
Fs *fs;
virtual void allocate();
virtual void array2spline();
void interpolate(int, double, double *, double **);
virtual void read_file(char *);
virtual void file2array();
// stats required for load balancing
int n_non_complex_accumulated; // number of calculated atoms
double time_wall_accumulated; // time required for the atom calculation
double time_per_atom; // time for one calculation
void calculate_time_per_atom();
bool lambda_thermostat; // true/false there is one/no fix lambda_thermostat
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,365 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing authors: Tim Lau (MIT) for EAM/FS
David Immel (d.immel@fz-juelich.de, FZJ, Germany) for APIP
------------------------------------------------------------------------- */
#include "pair_eam_fs_apip.h"
#include "atom.h"
#include "comm.h"
#include "error.h"
#include "memory.h"
#include "potential_file_reader.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairEAMFSapip::PairEAMFSapip(LAMMPS *lmp) : PairEAMapip(lmp)
{
one_coeff = 1;
manybody_flag = 1;
he_flag = 0;
}
/* ----------------------------------------------------------------------
set coeffs for one or more type pairs
read EAM Finnis-Sinclair file
------------------------------------------------------------------------- */
void PairEAMFSapip::coeff(int narg, char **arg)
{
int i, j;
if (!allocated) allocate();
if (narg != 3 + atom->ntypes) error->all(FLERR, "Incorrect args for pair coefficients");
// insure I,J args are * *
if (strcmp(arg[0], "*") != 0 || strcmp(arg[1], "*") != 0)
error->all(FLERR, "Incorrect args for pair coefficients");
// read EAM Finnis-Sinclair file
if (fs) {
for (i = 0; i < fs->nelements; i++) delete[] fs->elements[i];
delete[] fs->elements;
memory->destroy(fs->mass);
memory->destroy(fs->frho);
memory->destroy(fs->rhor);
memory->destroy(fs->z2r);
delete fs;
}
fs = new Fs();
read_file(arg[2]);
// read args that map atom types to elements in potential file
// map[i] = which element the Ith atom type is, -1 if "NULL"
for (i = 3; i < narg; i++) {
if (strcmp(arg[i], "NULL") == 0) {
map[i - 2] = -1;
continue;
}
for (j = 0; j < fs->nelements; j++)
if (strcmp(arg[i], fs->elements[j]) == 0) break;
if (j < fs->nelements)
map[i - 2] = j;
else
error->all(FLERR, "No matching element in EAM potential file");
}
// clear setflag since coeff() called once with I,J = * *
int n = atom->ntypes;
for (i = 1; i <= n; i++)
for (j = i; j <= n; j++) setflag[i][j] = 0;
// set setflag i,j for type pairs where both are mapped to elements
// set mass of atom type if i = j
int count = 0;
for (i = 1; i <= n; i++) {
for (j = i; j <= n; j++) {
if (map[i] >= 0 && map[j] >= 0) {
setflag[i][j] = 1;
if (i == j) atom->set_mass(FLERR, i, fs->mass[map[i]]);
count++;
}
scale[i][j] = 1.0;
}
}
if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients");
}
/* ----------------------------------------------------------------------
read a multi-element DYNAMO setfl file
------------------------------------------------------------------------- */
void PairEAMFSapip::read_file(char *filename)
{
Fs *file = fs;
// read potential file
if (comm->me == 0) {
PotentialFileReader reader(lmp, filename, he_flag ? "eam/he" : "eam/fs", unit_convert_flag);
// transparently convert units for supported conversions
int unit_convert = reader.get_unit_convert();
double conversion_factor = utils::get_conversion_factor(utils::ENERGY, unit_convert);
try {
reader.skip_line();
reader.skip_line();
reader.skip_line();
// extract element names from nelements line
ValueTokenizer values = reader.next_values(1);
file->nelements = values.next_int();
if ((int) values.count() != file->nelements + 1)
error->one(FLERR, "Incorrect element names in EAM potential file");
file->elements = new char *[file->nelements];
for (int i = 0; i < file->nelements; i++) {
const std::string word = values.next_string();
const int n = word.length() + 1;
file->elements[i] = new char[n];
strcpy(file->elements[i], word.c_str());
}
//
if (he_flag)
values = reader.next_values(6);
else
values = reader.next_values(5);
file->nrho = values.next_int();
file->drho = values.next_double();
file->nr = values.next_int();
file->dr = values.next_double();
file->cut = values.next_double();
if (he_flag) rhomax = values.next_double();
if ((file->nrho <= 0) || (file->nr <= 0) || (file->dr <= 0.0))
error->one(FLERR, "Invalid EAM potential file");
memory->create(file->mass, file->nelements, "pair:mass");
memory->create(file->frho, file->nelements, file->nrho + 1, "pair:frho");
memory->create(file->rhor, file->nelements, file->nelements, file->nr + 1, "pair:rhor");
memory->create(file->z2r, file->nelements, file->nelements, file->nr + 1, "pair:z2r");
for (int i = 0; i < file->nelements; i++) {
values = reader.next_values(2);
values.next_int(); // ignore
file->mass[i] = values.next_double();
reader.next_dvector(&file->frho[i][1], file->nrho);
if (unit_convert) {
for (int j = 1; j <= file->nrho; ++j) file->frho[i][j] *= conversion_factor;
}
for (int j = 0; j < file->nelements; j++) {
reader.next_dvector(&file->rhor[i][j][1], file->nr);
}
}
for (int i = 0; i < file->nelements; i++) {
for (int j = 0; j <= i; j++) {
reader.next_dvector(&file->z2r[i][j][1], file->nr);
if (unit_convert) {
for (int k = 1; k <= file->nr; ++k) file->z2r[i][j][k] *= conversion_factor;
}
}
}
} catch (TokenizerException &e) {
error->one(FLERR, e.what());
}
}
// broadcast potential information
MPI_Bcast(&file->nelements, 1, MPI_INT, 0, world);
MPI_Bcast(&file->nrho, 1, MPI_INT, 0, world);
MPI_Bcast(&file->drho, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->nr, 1, MPI_INT, 0, world);
MPI_Bcast(&file->dr, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->cut, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&rhomax, 1, MPI_DOUBLE, 0, world);
// allocate memory on other procs
if (comm->me != 0) {
file->elements = new char *[file->nelements];
for (int i = 0; i < file->nelements; i++) file->elements[i] = nullptr;
memory->create(file->mass, file->nelements, "pair:mass");
memory->create(file->frho, file->nelements, file->nrho + 1, "pair:frho");
memory->create(file->rhor, file->nelements, file->nelements, file->nr + 1, "pair:rhor");
memory->create(file->z2r, file->nelements, file->nelements, file->nr + 1, "pair:z2r");
}
// broadcast file->elements string array
for (int i = 0; i < file->nelements; i++) {
int n;
if (comm->me == 0) n = strlen(file->elements[i]) + 1;
MPI_Bcast(&n, 1, MPI_INT, 0, world);
if (comm->me != 0) file->elements[i] = new char[n];
MPI_Bcast(file->elements[i], n, MPI_CHAR, 0, world);
}
// broadcast file->mass, frho, rhor
for (int i = 0; i < file->nelements; i++) {
MPI_Bcast(&file->mass[i], 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&file->frho[i][1], file->nrho, MPI_DOUBLE, 0, world);
for (int j = 0; j < file->nelements; j++) {
MPI_Bcast(&file->rhor[i][j][1], file->nr, MPI_DOUBLE, 0, world);
}
}
// broadcast file->z2r
for (int i = 0; i < file->nelements; i++) {
for (int j = 0; j <= i; j++) { MPI_Bcast(&file->z2r[i][j][1], file->nr, MPI_DOUBLE, 0, world); }
}
}
/* ----------------------------------------------------------------------
copy read-in setfl potential to standard array format
------------------------------------------------------------------------- */
void PairEAMFSapip::file2array()
{
int i, j, m, n;
int ntypes = atom->ntypes;
// set function params directly from fs file
nrho = fs->nrho;
nr = fs->nr;
drho = fs->drho;
dr = fs->dr;
if (he_flag)
rhomin = rhomax - (nrho - 1) * drho;
else
rhomax = (nrho - 1) * drho;
// ------------------------------------------------------------------
// setup frho arrays
// ------------------------------------------------------------------
// allocate frho arrays
// nfrho = # of fs elements + 1 for zero array
nfrho = fs->nelements + 1;
memory->destroy(frho);
memory->create(frho, nfrho, nrho + 1, "pair:frho");
// copy each element's frho to global frho
for (i = 0; i < fs->nelements; i++)
for (m = 1; m <= nrho; m++) frho[i][m] = fs->frho[i][m];
// add extra frho of zeroes for non-EAM types to point to (pair hybrid)
// this is necessary b/c fp is still computed for non-EAM atoms
for (m = 1; m <= nrho; m++) frho[nfrho - 1][m] = 0.0;
// type2frho[i] = which frho array (0 to nfrho-1) each atom type maps to
// if atom type doesn't point to element (non-EAM atom in pair hybrid)
// then map it to last frho array of zeroes
for (i = 1; i <= ntypes; i++)
if (map[i] >= 0)
type2frho[i] = map[i];
else
type2frho[i] = nfrho - 1;
// ------------------------------------------------------------------
// setup rhor arrays
// ------------------------------------------------------------------
// allocate rhor arrays
// nrhor = square of # of fs elements
nrhor = fs->nelements * fs->nelements;
memory->destroy(rhor);
memory->create(rhor, nrhor, nr + 1, "pair:rhor");
// copy each element pair rhor to global rhor
n = 0;
for (i = 0; i < fs->nelements; i++)
for (j = 0; j < fs->nelements; j++) {
for (m = 1; m <= nr; m++) rhor[n][m] = fs->rhor[i][j][m];
n++;
}
// type2rhor[i][j] = which rhor array (0 to nrhor-1) each type pair maps to
// for fs files, there is a full NxN set of rhor arrays
// OK if map = -1 (non-EAM atom in pair hybrid) b/c type2rhor not used
for (i = 1; i <= ntypes; i++)
for (j = 1; j <= ntypes; j++) type2rhor[i][j] = map[i] * fs->nelements + map[j];
// ------------------------------------------------------------------
// setup z2r arrays
// ------------------------------------------------------------------
// allocate z2r arrays
// nz2r = N*(N+1)/2 where N = # of fs elements
nz2r = fs->nelements * (fs->nelements + 1) / 2;
memory->destroy(z2r);
memory->create(z2r, nz2r, nr + 1, "pair:z2r");
// copy each element pair z2r to global z2r, only for I >= J
n = 0;
for (i = 0; i < fs->nelements; i++)
for (j = 0; j <= i; j++) {
for (m = 1; m <= nr; m++) z2r[n][m] = fs->z2r[i][j][m];
n++;
}
// type2z2r[i][j] = which z2r array (0 to nz2r-1) each type pair maps to
// set of z2r arrays only fill lower triangular Nelement matrix
// value = n = sum over rows of lower-triangular matrix until reach irow,icol
// swap indices when irow < icol to stay lower triangular
// if map = -1 (non-EAM atom in pair hybrid):
// type2z2r is not used by non-opt
// but set type2z2r to 0 since accessed by opt
int irow, icol;
for (i = 1; i <= ntypes; i++) {
for (j = 1; j <= ntypes; j++) {
irow = map[i];
icol = map[j];
if (irow == -1 || icol == -1) {
type2z2r[i][j] = 0;
continue;
}
if (irow < icol) {
irow = map[j];
icol = map[i];
}
n = 0;
for (m = 0; m < irow; m++) n += m + 1;
n += icol;
type2z2r[i][j] = n;
}
}
}

View File

@ -0,0 +1,48 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: Tim Lau (MIT) for EAM/FS
David Immel (d.immel@fz-juelich.de, FZJ, Germany) for APIP
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(eam/fs/apip,PairEAMFSapip);
// clang-format on
#else
#ifndef LMP_PAIR_EAM_FS_APIP_H
#define LMP_PAIR_EAM_FS_APIP_H
#include "pair_eam_apip.h"
namespace LAMMPS_NS {
// need virtual public b/c of how eam/fs/opt inherits from it
class PairEAMFSapip : virtual public PairEAMapip {
public:
PairEAMFSapip(class LAMMPS *);
void coeff(int, char **) override;
protected:
void read_file(char *) override;
void file2array() override;
int he_flag;
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,218 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "pair_lambda_input.h"
#include "atom.h"
#include "comm.h"
#include "error.h"
#include "force.h"
#include "memory.h"
#include "modify.h"
#include "neigh_list.h"
#include "neighbor.h"
#include "update.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairLambdaInput::PairLambdaInput(LAMMPS *lmp) : Pair(lmp), fix_lambda(nullptr), cut(nullptr)
{
cut_global = -1;
ignore_group_bit = 0;
timer = 0;
n_calculations = 0;
time_per_atom = -1;
}
/* ---------------------------------------------------------------------- */
PairLambdaInput::~PairLambdaInput()
{
if (copymode) return;
if (allocated) {
memory->destroy(setflag);
memory->destroy(cutsq);
memory->destroy(cut);
}
}
/* ---------------------------------------------------------------------- */
void PairLambdaInput::coeff(int narg, char **arg)
{
if (narg != 2) error->all(FLERR, "Incorrect args for pair coefficients");
if (!allocated) allocate();
int ilo, ihi, jlo, jhi;
utils::bounds(FLERR, arg[0], 1, atom->ntypes, ilo, ihi, error);
utils::bounds(FLERR, arg[1], 1, atom->ntypes, jlo, jhi, error);
int count = 0;
for (int i = ilo; i <= ihi; i++) {
for (int j = MAX(jlo, i); j <= jhi; j++) {
setflag[i][j] = 1;
cut[i][j] = cut_global;
count++;
}
}
if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients");
}
/* ----------------------------------------------------------------------
allocate all arrays
------------------------------------------------------------------------- */
void PairLambdaInput::allocate()
{
allocated = 1;
int n = atom->ntypes + 1;
memory->create(setflag, n, n, "pair:setflag");
for (int i = 1; i < n; i++)
for (int j = i; j < n; j++) setflag[i][j] = 0;
memory->create(cutsq, n, n, "pair:cutsq");
memory->create(cut, n, n, "pair:cut");
}
/* ---------------------------------------------------------------------- */
void PairLambdaInput::compute(int eflag, int vflag)
{
// basic stuff (see pair_zero)
ev_init(eflag, vflag);
if (vflag_fdotr) virial_fdotr_compute();
double timer_start = platform::walltime();
n_calculations += calculate_lambda_input();
timer += platform::walltime() - timer_start;
fix_lambda->update_lambda_input_history();
}
/* ----------------------------------------------------------------------
global settings
------------------------------------------------------------------------- */
void PairLambdaInput::settings(int narg, char **arg)
{
if (narg < 1) utils::missing_cmd_args(FLERR, "pair_style lambda_input", error);
cut_global = utils::numeric(FLERR, arg[0], false, lmp);
// reset cutoffs that have been explicitly set
if (allocated) {
int i, j;
for (i = 1; i <= atom->ntypes; i++)
for (j = i; j <= atom->ntypes; j++)
if (setflag[i][j]) cut[i][j] = cut_global;
}
}
/* ----------------------------------------------------------------------
init specific to this pair style
------------------------------------------------------------------------- */
void PairLambdaInput::init_style()
{
if (!atom->lambda_input_flag)
error->all(FLERR, "pair_lambda input requires an atom style with lambda_input");
// find fix lambda
int count = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "lambda") == 0) {
fix_lambda = (FixLambda *) modify->fix[i];
count++;
}
}
if (count != 1) error->all(FLERR, "Exact one fix lambda required");
// get group whose input is ignored from fix lambda
ignore_group_bit = fix_lambda->group_bit_ignore_lambda_input;
neighbor->add_request(this, NeighConst::REQ_FULL);
}
/* ----------------------------------------------------------------------
init for one type pair i,j and corresponding j,i
------------------------------------------------------------------------- */
double PairLambdaInput::init_one(int i, int j)
{
if (setflag[i][j] == 0) { cut[i][j] = mix_distance(cut[i][i], cut[j][j]); }
return cut[i][j];
}
/**
* Compute lambda_input and write it to atom->lambda_input.
* Count the number of computations and measure the compute time for
* fix apip_atom_weight.
*/
int PairLambdaInput::calculate_lambda_input()
{
int i, ii, inum;
int *ilist;
inum = list->inum;
ilist = list->ilist;
double *lambda_input = atom->lambda_input;
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
// "compute" and set lambda input
lambda_input[i] = 0;
}
// return number of calculations
return inum;
}
/* ----------------------------------------------------------------------
set return values for timers and counted particles
------------------------------------------------------------------------- */
void PairLambdaInput::calculate_time_per_atom()
{
if (n_calculations > 0)
time_per_atom = timer / n_calculations;
else
time_per_atom = -1;
// reset
timer = 0;
n_calculations = 0;
}
/* ---------------------------------------------------------------------- */
void *PairLambdaInput::extract(const char *str, int &dim)
{
dim = 0;
if (strcmp(str, "lambda_input:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
return nullptr;
}

View File

@ -0,0 +1,64 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(lambda_input,PairLambdaInput);
// clang-format on
#else
#ifndef LMP_PAIR_LAMBDA_INPUT_H
#define LMP_PAIR_LAMBDA_INPUT_H
#include "fix_lambda.h"
#include "pair.h"
namespace LAMMPS_NS {
class PairLambdaInput : public Pair {
friend class FixLambda;
public:
PairLambdaInput(class LAMMPS *);
~PairLambdaInput() override;
void compute(int, int) override;
virtual void settings(int, char **) override;
void coeff(int, char **) override;
void init_style() override;
double init_one(int, int) override;
void *extract(const char *, int &) override;
protected:
class FixLambda *fix_lambda; // ptr to fix lambda to store the calculated lambda_input
// pro forma pair style variables
double cut_global;
double **cut;
double timer; // accumulated compute time
double time_per_atom; // compute time of one calculaiton
int n_calculations; // number of accumulated calculations
int ignore_group_bit; // groupbit of the group that must not be calculated
void allocate();
void calculate_time_per_atom();
virtual int calculate_lambda_input();
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,319 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "pair_lambda_input_csp.h"
#include "atom.h"
#include "error.h"
#include "memory.h"
#include "neigh_list.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairLambdaInputCsp::PairLambdaInputCsp(LAMMPS *lmp) :
PairLambdaInput(lmp), distsq(nullptr), nearest(nullptr)
{
// set defaults
nnn = 0;
nnn_buffer = 0;
maxneigh = 0;
cut_csp_sq = -1;
}
/* ---------------------------------------------------------------------- */
PairLambdaInputCsp::~PairLambdaInputCsp()
{
if (copymode) return;
memory->destroy(distsq);
memory->destroy(nearest);
}
/* ----------------------------------------------------------------------
global settings
------------------------------------------------------------------------- */
void PairLambdaInputCsp::settings(int narg, char **arg)
{
double cut_csp = 5;
// parse arguments
if (narg < 1) error->all(FLERR, "pair lambda_input/csp: lattice requires one argument");
if (strcmp(arg[0], "fcc") == 0)
nnn = 12;
else if (strcmp(arg[0], "bcc") == 0)
nnn = 8;
else
nnn = utils::inumeric(FLERR, arg[0], false, lmp);
int iarg = 1;
while (iarg < narg) {
if (strcmp(arg[iarg], "cutoff") == 0) {
if (iarg + 1 >= narg)
error->all(FLERR, "pair lambda_input/csp: threshold requires an argument");
cut_csp = utils::numeric(FLERR, arg[iarg + 1], false, lmp);
iarg += 2;
} else if (strcmp(arg[iarg], "N_buffer") == 0) {
if (iarg + 1 >= narg)
error->all(FLERR, "pair lambda_input/csp: N_buffer requires an argument");
nnn_buffer = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
iarg += 2;
} else
error->all(FLERR, "pair_lambda_input_csp: unknown argument {}", arg[iarg]);
}
if (nnn <= 1 || nnn % 2)
error->all(FLERR,
"pair_lambda_input_csp: even number of neighbours > 1 for csp calculation required");
if (cut_csp <= 0) error->all(FLERR, "pair_lambda_input_csp: cut_csp <= 0");
if (nnn_buffer < 0) error->all(FLERR, "pair_lambda_input_csp: N_buffer negative");
cut_global = cut_csp;
cut_csp_sq = cut_csp * cut_csp;
// reset cutoffs that have been explicitly set
if (allocated) {
int i, j;
for (i = 1; i <= atom->ntypes; i++)
for (j = i; j <= atom->ntypes; j++)
if (setflag[i][j]) cut[i][j] = cut_global;
}
}
/**
* Compute CSP and write it to atom->lambda_input.
* Count the number of computations and measure the compute time for
* fix apip_atom_weight.
*/
int PairLambdaInputCsp::calculate_lambda_input()
{
int i, j, k, ii, jj, kk, n, n_cutoff, inum, jnum;
double xtmp, ytmp, ztmp, delx, dely, delz, rsq, value;
int *ilist, *jlist, *numneigh, **firstneigh, *mask;
inum = list->inum;
ilist = list->ilist;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
mask = atom->mask;
// npairs = number of unique pairs
int nhalf = nnn / 2;
int nnn_all = nnn + nnn_buffer;
int npairs = nnn_all * (nnn_all - 1) / 2;
auto pairs = new double[npairs];
double **x = atom->x;
double *lambda_input = atom->lambda_input;
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
if (mask[i] & ignore_group_bit) {
// do not calculate the input as it is not used later
lambda_input[i] = 0;
continue;
}
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
jlist = firstneigh[i];
jnum = numneigh[i];
// ensure distsq and nearest arrays are long enough
if (jnum > maxneigh) {
memory->destroy(distsq);
memory->destroy(nearest);
maxneigh = jnum;
memory->create(distsq, maxneigh, "pair lambda_input/csp:distsq");
memory->create(nearest, maxneigh, "pair lambda_input/csp:nearest");
}
// loop over list of all neighbors within force cutoff
// distsq[] = distance sq to each
// nearest[] = atom indices of neighbors
n_cutoff = 0;
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
rsq = delx * delx + dely * dely + delz * delz;
// do not use cutsq since cutsq may be quite large due to the maximum search of lambda
if (rsq < cut_csp_sq) {
distsq[n_cutoff] = rsq;
nearest[n_cutoff++] = j;
}
}
if (n_cutoff >= nnn_all) {
// calculate the values of the centro symmetry parameter for this atom
// store nnn_all nearest neighs in 1st nnn_all locations of distsq and nearest
select2(nnn_all, n_cutoff, distsq, nearest);
// R = Ri + Rj for each of npairs i,j pairs among nnn_all neighbors
// pairs = squared length of each R
n = 0;
for (j = 0; j < nnn_all; j++) {
jj = nearest[j];
for (k = j + 1; k < nnn_all; k++) {
kk = nearest[k];
delx = x[jj][0] + x[kk][0] - 2.0 * xtmp;
dely = x[jj][1] + x[kk][1] - 2.0 * ytmp;
delz = x[jj][2] + x[kk][2] - 2.0 * ztmp;
pairs[n++] = delx * delx + dely * dely + delz * delz;
}
}
// store nhalf smallest pair distances in 1st nhalf locations of pairs
select(nhalf, npairs, pairs);
// centrosymmetry = sum of nhalf smallest squared values
// calculate centro symmetry parameter of this atom
value = 0.0;
for (j = 0; j < nhalf; j++) value += pairs[j];
} else {
// cannot calculate nnn/2 neighbour pairs
// -> just set a high value
value = 1000.0;
}
// store cs for this atom
lambda_input[i] = value;
}
delete[] pairs;
// return number of calculations
return inum;
}
/* ----------------------------------------------------------------------
2 select routines from Numerical Recipes (slightly modified)
find k smallest values in array of length n
2nd routine sorts auxiliary array at same time
------------------------------------------------------------------------- */
void PairLambdaInputCsp::select(int k, int n, double *arr)
{
int i, ir, j, l, mid;
double a;
arr--;
l = 1;
ir = n;
for (;;) {
if (ir <= l + 1) {
if (ir == l + 1 && arr[ir] < arr[l]) { std::swap(arr[l], arr[ir]); }
return;
} else {
mid = (l + ir) >> 1;
std::swap(arr[mid], arr[l + 1]);
if (arr[l] > arr[ir]) { std::swap(arr[l], arr[ir]); }
if (arr[l + 1] > arr[ir]) { std::swap(arr[l + 1], arr[ir]); }
if (arr[l] > arr[l + 1]) { std::swap(arr[l], arr[l + 1]); }
i = l + 1;
j = ir;
a = arr[l + 1];
for (;;) {
do i++;
while (arr[i] < a);
do j--;
while (arr[j] > a);
if (j < i) break;
std::swap(arr[i], arr[j]);
}
arr[l + 1] = arr[j];
arr[j] = a;
if (j >= k) ir = j - 1;
if (j <= k) l = i;
}
}
}
/* ---------------------------------------------------------------------- */
void PairLambdaInputCsp::select2(int k, int n, double *arr, int *iarr)
{
int i, ir, j, l, mid, ia, itmp;
double a;
arr--;
iarr--;
l = 1;
ir = n;
for (;;) {
if (ir <= l + 1) {
if (ir == l + 1 && arr[ir] < arr[l]) {
std::swap(arr[l], arr[ir]);
std::swap(iarr[l], iarr[ir]);
}
return;
} else {
mid = (l + ir) >> 1;
std::swap(arr[mid], arr[l + 1]);
std::swap(iarr[mid], iarr[l + 1]);
if (arr[l] > arr[ir]) {
std::swap(arr[l], arr[ir]);
std::swap(iarr[l], iarr[ir]);
}
if (arr[l + 1] > arr[ir]) {
std::swap(arr[l + 1], arr[ir]);
std::swap(iarr[l + 1], iarr[ir]);
}
if (arr[l] > arr[l + 1]) {
std::swap(arr[l], arr[l + 1]);
std::swap(iarr[l], iarr[l + 1]);
}
i = l + 1;
j = ir;
a = arr[l + 1];
ia = iarr[l + 1];
for (;;) {
do i++;
while (arr[i] < a);
do j--;
while (arr[j] > a);
if (j < i) break;
std::swap(arr[i], arr[j]);
std::swap(iarr[i], iarr[j]);
}
arr[l + 1] = arr[j];
arr[j] = a;
iarr[l + 1] = iarr[j];
iarr[j] = ia;
if (j >= k) ir = j - 1;
if (j <= k) l = i;
}
}
}

View File

@ -0,0 +1,54 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(lambda_input/csp,PairLambdaInputCsp);
// clang-format on
#else
#ifndef LMP_PAIR_LAMBDA_INPUT_CSP_H
#define LMP_PAIR_LAMBDA_INPUT_CSP_H
#include "pair_lambda_input.h"
namespace LAMMPS_NS {
class PairLambdaInputCsp : virtual public PairLambdaInput {
public:
PairLambdaInputCsp(class LAMMPS *);
~PairLambdaInputCsp();
void settings(int, char **) override;
protected:
// csp variables
double cut_csp_sq; ///< squared cutoff
int maxneigh; ///< number of atoms for which distsq and nearest are allocated
int nnn; ///< number of nearest neighbours that are used in the csp calculation
int nnn_buffer; ///< number of additional (to nnn) stored nearest neighbours
double *distsq; ///< distance sq to each neighbor
int *nearest; ///< atom indices of neighbors
int calculate_lambda_input() override;
void select(int, int, double *);
void select2(int, int, double *, int *);
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,309 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "pair_lambda_zone.h"
#include "atom.h"
#include "error.h"
#include "memory.h"
#include "modify.h"
#include "neigh_list.h"
#include "neighbor.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairLambdaZone::PairLambdaZone(LAMMPS *lmp) :
Pair(lmp), fix_lambda(nullptr), lambda_ta(nullptr), cut(nullptr)
{
// set defaults
cut_global = cut_lo = cut_hi = cut_hi_sq = cut_width = lambda_non_group = -1;
groupbit = -1;
nmax_ta = 0;
timer = 0;
n_calculations = 0;
time_per_atom = -1;
}
/* ---------------------------------------------------------------------- */
PairLambdaZone::~PairLambdaZone()
{
if (copymode) return;
if (allocated) {
memory->destroy(setflag);
memory->destroy(cutsq);
memory->destroy(cut);
}
if (nmax_ta > 0) memory->destroy(lambda_ta);
}
/* ---------------------------------------------------------------------- */
void PairLambdaZone::coeff(int narg, char **arg)
{
if (narg != 2) error->all(FLERR, "Incorrect args for pair coefficients");
if (!allocated) allocate();
int ilo, ihi, jlo, jhi;
utils::bounds(FLERR, arg[0], 1, atom->ntypes, ilo, ihi, error);
utils::bounds(FLERR, arg[1], 1, atom->ntypes, jlo, jhi, error);
int count = 0;
for (int i = ilo; i <= ihi; i++) {
for (int j = MAX(jlo, i); j <= jhi; j++) {
setflag[i][j] = 1;
cut[i][j] = cut_global;
count++;
}
}
if (count == 0) error->all(FLERR, "Incorrect args for pair coefficients");
}
/* ----------------------------------------------------------------------
allocate all arrays
------------------------------------------------------------------------- */
void PairLambdaZone::allocate()
{
allocated = 1;
int n = atom->ntypes + 1;
memory->create(setflag, n, n, "pair:setflag");
for (int i = 1; i < n; i++)
for (int j = i; j < n; j++) setflag[i][j] = 0;
memory->create(cutsq, n, n, "pair:cutsq");
memory->create(cut, n, n, "pair:cut");
}
/* ---------------------------------------------------------------------- */
void PairLambdaZone::compute(int eflag, int vflag)
{
// basic stuff (see pair_zero)
ev_init(eflag, vflag);
if (vflag_fdotr) virial_fdotr_compute();
calculate_lambda();
}
/* ----------------------------------------------------------------------
global settings
------------------------------------------------------------------------- */
void PairLambdaZone::settings(int narg, char **arg)
{
// parse arguments
if (narg != 1) error->all(FLERR, "pair_lambda_zone: expected 1 instead of {} arguments", narg);
cut_global = utils::numeric(FLERR, arg[0], false, lmp);
if (cut_global <= 0) error->all(FLERR, "pair_lambda_zone: cut_global = {} <= 0", cut_global);
// reset cutoffs that have been explicitly set
if (allocated) {
int i, j;
for (i = 1; i <= atom->ntypes; i++)
for (j = i; j <= atom->ntypes; j++)
if (setflag[i][j]) cut[i][j] = cut_global;
}
}
/* ----------------------------------------------------------------------
init specific to this pair style
------------------------------------------------------------------------- */
void PairLambdaZone::init_style()
{
if (!atom->lambda_input_ta_flag)
error->all(FLERR, "pair_lambda_zone requires an atom style with lambda_input_ta");
// find thermo style
int count = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "lambda") == 0) {
fix_lambda = modify->fix[i];
count++;
}
}
if (count != 1) error->all(FLERR, "Exact one fix lambda required");
int dim = 0;
cut_lo = *((double *) fix_lambda->extract("fix_lambda:cut_lo", dim));
cut_hi = *((double *) fix_lambda->extract("fix_lambda:cut_hi", dim));
cut_hi_sq = *((double *) fix_lambda->extract("fix_lambda:cut_hi_sq", dim));
cut_width = *((double *) fix_lambda->extract("fix_lambda:cut_width", dim));
lambda_non_group = *((double *) fix_lambda->extract("fix_lambda:lambda_non_group", dim));
groupbit = fix_lambda->groupbit;
if (cut_hi > cut_global) error->all(FLERR, "The r_lambda_hi > neighbour_list_cutoff.");
neighbor->add_request(this, NeighConst::REQ_FULL | NeighConst::REQ_GHOST);
}
/* ----------------------------------------------------------------------
init for one type pair i,j and corresponding j,i
------------------------------------------------------------------------- */
double PairLambdaZone::init_one(int i, int j)
{
if (setflag[i][j] == 0) { cut[i][j] = mix_distance(cut[i][i], cut[j][j]); }
return cut[i][j];
}
/**
* calculate new lambda with lambda_input_ta of own and ghost atoms.
* Search local maximum of neighbouring atoms for each atom.
* input: lambda_input_ta of own + ghost particles
* output: lambda_input_ta of own particles
*/
void PairLambdaZone::calculate_lambda()
{
double timer_start = platform::walltime();
double xtmp, ytmp, ztmp, delx, dely, delz, lambda_tmp, rsq, lambda_new;
double **x, *lambda_input_ta;
int allnum, ii, i, nlocal, nall, jnum, j, jj, loop_iterations;
int *ilist, *mask, *jlist, *numneigh, **firstneigh;
mask = atom->mask;
x = atom->x;
lambda_input_ta = atom->lambda_input_ta;
nlocal = atom->nlocal;
nall = nlocal + atom->nghost;
if (nlocal > nmax_ta) {
memory->destroy(lambda_ta);
nmax_ta = nlocal;
memory->create(lambda_ta, nmax_ta, "pair/lambda:lambda_ta");
}
// 1 set lambda for own particles
for (i = 0; i < nlocal; i++) {
lambda_ta[i] = (mask[i] & groupbit) ? lambda_input_ta[i] : lambda_non_group;
}
// 2 loop over all atoms with non-simple lambda
loop_iterations = 0;
if (cut_hi_sq > 0) {
ilist = list->ilist;
allnum = list->inum + list->gnum;
numneigh = list->numneigh;
firstneigh = list->firstneigh;
for (ii = 0; ii < allnum; ii++) {
i = ilist[ii];
// skip simple atoms and non-group atoms
// which do not influence the lambda_ta of neighbouring atoms
if (lambda_input_ta[i] == 1 || (!(mask[i] & groupbit))) { continue; }
xtmp = x[i][0];
ytmp = x[i][1];
ztmp = x[i][2];
lambda_tmp = 1 - lambda_input_ta[i];
jlist = firstneigh[i];
jnum = numneigh[i];
loop_iterations++;
// 3 loop over neighbours to set their lambda_ta
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
// it is not required to set lambda_ta of ghosts or non-group atoms
if (j >= nlocal || (!(mask[j] & groupbit))) { continue; }
// the neighbour j is already complex -> skip
if (lambda_ta[j] == 0) { continue; }
delx = xtmp - x[j][0];
dely = ytmp - x[j][1];
delz = ztmp - x[j][2];
rsq = delx * delx + dely * dely + delz * delz;
if (rsq >= cut_hi_sq) { continue; }
lambda_new = 1 - lambda_tmp * switching_function_poly_distance(sqrt(rsq));
// more complex lambda_ta found ? set lambda_ta for ngh
if (lambda_new < lambda_ta[j]) lambda_ta[j] = lambda_new;
}
}
}
// copy calculated lambda max back to lambda_ta
for (i = 0; i < nlocal; i++) lambda_input_ta[i] = lambda_ta[i];
timer += platform::walltime() - timer_start;
n_calculations += loop_iterations;
}
// helper function
// similar to cutoff_func_poly in ace_radial.cpp
// compare Phys Rev Mat 6, 013804 (2022) APPENDIX C: RADIAL AND CUTOFF FUNCTIONS 2. Cutoff function
// the first two derivatives of the switching function lambda vanishes at the boundaries of the switching region
double PairLambdaZone::switching_function_poly_distance(double input)
{
// calculate lambda
if (input <= cut_lo) {
return 1;
} else if (input >= cut_hi) {
return 0;
} else {
double deltatmp = 1 - 2 * (1 + (input - cut_hi) / (cut_width));
return 0.5 + 7.5 / 2. * (deltatmp / 4. - pow(deltatmp, 3) / 6. + pow(deltatmp, 5) / 20.);
}
}
/* ----------------------------------------------------------------------
set return values for timers and counted particles
------------------------------------------------------------------------- */
void PairLambdaZone::calculate_time_per_atom()
{
if (n_calculations > 0)
time_per_atom = timer / n_calculations;
else
time_per_atom = -1;
// reset
timer = 0;
n_calculations = 0;
}
/* ---------------------------------------------------------------------- */
void *PairLambdaZone::extract(const char *str, int &dim)
{
dim = 0;
if (strcmp(str, "lambda_zone:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
return nullptr;
}

View File

@ -0,0 +1,76 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(lambda/zone,PairLambdaZone);
// clang-format on
#else
#ifndef LMP_PAIR_LAMBDA_ZONE_H
#define LMP_PAIR_LAMBDA_ZONE_H
#include "fix.h"
#include "pair.h"
namespace LAMMPS_NS {
class PairLambdaZone : public Pair {
friend class FixLambda;
public:
PairLambdaZone(class LAMMPS *);
~PairLambdaZone() override;
void compute(int, int) override;
void settings(int, char **) override;
void coeff(int, char **) override;
void init_style() override;
double init_one(int, int) override;
void *extract(const char *, int &) override;
protected:
class Fix *fix_lambda;
// pro forma pair style variables
double **cut;
// lambda calculation variables
double timer; ///< accumulated compute time
double time_per_atom; ///< compute time for one atom
int n_calculations; ///< number of accumulated computations
double cut_global; ///< used cutoff
double cut_lo; ///< distance at which the cutoff function of the transition zone decays from 1
double cut_hi; ///< distance at which the cutoff function of the transition zone is 0
double cut_width; ///< cut_hi - cut_lo
double cut_hi_sq; ///< cut_hi_sq * cut_hi_sq
double lambda_non_group; ///< lambda for atoms that are not in the group of this fix
int groupbit; ///< group for which lambda is calculated
// variables for calculation
double *lambda_ta; ///< time averaged lambda input
int nmax_ta; ///< size of lambda_ta
virtual void allocate();
void calculate_time_per_atom();
void calculate_lambda();
double switching_function_poly_distance(double);
};
} // namespace LAMMPS_NS
#endif
#endif

609
src/APIP/pair_pace_apip.cpp Normal file
View File

@ -0,0 +1,609 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/*
This file is a modified version of src/ML-PACE/pair_pace.cpp.
Original copyright:
Copyright 2021 Yury Lysogorskiy^1, Cas van der Oord^2, Anton Bochkarev^1,
Sarath Menon^1, Matteo Rinaldi^1, Thomas Hammerschmidt^1, Matous Mrovec^1,
Aidan Thompson^3, Gabor Csanyi^2, Christoph Ortner^4, Ralf Drautz^1
^1: Ruhr-University Bochum, Bochum, Germany
^2: University of Cambridge, Cambridge, United Kingdom
^3: Sandia National Laboratories, Albuquerque, New Mexico, USA
^4: University of British Columbia, Vancouver, BC, Canada
*/
//
// Originally created by Lysogorskiy Yury on 27.02.20.
// Adaptive precision added by David Immel in 2025
// (d.immel@fz-juelich.de, FZJ, Germany).
//
#include "pair_pace_apip.h"
#include "atom.h"
#include "atom_vec_apip.h"
#include "comm.h"
#include "error.h"
#include "force.h"
#include "math_const.h"
#include "memory.h"
#include "modify.h"
#include "neigh_list.h"
#include "neighbor.h"
#include "update.h"
#include <exception>
#include "ace-evaluator/ace_c_basis.h"
#include "ace-evaluator/ace_evaluator.h"
#include "ace-evaluator/ace_recursive.h"
#include "ace-evaluator/ace_version.h"
#include "ace/ace_b_basis.h"
namespace LAMMPS_NS {
struct ACEImpl {
ACEImpl() : basis_set(nullptr), ace(nullptr) {}
~ACEImpl()
{
delete basis_set;
delete ace;
}
ACECTildeBasisSet *basis_set;
ACERecursiveEvaluator *ace;
};
} // namespace LAMMPS_NS
using namespace LAMMPS_NS;
using namespace MathConst;
static char const *const elements_pace[] = {
"X", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si",
"P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu",
"Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru",
"Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr",
"Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W",
"Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac",
"Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr"};
static constexpr int elements_num_pace = sizeof(elements_pace) / sizeof(const char *);
static int AtomicNumberByName_pace(char *elname)
{
for (int i = 1; i < elements_num_pace; i++)
if (strcmp(elname, elements_pace[i]) == 0) return i;
return -1;
}
/* ---------------------------------------------------------------------- */
PairPACEapip::PairPACEapip(LAMMPS *lmp) : Pair(lmp)
{
single_enable = 0;
restartinfo = 0;
one_coeff = 1;
manybody_flag = 1;
nmax_corerep = 0;
flag_corerep_factor = 0;
corerep_factor = nullptr;
aceimpl = new ACEImpl;
recursive = false;
scale = nullptr;
chunksize = 4096;
// start of adaptive-precision modifications by DI
lambda_thermostat = true;
n_computations_accumulated = 0;
time_wall_accumulated = 0;
time_per_atom = -1;
// end of adaptive-precision modifications by DI
}
/* ----------------------------------------------------------------------
check if allocated, since class can be destructed when incomplete
------------------------------------------------------------------------- */
PairPACEapip::~PairPACEapip()
{
if (copymode) return;
delete aceimpl;
if (allocated) {
memory->destroy(setflag);
memory->destroy(cutsq);
memory->destroy(scale);
memory->destroy(corerep_factor);
}
}
/**
* Set lambda_required based on lambda and lambda_const
* @return true if this calculation is not required
*/
// written by DI. This function is required for the adaptive-precision.
int PairPACEapip::check_abort_condition(double *lambda, double *lambda_const, int *lambda_required,
int i)
{
if ((lambda[i] == 1) && ((!lambda_thermostat) || (lambda_thermostat && lambda_const[i] == 1))) {
lambda_required[i] |= LambdaRequired::NO_COMPLEX;
return 1;
}
lambda_required[i] |= LambdaRequired::COMPLEX;
return 0;
}
/**
* @return prefactor 1-lambda which is used for a precise ACE potential
*/
// written by DI. This function is required for the adaptive-precision.
double PairPACEapip::compute_factor_lambda(double lambda)
{
return 1 - lambda;
}
/**
* @return atom->e_complex which is used for a precise ACE potential
*/
// written by DI. This function is required for the adaptive-precision.
double *PairPACEapip::get_e_ref_ptr()
{
return atom->e_complex;
}
/* ---------------------------------------------------------------------- */
void PairPACEapip::compute(int eflag, int vflag)
{
// start of adaptive-precision modifications by DI
// start timers
double time_wall_start = platform::walltime();
double *lambda = atom->lambda;
int *lambda_required = atom->lambda_required;
double **f_const_lambda = nullptr;
double **f_dyn_lambda = nullptr;
double *e_ref = nullptr;
double *lambda_const = nullptr;
if (lambda_thermostat) {
f_const_lambda = atom->f_const_lambda;
f_dyn_lambda = atom->f_dyn_lambda;
e_ref = get_e_ref_ptr();
lambda_const = atom->lambda_const;
}
int n_computations = 0;
// end of adaptive-precision modifications by DI
int i, j, ii, jj, inum, jnum;
double delx, dely, delz, evdwl;
double fij[3];
int *ilist, *jlist, *numneigh, **firstneigh;
ev_init(eflag, vflag);
double **x = atom->x;
double **f = atom->f;
int *type = atom->type;
// number of atoms in cell
int nlocal = atom->nlocal;
int newton_pair = force->newton_pair;
// inum: length of the neighborlists list
inum = list->inum;
// ilist: list of "i" atoms for which neighbor lists exist
ilist = list->ilist;
//numneigh: the length of each these neigbor list
numneigh = list->numneigh;
// the pointer to the list of neighbors of "i"
firstneigh = list->firstneigh;
if (flag_corerep_factor && atom->nlocal > nmax_corerep) {
memory->destroy(corerep_factor);
nmax_corerep = atom->nlocal;
memory->create(corerep_factor, nmax_corerep, "pace/atom:corerep_factor");
//zeroify array
memset(corerep_factor, 0, nmax_corerep * sizeof(*corerep_factor));
}
//determine the maximum number of neighbours
int max_jnum = 0;
int nei = 0;
for (ii = 0; ii < inum; ii++) {
i = ilist[ii];
jnum = numneigh[i];
nei = nei + jnum;
if (jnum > max_jnum) max_jnum = jnum;
}
aceimpl->ace->resize_neighbours_cache(max_jnum);
//loop over atoms
for (ii = 0; ii < inum; ii++) {
i = list->ilist[ii];
const int itype = type[i];
const double xtmp = x[i][0];
const double ytmp = x[i][1];
const double ztmp = x[i][2];
jlist = firstneigh[i];
jnum = numneigh[i];
// start of adaptive-precision modifications by DI
// Abort calculation when ace is not required for this atom.
// All force and energy contributions calculated in the following are weighted with
// 1-lambda when ace is used as precise potential (and with lambda if ace is used as simple potential).
// As this weighting factor can be 0, i.e. there is no contribution, one can abort the calculation
// of this atom in this case.
if (check_abort_condition(lambda, lambda_const, lambda_required, i)) { continue; }
n_computations++;
// set factor required for forces and for energy summation
// fast potential: lambda
// precise potential: 1-lambda
const double factor_lambda_i = compute_factor_lambda(lambda[i]);
const double factor_lambdaconst_i =
(lambda_thermostat ? compute_factor_lambda(lambda_const[i]) : 0);
// end of adaptive-precision modifications by DI
// checking if neighbours are actually within cutoff range is done inside compute_atom
// mapping from LAMMPS atom types ('type' array) to ACE species is done inside compute_atom
// by using 'aceimpl->ace->element_type_mapping' array
// x: [r0 ,r1, r2, ..., r100]
// i = 0 ,1
// jnum(0) = 50
// jlist(neigh ind of 0-atom) = [1,2,10,7,99,25, .. 50 element in total]
try {
aceimpl->ace->compute_atom(i, x, type, jnum, jlist);
} catch (std::exception &e) {
error->one(FLERR, e.what());
}
if (flag_corerep_factor) corerep_factor[i] = 1 - aceimpl->ace->ace_fcut;
// 'compute_atom' will update the `aceimpl->ace->e_atom` and `aceimpl->ace->neighbours_forces(jj, alpha)` arrays
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
delx = x[j][0] - xtmp;
dely = x[j][1] - ytmp;
delz = x[j][2] - ztmp;
fij[0] = scale[itype][itype] * aceimpl->ace->neighbours_forces(jj, 0);
fij[1] = scale[itype][itype] * aceimpl->ace->neighbours_forces(jj, 1);
fij[2] = scale[itype][itype] * aceimpl->ace->neighbours_forces(jj, 2);
// start of adaptive-precision modifications by DI
// The force contributions fij need to be weighted with 1-lambda[i]
// (or lambda[i] in case of a fast ace potential).
f[i][0] += factor_lambda_i * fij[0];
f[i][1] += factor_lambda_i * fij[1];
f[i][2] += factor_lambda_i * fij[2];
f[j][0] -= factor_lambda_i * fij[0];
f[j][1] -= factor_lambda_i * fij[1];
f[j][2] -= factor_lambda_i * fij[2];
if (lambda_thermostat) {
f_dyn_lambda[i][0] += factor_lambda_i * fij[0];
f_dyn_lambda[i][1] += factor_lambda_i * fij[1];
f_dyn_lambda[i][2] += factor_lambda_i * fij[2];
f_dyn_lambda[j][0] -= factor_lambda_i * fij[0];
f_dyn_lambda[j][1] -= factor_lambda_i * fij[1];
f_dyn_lambda[j][2] -= factor_lambda_i * fij[2];
f_const_lambda[i][0] += factor_lambdaconst_i * fij[0];
f_const_lambda[i][1] += factor_lambdaconst_i * fij[1];
f_const_lambda[i][2] += factor_lambdaconst_i * fij[2];
f_const_lambda[j][0] -= factor_lambdaconst_i * fij[0];
f_const_lambda[j][1] -= factor_lambdaconst_i * fij[1];
f_const_lambda[j][2] -= factor_lambdaconst_i * fij[2];
}
// end of adaptive-precision modifications by DI
// tally per-atom virial contribution
if (vflag_either)
// following line of code modified by DI
ev_tally_xyz(i, j, nlocal, newton_pair, 0.0, 0.0, factor_lambda_i * fij[0],
factor_lambda_i * fij[1], factor_lambda_i * fij[2], -delx, -dely, -delz);
}
// tally energy contribution
// start of adaptive-precision modifications by DI
if (eflag_either || lambda_thermostat) {
// The potential energy needs to be stored to apply the
// energy correction with the local thermostat.
if (e_ref) e_ref[i] = scale[itype][itype] * aceimpl->ace->e_atom;
// evdwl = energy of atom I
// The potential energy is weighted with lambda[i] as well.
evdwl = factor_lambda_i * scale[itype][itype] * aceimpl->ace->e_atom;
// end of adaptive-precision modifications by DI
ev_tally_full(i, 2.0 * evdwl, 0.0, 0.0, 0.0, 0.0, 0.0);
}
}
if (vflag_fdotr) virial_fdotr_compute();
// end modifications YL
// start of adaptive-precision modifications by DI
// stop timers
time_wall_accumulated += platform::walltime() - time_wall_start;
n_computations_accumulated += n_computations;
// end of adaptive-precision modifications by DI
}
/* ---------------------------------------------------------------------- */
void PairPACEapip::allocate()
{
allocated = 1;
int n = atom->ntypes + 1;
memory->create(setflag, n, n, "pair:setflag");
memory->create(cutsq, n, n, "pair:cutsq");
memory->create(scale, n, n, "pair:scale");
map = new int[n];
}
/* ----------------------------------------------------------------------
global settings
------------------------------------------------------------------------- */
void PairPACEapip::settings(int narg, char **arg)
{
if (narg > 3) utils::missing_cmd_args(FLERR, "pair_style pace", error);
// ACE potentials are parameterized in metal units
if (strcmp("metal", update->unit_style) != 0)
error->all(FLERR, "ACE potentials require 'metal' units");
recursive = true; // default evaluator style: RECURSIVE
int iarg = 0;
while (iarg < narg) {
if (strcmp(arg[iarg], "recursive") == 0) {
recursive = true;
iarg += 1;
} else if (strcmp(arg[iarg], "product") == 0) {
recursive = false;
iarg += 1;
} else if (strcmp(arg[iarg], "chunksize") == 0) {
chunksize = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
iarg += 2;
} else
error->all(FLERR, "Unknown pair_style pace keyword: {}", arg[iarg]);
}
if (comm->me == 0) {
utils::logmesg(lmp, "ACE version: {}.{}.{}\n", VERSION_YEAR, VERSION_MONTH, VERSION_DAY);
if (recursive)
utils::logmesg(lmp, "Recursive evaluator is used by ACE\n");
else
utils::logmesg(lmp, "Product evaluator is used by ACE\n");
}
}
/* ----------------------------------------------------------------------
set coeffs for one or more type pairs
------------------------------------------------------------------------- */
void PairPACEapip::coeff(int narg, char **arg)
{
if (!allocated) allocate();
map_element2type(narg - 3, arg + 3);
auto potential_file_name = utils::get_potential_file_path(arg[2]);
//load potential file
delete aceimpl->basis_set;
if (comm->me == 0) utils::logmesg(lmp, "Loading {}\n", potential_file_name);
// if potential is in ACEBBasisSet (YAML) format, then convert to ACECTildeBasisSet automatically
if (utils::strmatch(potential_file_name, ".*\\.yaml$")) {
ACEBBasisSet bBasisSet = ACEBBasisSet(potential_file_name);
ACECTildeBasisSet cTildeBasisSet = bBasisSet.to_ACECTildeBasisSet();
aceimpl->basis_set = new ACECTildeBasisSet(cTildeBasisSet);
} else {
aceimpl->basis_set = new ACECTildeBasisSet(potential_file_name);
}
if (comm->me == 0) {
utils::logmesg(lmp, "Total number of basis functions\n");
for (SPECIES_TYPE mu = 0; mu < aceimpl->basis_set->nelements; mu++) {
int n_r1 = aceimpl->basis_set->total_basis_size_rank1[mu];
int n = aceimpl->basis_set->total_basis_size[mu];
utils::logmesg(lmp, "\t{}: {} (r=1) {} (r>1)\n", aceimpl->basis_set->elements_name[mu], n_r1,
n);
}
}
// read args that map atom types to PACE elements
// map[i] = which element the Ith atom type is, -1 if not mapped
// map[0] is not used
delete aceimpl->ace;
aceimpl->ace = new ACERecursiveEvaluator();
aceimpl->ace->set_recursive(recursive);
aceimpl->ace->element_type_mapping.init(atom->ntypes + 1);
const int n = atom->ntypes;
for (int i = 1; i <= n; i++) {
char *elemname = arg[2 + i];
if (strcmp(elemname, "NULL") == 0) {
// species_type=-1 value will not reach ACE Evaluator::compute_atom,
// but if it will ,then error will be thrown there
aceimpl->ace->element_type_mapping(i) = -1;
map[i] = -1;
if (comm->me == 0) utils::logmesg(lmp, "Skipping LAMMPS atom type #{}(NULL)\n", i);
} else {
int atomic_number = AtomicNumberByName_pace(elemname);
if (atomic_number == -1) error->all(FLERR, "'{}' is not a valid element\n", elemname);
SPECIES_TYPE mu = aceimpl->basis_set->get_species_index_by_name(elemname);
if (mu != -1) {
if (comm->me == 0)
utils::logmesg(lmp, "Mapping LAMMPS atom type #{}({}) -> ACE species type #{}\n", i,
elemname, mu);
map[i] = mu;
// set up LAMMPS atom type to ACE species mapping for ace evaluator
aceimpl->ace->element_type_mapping(i) = mu;
} else {
error->all(FLERR, "Element {} is not supported by ACE-potential from file {}", elemname,
potential_file_name);
}
}
}
// initialize scale factor
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) scale[i][j] = 1.0;
}
aceimpl->ace->set_basis(*aceimpl->basis_set, 1);
}
/* ----------------------------------------------------------------------
init specific to this pair style
------------------------------------------------------------------------- */
void PairPACEapip::init_style()
{
if (atom->tag_enable == 0) error->all(FLERR, "Pair style pace requires atom IDs");
if (force->newton_pair == 0) error->all(FLERR, "Pair style pace requires newton pair on");
// start of adaptive-precision modifications by DI
if (!atom->lambda_required_flag)
error->all(FLERR, "pair style pace/apip requires an atom style with lambda_required.");
if (!atom->lambda_flag)
error->all(FLERR, "Pair style pace/apip requires an atom style with lambda");
// end of adaptive-precision modifications by DI
// request a full neighbor list
neighbor->add_request(this, NeighConst::REQ_FULL);
}
/* ----------------------------------------------------------------------
init for one type pair i,j and corresponding j,i
------------------------------------------------------------------------- */
double PairPACEapip::init_one(int i, int j)
{
if (setflag[i][j] == 0) error->all(FLERR, "All pair coeffs are not set");
//cutoff from the basis set's radial functions settings
scale[j][i] = scale[i][j];
return aceimpl->basis_set->radial_functions->cut(map[i], map[j]);
}
/**
* setup specific to this pair style
* Determine whether there is a fix lambda_thermostat or not and set
* lambda_thermostat.
*/
// written by DI. This function is required for the adaptive-precision.
void PairPACEapip::setup()
{
if (modify->get_fix_by_style("^lambda_thermostat$").size() == 0) {
lambda_thermostat = false;
} else {
lambda_thermostat = true;
if (!atom->lambda_const_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with lambda_const for a local thermostat.");
if (!atom->e_simple_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with e_simple for a local thermostat.");
if (!atom->e_complex_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with e_complex for a local thermostat.");
if (!atom->f_const_lambda_flag)
error->all(FLERR,
"Pair style pace/apip requires an atom style with f_const_lambda for a local "
"thermostat.");
if (!atom->f_dyn_lambda_flag)
error->all(FLERR,
"Pair style pace/apip requires an atom style with f_const_lambda for a local "
"thermostat.");
}
}
/**
* set return values for timers and number of computed particles
*/
// written by DI. This function is required for the adaptive-precision.
void PairPACEapip::calculate_time_per_atom()
{
if (n_computations_accumulated > 0)
time_per_atom = time_wall_accumulated / n_computations_accumulated;
else
time_per_atom = -1;
// reset
time_wall_accumulated = 0;
n_computations_accumulated = 0;
}
/* ----------------------------------------------------------------------
extract method for extracting value of scale variable
---------------------------------------------------------------------- */
void *PairPACEapip::extract(const char *str, int &dim)
{
dim = 0;
//check if str=="corerep_flag" then compute extrapolation grades on this iteration
if (strcmp(str, "corerep_flag") == 0) return (void *) &flag_corerep_factor;
// DI: The following option is required for the adaptive precision.
if (strcmp(str, "pace/apip:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
dim = 2;
if (strcmp(str, "scale") == 0) return (void *) scale;
return nullptr;
}
/* ----------------------------------------------------------------------
peratom requests from FixPair
return ptr to requested data
also return ncol = # of quantites per atom
0 = per-atom vector
1 or more = # of columns in per-atom array
return NULL if str is not recognized
---------------------------------------------------------------------- */
void *PairPACEapip::extract_peratom(const char *str, int &ncol)
{
if (strcmp(str, "corerep") == 0) {
ncol = 0;
return (void *) corerep_factor;
}
return nullptr;
}

90
src/APIP/pair_pace_apip.h Normal file
View File

@ -0,0 +1,90 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
This software is distributed under the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/*
This file is a modified version of src/ML-PACE/pair_pace.h.
Original copyright:
Copyright 2021 Yury Lysogorskiy^1, Cas van der Oord^2, Anton Bochkarev^1,
Sarath Menon^1, Matteo Rinaldi^1, Thomas Hammerschmidt^1, Matous Mrovec^1,
Aidan Thompson^3, Gabor Csanyi^2, Christoph Ortner^4, Ralf Drautz^1
^1: Ruhr-University Bochum, Bochum, Germany
^2: University of Cambridge, Cambridge, United Kingdom
^3: Sandia National Laboratories, Albuquerque, New Mexico, USA
^4: University of British Columbia, Vancouver, BC, Canada
*/
//
// Originally created by Lysogorskiy Yury on 27.02.20.
// Adaptive precision added by David Immel in 2025
// (d.immel@fz-juelich.de, FZJ, Germany).
//
#ifdef PAIR_CLASS
// clang-format off
PairStyle(pace/apip,PairPACEapip);
// clang-format on
#else
#ifndef LMP_PAIR_PACE_APIP_H
#define LMP_PAIR_PACE_APIP_H
#include "pair.h"
namespace LAMMPS_NS {
class PairPACEapip : public Pair {
public:
PairPACEapip(class LAMMPS *);
~PairPACEapip() override;
void compute(int, int) override;
void settings(int, char **) override;
void coeff(int, char **) override;
void init_style() override;
double init_one(int, int) override;
void setup() override;
void *extract(const char *, int &) override;
void *extract_peratom(const char *, int &) override;
protected:
struct ACEImpl *aceimpl;
int nmax_corerep;
virtual void allocate();
double *corerep_factor; //per-atom core-rep factor (= 1 - fcut)
int flag_corerep_factor;
double **scale;
bool recursive; // "recursive" option for ACERecursiveEvaluator
int chunksize;
// start of adaptive-precision modifications by DI
virtual double *get_e_ref_ptr();
virtual double compute_factor_lambda(double);
virtual int check_abort_condition(double *, double *, int *, int);
bool lambda_thermostat; // true/false there is one/no fix lambda_thermostat
void calculate_time_per_atom();
// stats required for load balancing
int n_computations_accumulated; // number of accumulated computations
double time_wall_accumulated; // accumulated compute time
double time_per_atom; // average time of one computation
// end of adaptive-precision modifications by DI
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,73 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "pair_pace_apip_fast.h"
#include "atom.h"
#include "atom_vec_apip.h"
using namespace LAMMPS_NS;
PairPACEapipFast::PairPACEapipFast(LAMMPS *lmp) : PairPACEapip(lmp) {}
/**
* Set lambda_required based on lambda and lambda_const
* @return true if this calculation is not required
*/
int PairPACEapipFast::check_abort_condition(double *lambda, double *lambda_const,
int *lambda_required, int i)
{
if ((lambda[i] == 0) && ((!lambda_thermostat) || (lambda_thermostat && lambda_const[i] == 0))) {
lambda_required[i] |= LambdaRequired::NO_SIMPLE;
return 1;
}
lambda_required[i] |= LambdaRequired::SIMPLE;
return 0;
}
/**
* @return prefactor lambda which is used for a fast ACE potential
*/
double PairPACEapipFast::compute_factor_lambda(double lambda)
{
return lambda;
}
/**
* @return atom->e_simple which is used for a fast ACE potential
*/
double *PairPACEapipFast::get_e_ref_ptr()
{
return atom->e_simple;
}
/* ----------------------------------------------------------------------
extract method for extracting value of scale variable
---------------------------------------------------------------------- */
void *PairPACEapipFast::extract(const char *str, int &dim)
{
dim = 2;
if (strcmp(str, "scale") == 0) return (void *) scale;
dim = 0;
if (strcmp(str, "pace/apip/fast:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
return nullptr;
}

View File

@ -0,0 +1,44 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(pace/apip/fast,PairPACEapipFast);
// clang-format on
#else
#ifndef LMP_PAIR_PACE_APIP_FAST_H
#define LMP_PAIR_PACE_APIP_FAST_H
#include "pair_pace_apip.h"
namespace LAMMPS_NS {
class PairPACEapipFast : public PairPACEapip {
public:
PairPACEapipFast(class LAMMPS *);
void *extract(const char *, int &) override;
protected:
double *get_e_ref_ptr() override;
double compute_factor_lambda(double) override;
int check_abort_condition(double *, double *, int *, int) override;
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,73 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#include "pair_pace_apip_precise.h"
#include "atom.h"
#include "atom_vec_apip.h"
using namespace LAMMPS_NS;
PairPACEapipPrecise::PairPACEapipPrecise(LAMMPS *lmp) : PairPACEapip(lmp) {}
/**
* Set lambda_required based on lambda and lambda_const
* @return true if this calculation is not required
*/
int PairPACEapipPrecise::check_abort_condition(double *lambda, double *lambda_const,
int *lambda_required, int i)
{
if ((lambda[i] == 1) && ((!lambda_thermostat) || (lambda_thermostat && lambda_const[i] == 1))) {
lambda_required[i] |= LambdaRequired::NO_COMPLEX;
return 1;
}
lambda_required[i] |= LambdaRequired::COMPLEX;
return 0;
}
/**
* @return prefactor 1-lambda which is used for a precise ACE potential
*/
double PairPACEapipPrecise::compute_factor_lambda(double lambda)
{
return 1 - lambda;
}
/**
* @return atom->e_complex which is used for a precise ACE potential
*/
double *PairPACEapipPrecise::get_e_ref_ptr()
{
return atom->e_complex;
}
/* ----------------------------------------------------------------------
extract method for extracting value of scale variable
---------------------------------------------------------------------- */
void *PairPACEapipPrecise::extract(const char *str, int &dim)
{
dim = 2;
if (strcmp(str, "scale") == 0) return (void *) scale;
dim = 0;
if (strcmp(str, "pace/apip:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
return nullptr;
}

View File

@ -0,0 +1,44 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: David Immel (d.immel@fz-juelich.de, FZJ, Germany)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(pace/apip/precise,PairPACEapipPrecise);
// clang-format on
#else
#ifndef LMP_PAIR_PACE_APIP_PRECISE_H
#define LMP_PAIR_PACE_APIP_PRECISE_H
#include "pair_pace_apip.h"
namespace LAMMPS_NS {
class PairPACEapipPrecise : public PairPACEapip {
public:
PairPACEapipPrecise(class LAMMPS *);
void *extract(const char *, int &) override;
protected:
double *get_e_ref_ptr() override;
double compute_factor_lambda(double) override;
int check_abort_condition(double *, double *, int *, int) override;
};
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -32,6 +32,7 @@ for file in *.cpp *.h; do
test -f ${file} && action $file test -f ${file} && action $file
done done
# edit 2 Makefile.package files to include/exclude package info # edit 2 Makefile.package files to include/exclude package info
if (test $1 = 1) then if (test $1 = 1) then
@ -53,6 +54,12 @@ include ..\/..\/lib\/pace\/Makefile.lammps
elif (test $1 = 0) then elif (test $1 = 0) then
# The APIP package depends also on the pace library.
# Remove in Makefiles only if there are no remaining includes from APIP.
# grep searches also in kspace.cpp and thus returns no error.
if (test $(grep '#include "ace' ../*pace*.cpp | wc -l) = 0) then
# update Makefiles
if (test -e ../Makefile.package) then if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*pace[^ \t]* //' ../Makefile.package sed -i -e 's/[^ \t]*pace[^ \t]* //' ../Makefile.package
fi fi
@ -62,3 +69,5 @@ elif (test $1 = 0) then
fi fi
fi fi
fi

View File

@ -59,6 +59,7 @@ PACKAGE = \
kspace \ kspace \
adios \ adios \
amoeba \ amoeba \
apip \
asphere \ asphere \
awpmd \ awpmd \
bocs \ bocs \
@ -225,6 +226,7 @@ PACKLIB = \
python \ python \
voronoi \ voronoi \
adios \ adios \
apip \
atc \ atc \
awpmd \ awpmd \
colvars \ colvars \
@ -252,6 +254,7 @@ PACKINT = atc awpmd colvars electrode gpu kokkos lepton ml-pod poems
PACKEXT = \ PACKEXT = \
adios \ adios \
apip \
h5md \ h5md \
kim \ kim \
machdyn \ machdyn \

View File

@ -220,6 +220,11 @@ Atom::Atom(LAMMPS *_lmp) : Pointers(_lmp), atom_style(nullptr), avec(nullptr), a
area = ed = em = epsilon = curvature = q_scaled = nullptr; area = ed = em = epsilon = curvature = q_scaled = nullptr;
// APIP package
lambda_const = lambda = lambda_input = lambda_input_ta = e_simple = e_complex = nullptr;
lambda_required = nullptr;
f_const_lambda = f_dyn_lambda = nullptr;
// end of customization section // end of customization section
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -575,6 +580,17 @@ void Atom::peratom_create()
add_peratom("curvature",&curvature,DOUBLE,0); add_peratom("curvature",&curvature,DOUBLE,0);
add_peratom("q_scaled",&q_scaled,DOUBLE,0); add_peratom("q_scaled",&q_scaled,DOUBLE,0);
// APIP package
add_peratom("lambda",&lambda,DOUBLE,0);
add_peratom("lambda_required",&lambda_required,INT,0);
add_peratom("lambda_input",&lambda_input,DOUBLE,0);
add_peratom("lambda_input_ta",&lambda_input_ta,DOUBLE,0);
add_peratom("e_simple",&e_simple,DOUBLE,0);
add_peratom("e_complex",&e_complex,DOUBLE,0);
add_peratom("lambda_const",&lambda_const,DOUBLE,0);
add_peratom("f_const_lambda",&f_const_lambda,DOUBLE,3,1);
add_peratom("f_dyn_lambda",&f_dyn_lambda,DOUBLE,3,1);
// end of customization section // end of customization section
// -------------------------------------------------------------------- // --------------------------------------------------------------------
} }
@ -658,6 +674,7 @@ void Atom::set_atomflag_defaults()
contact_radius_flag = smd_data_9_flag = smd_stress_flag = 0; contact_radius_flag = smd_data_9_flag = smd_stress_flag = 0;
eff_plastic_strain_flag = eff_plastic_strain_rate_flag = 0; eff_plastic_strain_flag = eff_plastic_strain_rate_flag = 0;
nspecial15_flag = 0; nspecial15_flag = 0;
lambda_flag = e_simple_flag = e_complex_flag = lambda_input_flag = lambda_input_ta_flag = lambda_required_flag = f_const_lambda_flag = f_dyn_lambda_flag = lambda_const_flag = 0;
pdscale = 1.0; pdscale = 1.0;
} }
@ -3116,6 +3133,18 @@ void *Atom::extract(const char *name)
if (strcmp(name,"curvature") == 0) return (void *) curvature; if (strcmp(name,"curvature") == 0) return (void *) curvature;
if (strcmp(name,"q_scaled") == 0) return (void *) q_scaled; if (strcmp(name,"q_scaled") == 0) return (void *) q_scaled;
// APIP package
if (strcmp(name,"lambda") == 0) return (void *) lambda;
if (strcmp(name,"lambda_required") == 0) return (void *) lambda_required;
if (strcmp(name,"lambda_input") == 0) return (void *) lambda_input;
if (strcmp(name,"lambda_input_ta") == 0) return (void *) lambda_input_ta;
if (strcmp(name,"e_simple") == 0) return (void *) e_simple;
if (strcmp(name,"e_complex") == 0) return (void *) e_complex;
if (strcmp(name,"f_const_lambda") == 0) return (void *) f_const_lambda;
if (strcmp(name,"f_dyn_lambda") == 0) return (void *) f_dyn_lambda;
if (strcmp(name,"lambda_const") == 0) return (void *) lambda_const;
// end of customization section // end of customization section
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -3247,6 +3276,17 @@ int Atom::extract_datatype(const char *name)
if (strcmp(name,"curvature") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"curvature") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"q_unscaled") == 0) return LAMMPS_DOUBLE; if (strcmp(name,"q_unscaled") == 0) return LAMMPS_DOUBLE;
// PACE package
if (strcmp(name,"lambda") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"lambda_required") == 0) return LAMMPS_INT;
if (strcmp(name,"lambda_input") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"lambda_input_ta") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"e_simple") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"e_complex") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"lambda_const") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"f_const_lambda") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"f_dyn_lambda") == 0) return LAMMPS_DOUBLE_2D;
// end of customization section // end of customization section
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -3383,6 +3423,18 @@ int Atom::extract_size(const char *name, int type)
if (strcmp(name, "smd_data_9") == 0) return 9; if (strcmp(name, "smd_data_9") == 0) return 9;
if (strcmp(name, "smd_stress") == 0) return 6; if (strcmp(name, "smd_stress") == 0) return 6;
// APIP package
if (strcmp(name, "lambda") == 0) return nlocal;
if (strcmp(name, "lambda_required") == 0) return nlocal;
if (strcmp(name, "lambda_input") == 0) return nlocal;
if (strcmp(name, "lambda_input_ta") == 0) return nlocal;
if (strcmp(name, "e_simple") == 0) return nlocal;
if (strcmp(name, "e_complex") == 0) return nlocal;
if (strcmp(name, "lambda_const") == 0) return nlocal;
if (strcmp(name, "f_const_lambda") == 0) return nall;
if (strcmp(name, "f_dyn_lambda") == 0) return nall;
} }
// custom arrays // custom arrays

View File

@ -177,6 +177,11 @@ class Atom : protected Pointers {
double *area, *ed, *em, *epsilon, *curvature, *q_scaled; double *area, *ed, *em, *epsilon, *curvature, *q_scaled;
// APIP package
double *lambda, *lambda_input, *lambda_input_ta, *e_simple, *e_complex, **f_const_lambda, **f_dyn_lambda, *lambda_const;
int *lambda_required;
// end of customization section // end of customization section
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -225,6 +230,10 @@ class Atom : protected Pointers {
int dielectric_flag; int dielectric_flag;
// APIP package
int lambda_flag, e_simple_flag, e_complex_flag, lambda_input_flag, lambda_input_ta_flag, lambda_required_flag, f_const_lambda_flag, f_dyn_lambda_flag, lambda_const_flag;
// end of customization section // end of customization section
// -------------------------------------------------------------------- // --------------------------------------------------------------------

96
src/atom_vec_apip.cpp Normal file
View File

@ -0,0 +1,96 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#include "atom_vec_apip.h"
#include "atom.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
AtomVecApip::AtomVecApip(LAMMPS *lmp) : AtomVec(lmp)
{
molecular = Atom::ATOMIC;
mass_type = PER_TYPE;
forceclearflag = 1;
double *lambda, *lambda_input, *lambda_input_ta, *lambda_const, *e_simple, *e_complex,
**f_const_lambda, **f_dyn_lambda;
int *lambda_required;
atom->lambda_flag = 1;
atom->lambda_input_flag = 1;
atom->lambda_input_ta_flag = 1;
atom->lambda_const_flag = 1;
atom->lambda_required_flag = 1;
atom->e_simple_flag = 1;
atom->e_complex_flag = 1;
atom->f_const_lambda_flag = 1;
atom->f_dyn_lambda_flag = 1;
// strings with peratom variables to include in each AtomVec method
// strings cannot contain fields in corresponding AtomVec default strings
// order of fields in a string does not matter
// except: fields_data_atom & fields_data_vel must match data file
// The full list of fields is in atom_vec.cpp
fields_copy = {"lambda", "lambda_required", "lambda_input", "lambda_input_ta", "lambda_const"};
fields_comm = {"lambda", "lambda_required", "lambda_input_ta", "lambda_const"};
fields_comm_vel = {};
fields_border = {"lambda", "lambda_required", "lambda_input_ta", "lambda_const"};
fields_border_vel = {};
fields_exchange = {"lambda", "lambda_required", "lambda_input_ta", "lambda_const"};
fields_restart = {"lambda", "lambda_required", "lambda_input", "lambda_input_ta", "lambda_const"};
fields_create = {};
fields_grow = {
"lambda", "lambda_required", "lambda_input", "lambda_input_ta", "lambda_const",
"e_simple", "e_complex", "f_const_lambda", "f_dyn_lambda"}; // allocates memory
fields_reverse = {"f_const_lambda",
"f_dyn_lambda"}; // communication of force after calculation
fields_data_atom = {"id", "type", "x"};
fields_data_vel = {"id", "v"};
setup_fields();
}
/* ----------------------------------------------------------------------
set local copies of all grow ptrs used by this class, except defaults
needed in replicate when 2 atom classes exist and it calls pack_restart()
------------------------------------------------------------------------- */
void AtomVecApip::grow_pointers()
{
lambda = atom->lambda;
lambda_required = atom->lambda_required;
lambda_input = atom->lambda_input;
lambda_input_ta = atom->lambda_input_ta;
lambda_const = atom->lambda_const;
e_simple = atom->e_simple;
e_complex = atom->e_complex;
f_const_lambda = atom->f_const_lambda;
f_dyn_lambda = atom->f_dyn_lambda;
}
/* ----------------------------------------------------------------------
clear extra forces starting at atom n
natoms = # of atoms to clear
nbytes = natoms * sizeof(double)
requires forceclearflag = 1 to be called
------------------------------------------------------------------------- */
void AtomVecApip::force_clear(int n, size_t nbytes)
{
memset(&f_const_lambda[n][0], 0, 3 * nbytes);
memset(&f_dyn_lambda[n][0], 0, 3 * nbytes);
memset(&lambda_required[n], 0, nbytes / sizeof(double) * sizeof(int));
}

58
src/atom_vec_apip.h Normal file
View File

@ -0,0 +1,58 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
LAMMPS development team: developers@lammps.org
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
#ifdef ATOM_CLASS
// clang-format off
AtomStyle(apip,AtomVecApip);
// clang-format on
#else
#ifndef LMP_ATOM_VEC_APIP_H
#define LMP_ATOM_VEC_APIP_H
#include "atom_vec.h"
namespace LAMMPS_NS {
class AtomVecApip : public AtomVec {
public:
AtomVecApip(class LAMMPS *);
void grow_pointers();
void force_clear(int, size_t) override;
protected:
double *lambda, *lambda_input, *lambda_const, *lambda_input_ta, *e_simple, *e_complex,
**f_const_lambda, **f_dyn_lambda;
int *lambda_required;
};
#ifndef LMP_ATOM_LAMBDA_REQUIRED
#define LMP_ATOM_LAMBDA_REQUIRED
namespace LambdaRequired {
enum {
UNKNOWN = 0,
SIMPLE = 1 << 0,
NO_SIMPLE = 1 << 1,
COMPLEX = 1 << 2,
NO_COMPLEX = 1 << 3,
};
} // namespace LambdaRequired
#endif
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -331,6 +331,27 @@ ComputePropertyAtom::ComputePropertyAtom(LAMMPS *lmp, int narg, char **arg) :
error->all(FLERR,"Compute property/atom {} requires atom style tri", arg[iarg]); error->all(FLERR,"Compute property/atom {} requires atom style tri", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_corner3z; pack_choice[i] = &ComputePropertyAtom::pack_corner3z;
} else if (strcmp(arg[iarg],"lambda") == 0) {
if (!atom->lambda_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_lambda;
} else if (strcmp(arg[iarg],"lambda_input") == 0) {
if (!atom->lambda_input_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_lambda_input;
} else if (strcmp(arg[iarg],"lambda_required") == 0) {
if (!atom->lambda_required_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_lambda_required;
} else if (strcmp(arg[iarg],"e_simple") == 0) {
if (!atom->e_simple_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_e_simple;
} else if (strcmp(arg[iarg],"e_complex") == 0) {
if (!atom->e_complex_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_e_complex;
// custom per-atom vector or array // custom per-atom vector or array
} else if (utils::strmatch(arg[iarg],"^[id]2?_")) { } else if (utils::strmatch(arg[iarg],"^[id]2?_")) {
@ -1564,6 +1585,81 @@ void ComputePropertyAtom::pack_tqz(int n)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_lambda(int n)
{
double *lambda = atom->lambda;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) buf[n] = lambda[i];
else buf[n] = 0.0;
n += nvalues;
}
}
/* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_lambda_input(int n)
{
double *lambda_input = atom->lambda_input;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) buf[n] = lambda_input[i];
else buf[n] = 0.0;
n += nvalues;
}
}
/* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_lambda_required(int n)
{
int *lambda_required = atom->lambda_required;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) buf[n] = lambda_required[i];
else buf[n] = 0.0;
n += nvalues;
}
}
/* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_e_simple(int n)
{
double *e_simple = atom->e_simple;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) buf[n] = e_simple[i];
else buf[n] = 0.0;
n += nvalues;
}
}
/* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_e_complex(int n)
{
double *e_complex = atom->e_complex;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) buf[n] = e_complex[i];
else buf[n] = 0.0;
n += nvalues;
}
}
/* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_end1x(int n) void ComputePropertyAtom::pack_end1x(int n)
{ {
AtomVecLine::Bonus *bonus = avec_line->bonus; AtomVecLine::Bonus *bonus = avec_line->bonus;

View File

@ -137,6 +137,12 @@ class ComputePropertyAtom : public Compute {
void pack_d2name(int); void pack_d2name(int);
void pack_atom_style(int); void pack_atom_style(int);
void pack_lambda(int);
void pack_lambda_required(int);
void pack_lambda_input(int);
void pack_e_simple(int);
void pack_e_complex(int);
}; };
} // namespace LAMMPS_NS } // namespace LAMMPS_NS

View File

@ -45,6 +45,7 @@ enum{ID,MOL,PROC,PROCP1,TYPE,TYPELABEL,ELEMENT,MASS,
Q,MUX,MUY,MUZ,MU,RADIUS,DIAMETER, Q,MUX,MUY,MUZ,MU,RADIUS,DIAMETER,
OMEGAX,OMEGAY,OMEGAZ,ANGMOMX,ANGMOMY,ANGMOMZ, OMEGAX,OMEGAY,OMEGAZ,ANGMOMX,ANGMOMY,ANGMOMZ,
TQX,TQY,TQZ, TQX,TQY,TQZ,
LAMBDA,LAMBDA_INPUT,LAMBDA_REQUIRED,ESIMPLE,ECOMPLEX, // APIP package
COMPUTE,FIX,VARIABLE,IVEC,DVEC,IARRAY,DARRAY}; COMPUTE,FIX,VARIABLE,IVEC,DVEC,IARRAY,DARRAY};
enum{LT,LE,GT,GE,EQ,NEQ,XOR}; enum{LT,LE,GT,GE,EQ,NEQ,XOR};
@ -1223,6 +1224,25 @@ int DumpCustom::count()
double **darray = atom->darray[iwhich]; double **darray = atom->darray[iwhich];
ptr = &darray[0][argindex[i]-1]; ptr = &darray[0][argindex[i]-1];
nstride = atom->dcols[iwhich]; nstride = atom->dcols[iwhich];
// APIP package
} else if (thresh_array[ithresh] == LAMBDA) {
ptr = &atom->lambda[0];
nstride = 1;
} else if (thresh_array[ithresh] == LAMBDA_INPUT) {
ptr = &atom->lambda_input[0];
nstride = 1;
} else if (thresh_array[ithresh] == LAMBDA_REQUIRED) {
int *lambda_required = atom->lambda_required;
for (i = 0; i < nlocal; i++) dchoose[i] = lambda_required[i];
ptr = dchoose;
nstride = 1;
} else if (thresh_array[ithresh] == ESIMPLE) {
ptr = &atom->e_simple[0];
nstride = 1;
} else if (thresh_array[ithresh] == ECOMPLEX) {
ptr = &atom->e_complex[0];
nstride = 1;
} }
// unselect atoms that don't meet threshold criterion // unselect atoms that don't meet threshold criterion
@ -1624,6 +1644,33 @@ int DumpCustom::parse_fields(int narg, char **arg)
pack_choice[iarg] = &DumpCustom::pack_tqz; pack_choice[iarg] = &DumpCustom::pack_tqz;
vtype[iarg] = Dump::DOUBLE; vtype[iarg] = Dump::DOUBLE;
// APIP package
} else if (strcmp(arg[iarg],"lambda") == 0) {
if (!atom->lambda_flag)
error->all(FLERR,"Dumping an atom property that isn't allocated");
pack_choice[iarg] = &DumpCustom::pack_lambda;
vtype[iarg] = Dump::DOUBLE;
} else if (strcmp(arg[iarg],"lambda_input") == 0) {
if (!atom->lambda_input_flag)
error->all(FLERR,"Dumping an atom property that isn't allocated");
pack_choice[iarg] = &DumpCustom::pack_lambda_input;
vtype[iarg] = Dump::DOUBLE;
} else if (strcmp(arg[iarg],"lambda_required") == 0) {
if (!atom->lambda_required_flag)
error->all(FLERR,"Dumping an atom property that isn't allocated");
pack_choice[iarg] = &DumpCustom::pack_lambda_required;
vtype[iarg] = Dump::INT;
} else if (strcmp(arg[iarg],"e_simple") == 0) {
if (!atom->e_simple_flag)
error->all(FLERR,"Dumping an atom property that isn't allocated");
pack_choice[iarg] = &DumpCustom::pack_e_simple;
vtype[iarg] = Dump::DOUBLE;
} else if (strcmp(arg[iarg],"e_complex") == 0) {
if (!atom->e_complex_flag)
error->all(FLERR,"Dumping an atom property that isn't allocated");
pack_choice[iarg] = &DumpCustom::pack_e_complex;
vtype[iarg] = Dump::DOUBLE;
// compute or fix or variable or custom vector/array // compute or fix or variable or custom vector/array
} else { } else {
@ -2067,6 +2114,13 @@ int DumpCustom::modify_param(int narg, char **arg)
else if (strcmp(arg[1],"tqy") == 0) thresh_array[nthresh] = TQY; else if (strcmp(arg[1],"tqy") == 0) thresh_array[nthresh] = TQY;
else if (strcmp(arg[1],"tqz") == 0) thresh_array[nthresh] = TQZ; else if (strcmp(arg[1],"tqz") == 0) thresh_array[nthresh] = TQZ;
// APIP package
else if (strcmp(arg[1],"lambda") == 0) thresh_array[nthresh] = LAMBDA;
else if (strcmp(arg[1],"lambda_input") == 0) thresh_array[nthresh] = LAMBDA_INPUT;
else if (strcmp(arg[1],"lambda_required") == 0) thresh_array[nthresh] = LAMBDA_REQUIRED;
else if (strcmp(arg[1],"e_simple") == 0) thresh_array[nthresh] = ESIMPLE;
else if (strcmp(arg[1],"e_complex") == 0) thresh_array[nthresh] = ECOMPLEX;
// compute or fix or variable or custom vector/array // compute or fix or variable or custom vector/array
// must grow field2index and argindex arrays, since access is beyond nfield // must grow field2index and argindex arrays, since access is beyond nfield
@ -3448,3 +3502,63 @@ void DumpCustom::pack_tqz_triclinic_general(int n)
n += size_one; n += size_one;
} }
} }
/* ---------------------------------------------------------------------- */
void DumpCustom::pack_lambda(int n)
{
double *lambda = atom->lambda;
for (int i = 0; i < nchoose; i++) {
buf[n] = lambda[clist[i]];
n += size_one;
}
}
/* ---------------------------------------------------------------------- */
void DumpCustom::pack_lambda_input(int n)
{
double *lambda_input = atom->lambda_input;
for (int i = 0; i < nchoose; i++) {
buf[n] = lambda_input[clist[i]];
n += size_one;
}
}
/* ---------------------------------------------------------------------- */
void DumpCustom::pack_lambda_required(int n)
{
int *lambda_required = atom->lambda_required;
for (int i = 0; i < nchoose; i++) {
buf[n] = lambda_required[clist[i]];
n += size_one;
}
}
/* ---------------------------------------------------------------------- */
void DumpCustom::pack_e_simple(int n)
{
double *e_simple = atom->e_simple;
for (int i = 0; i < nchoose; i++) {
buf[n] = e_simple[clist[i]];
n += size_one;
}
}
/* ---------------------------------------------------------------------- */
void DumpCustom::pack_e_complex(int n)
{
double *e_complex = atom->e_complex;
for (int i = 0; i < nchoose; i++) {
buf[n] = e_complex[clist[i]];
n += size_one;
}
}

View File

@ -235,6 +235,12 @@ class DumpCustom : public Dump {
void pack_tqx_triclinic_general(int); void pack_tqx_triclinic_general(int);
void pack_tqy_triclinic_general(int); void pack_tqy_triclinic_general(int);
void pack_tqz_triclinic_general(int); void pack_tqz_triclinic_general(int);
void pack_lambda(int);
void pack_lambda_input(int);
void pack_lambda_required(int);
void pack_e_simple(int);
void pack_e_complex(int);
}; };
} // namespace LAMMPS_NS } // namespace LAMMPS_NS

View File

@ -141,6 +141,11 @@ FixStoreState::FixStoreState(LAMMPS *lmp, int narg, char **arg) :
error->all(FLERR, "Cannot use fix store/state {} for atom style {}", error->all(FLERR, "Cannot use fix store/state {} for atom style {}",
arg[iarg], atom->get_style()); arg[iarg], atom->get_style());
val.pack_choice = &FixStoreState::pack_q; val.pack_choice = &FixStoreState::pack_q;
} else if (strcmp(arg[iarg],"lambda") == 0) {
if (!atom->lambda_flag)
error->all(FLERR, "Cannot use fix store/state {} for atom style {}",
arg[iarg], atom->get_style());
val.pack_choice = &FixStoreState::pack_lambda;
} else if (strcmp(arg[iarg],"mux") == 0) { } else if (strcmp(arg[iarg],"mux") == 0) {
if (!atom->mu_flag) if (!atom->mu_flag)
error->all(FLERR, "Cannot use fix store/state {} for atom style {}", error->all(FLERR, "Cannot use fix store/state {} for atom style {}",
@ -1269,6 +1274,21 @@ void FixStoreState::pack_q(int n)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
void FixStoreState::pack_lambda(int n)
{
double *lambda = atom->lambda;
int *mask = atom->mask;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) vbuf[n] = lambda[i];
else vbuf[n] = 0.0;
n += values.size();
}
}
/* ---------------------------------------------------------------------- */
void FixStoreState::pack_mux(int n) void FixStoreState::pack_mux(int n)
{ {
double **mu = atom->mu; double **mu = atom->mu;

View File

@ -106,6 +106,7 @@ class FixStoreState : public Fix {
void pack_fy(int); void pack_fy(int);
void pack_fz(int); void pack_fz(int);
void pack_q(int); void pack_q(int);
void pack_lambda(int);
void pack_mux(int); void pack_mux(int);
void pack_muy(int); void pack_muy(int);
void pack_muz(int); void pack_muz(int);

View File

@ -813,6 +813,7 @@ void ReadDump::process_atoms()
double **x = atom->x; double **x = atom->x;
double **v = atom->v; double **v = atom->v;
double *q = atom->q; double *q = atom->q;
double *lambda = atom->lambda;
double **f = atom->f; double **f = atom->f;
tagint *tag = atom->tag; tagint *tag = atom->tag;
imageint *image = atom->image; imageint *image = atom->image;
@ -862,6 +863,9 @@ void ReadDump::process_atoms()
case Reader::Q: case Reader::Q:
q[m] = fields[i][ifield]; q[m] = fields[i][ifield];
break; break;
case Reader::LAMBDA:
lambda[m] = fields[i][ifield];
break;
case Reader::VY: case Reader::VY:
v[m][1] = fields[i][ifield]; v[m][1] = fields[i][ifield];
break; break;
@ -977,6 +981,7 @@ void ReadDump::process_atoms()
tag = atom->tag; tag = atom->tag;
v = atom->v; v = atom->v;
q = atom->q; q = atom->q;
lambda = atom->lambda;
image = atom->image; image = atom->image;
// set atom attributes from other dump file fields // set atom attributes from other dump file fields
@ -1001,6 +1006,9 @@ void ReadDump::process_atoms()
case Reader::Q: case Reader::Q:
q[m] = fields[i][ifield]; q[m] = fields[i][ifield];
break; break;
case Reader::LAMBDA:
lambda[m] = fields[i][ifield];
break;
case Reader::IX: case Reader::IX:
xbox = static_cast<int> (fields[i][ifield]); xbox = static_cast<int> (fields[i][ifield]);
break; break;
@ -1163,6 +1171,8 @@ int ReadDump::fields_and_keywords(int narg, char **arg)
if (type < 0) break; if (type < 0) break;
if (type == Reader::Q && !atom->q_flag) if (type == Reader::Q && !atom->q_flag)
error->all(FLERR,"Read dump of charge property that isn't supported by atom style"); error->all(FLERR,"Read dump of charge property that isn't supported by atom style");
if (type == Reader::LAMBDA && !atom->lambda_flag)
error->all(FLERR,"Read dump of lambda property that isn't supported by atom style");
fieldtype[nfield++] = type; fieldtype[nfield++] = type;
iarg++; iarg++;
} }
@ -1288,6 +1298,7 @@ int ReadDump::whichtype(char *str)
else if (strcmp(str,"vy") == 0) type = Reader::VY; else if (strcmp(str,"vy") == 0) type = Reader::VY;
else if (strcmp(str,"vz") == 0) type = Reader::VZ; else if (strcmp(str,"vz") == 0) type = Reader::VZ;
else if (strcmp(str,"q") == 0) type = Reader::Q; else if (strcmp(str,"q") == 0) type = Reader::Q;
else if (strcmp(str,"lambda") == 0) type = Reader::LAMBDA;
else if (strcmp(str,"ix") == 0) type = Reader::IX; else if (strcmp(str,"ix") == 0) type = Reader::IX;
else if (strcmp(str,"iy") == 0) type = Reader::IY; else if (strcmp(str,"iy") == 0) type = Reader::IY;
else if (strcmp(str,"iz") == 0) type = Reader::IZ; else if (strcmp(str,"iz") == 0) type = Reader::IZ;

View File

@ -22,7 +22,7 @@ namespace LAMMPS_NS {
class Reader : protected Pointers { class Reader : protected Pointers {
public: public:
enum { ID, TYPE, X, Y, Z, VX, VY, VZ, Q, IX, IY, IZ, FX, FY, FZ }; enum { ID, TYPE, X, Y, Z, VX, VY, VZ, Q, LAMBDA, IX, IY, IZ, FX, FY, FZ };
enum { UNSET, NOSCALE_NOWRAP, NOSCALE_WRAP, SCALE_NOWRAP, SCALE_WRAP }; enum { UNSET, NOSCALE_NOWRAP, NOSCALE_WRAP, SCALE_NOWRAP, SCALE_WRAP };
Reader(class LAMMPS *); Reader(class LAMMPS *);

View File

@ -413,6 +413,9 @@ bigint ReaderNative::read_header(double box[3][3], int &boxinfo, int &triclinic,
else if (fieldtype[i] == Q) else if (fieldtype[i] == Q)
fieldindex[i] = find_label("q", labels); fieldindex[i] = find_label("q", labels);
else if (fieldtype[i] == LAMBDA)
fieldindex[i] = find_label("lambda", labels);
else if (fieldtype[i] == IX) else if (fieldtype[i] == IX)
fieldindex[i] = find_label("ix", labels); fieldindex[i] = find_label("ix", labels);
else if (fieldtype[i] == IY) else if (fieldtype[i] == IY)

View File

@ -4992,6 +4992,30 @@ void Variable::peratom2global(int flag, char *word, double *vector, int nstride,
if (!atom->q_flag) if (!atom->q_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated"); error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->q[index]; mine = atom->q[index];
} else if (strcmp(word,"lambda") == 0) {
if (!atom->lambda_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->lambda[index];
} else if (strcmp(word,"lambda_required") == 0) {
if (!atom->lambda_required_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->lambda_required[index];
} else if (strcmp(word,"lambda_input") == 0) {
if (!atom->lambda_input_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->lambda_input[index];
} else if (strcmp(word,"lambda_const") == 0) {
if (!atom->lambda_const_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->lambda_const[index];
} else if (strcmp(word,"e_simple") == 0) {
if (!atom->e_simple_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->e_simple[index];
} else if (strcmp(word,"e_complex") == 0) {
if (!atom->e_complex_flag)
error->one(FLERR,"Variable uses atom property that isn't allocated");
mine = atom->e_complex[index];
} }
else if (strcmp(word,"x") == 0) mine = atom->x[index][0]; else if (strcmp(word,"x") == 0) mine = atom->x[index][0];
@ -5087,6 +5111,12 @@ int Variable::is_atom_vector(char *word)
if (strcmp(word,"fx") == 0) return 1; if (strcmp(word,"fx") == 0) return 1;
if (strcmp(word,"fy") == 0) return 1; if (strcmp(word,"fy") == 0) return 1;
if (strcmp(word,"fz") == 0) return 1; if (strcmp(word,"fz") == 0) return 1;
if (strcmp(word,"lambda") == 0) return 1;
if (strcmp(word,"lambda_input") == 0) return 1;
if (strcmp(word,"lambda_required") == 0) return 1;
if (strcmp(word,"lambda_const") == 0) return 1;
if (strcmp(word,"e_simple") == 0) return 1;
if (strcmp(word,"e_complex") == 0) return 1;
return 0; return 0;
} }
@ -5155,6 +5185,37 @@ void Variable::atom_vector(char *word, Tree **tree, Tree **treestack, int &ntree
error->one(FLERR,"Variable uses atom property 'q' that isn't allocated"); error->one(FLERR,"Variable uses atom property 'q' that isn't allocated");
newtree->array = atom->q; newtree->array = atom->q;
newtree->nstride = 1; newtree->nstride = 1;
} else if (strcmp(word,"lambda") == 0) {
if (!atom->lambda_flag)
error->one(FLERR,"Variable uses atom property 'lambda' that isn't allocated");
newtree->array = atom->lambda;
newtree->nstride = 1;
} else if (strcmp(word,"lambda_required") == 0) {
if (!atom->lambda_required_flag)
error->one(FLERR,"Variable uses atom property 'lambda_required' that isn't allocated");
newtree->type = INTARRAY;
newtree->nstride = 1;
newtree->iarray = atom->lambda_required;
} else if (strcmp(word,"lambda_input") == 0) {
if (!atom->lambda_input_flag)
error->one(FLERR,"Variable uses atom property 'lambda_input' that isn't allocated");
newtree->array = atom->lambda_input;
newtree->nstride = 1;
} else if (strcmp(word,"lambda_const") == 0) {
if (!atom->lambda_const_flag)
error->one(FLERR,"Variable uses atom property 'lambda_const' that isn't allocated");
newtree->array = atom->lambda_const;
newtree->nstride = 1;
} else if (strcmp(word,"e_simple") == 0) {
if (!atom->e_simple_flag)
error->one(FLERR,"Variable uses atom property 'e_simple' that isn't allocated");
newtree->array = atom->e_simple;
newtree->nstride = 1;
} else if (strcmp(word,"e_complex") == 0) {
if (!atom->e_complex_flag)
error->one(FLERR,"Variable uses atom property 'e_complex' that isn't allocated");
newtree->array = atom->e_complex;
newtree->nstride = 1;
} }
else if (strcmp(word,"x") == 0) newtree->array = &atom->x[0][0]; else if (strcmp(word,"x") == 0) newtree->array = &atom->x[0][0];