Merge pull request #4543 from d-immel/apip

adaptive-precision interatomic potentials (APIP)
This commit is contained in:
Axel Kohlmeyer
2025-06-16 15:56:12 -04:00
committed by GitHub
79 changed files with 99771 additions and 6 deletions

View File

@ -277,6 +277,7 @@ option(CMAKE_VERBOSE_MAKEFILE "Generate verbose Makefiles" OFF)
set(STANDARD_PACKAGES
ADIOS
AMOEBA
APIP
ASPHERE
ATC
AWPMD
@ -476,6 +477,7 @@ pkg_depends(ELECTRODE KSPACE)
pkg_depends(EXTRA-MOLECULE MOLECULE)
pkg_depends(MESONT MOLECULE)
pkg_depends(RHEO BPM)
pkg_depends(APIP ML-PACE)
# detect if we may enable OpenMP support by default
set(BUILD_OMP_DEFAULT OFF)

View File

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

View File

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

View File

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

View File

@ -35,6 +35,7 @@ This is the list of packages that may require additional steps.
:columns: 6
* :ref:`ADIOS <adios>`
* :ref:`APIP <apip>`
* :ref:`ATC <atc>`
* :ref:`AWPMD <awpmd>`
* :ref:`COLVARS <colvar>`
@ -1272,6 +1273,34 @@ systems.
----------
.. _apip:
APIP package
-----------------------------
The APIP package depends on the library of the
:ref:`ML-PACE <ml-pace>` package.
The code for the library can be found
at: `https://github.com/ICAMS/lammps-user-pace/ <https://github.com/ICAMS/lammps-user-pace/>`_
.. tabs::
.. tab:: CMake build
No additional settings are needed besides ``-D PKG_APIP=yes``
and ``-D PKG_ML-PACE=yes``.
One can use a local version of the ML-PACE library instead of
automatically downloading the library as described :ref:`here <ml-pace>`.
.. tab:: Traditional make
You need to install the ML-PACE package *first* and follow
the instructions :ref:`here <ml-pace>` before installing
the APIP package.
----------
.. _atc:
ATC package

View File

@ -22,6 +22,7 @@ OPT.
* :doc:`append/atoms <fix_append_atoms>`
* :doc:`atc <fix_atc>`
* :doc:`atom/swap <fix_atom_swap>`
* :doc:`atom_weight/apip <fix_atom_weight_apip>`
* :doc:`ave/atom <fix_ave_atom>`
* :doc:`ave/chunk <fix_ave_chunk>`
* :doc:`ave/correlate <fix_ave_correlate>`
@ -91,6 +92,8 @@ OPT.
* :doc:`imd <fix_imd>`
* :doc:`indent <fix_indent>`
* :doc:`ipi <fix_ipi>`
* :doc:`lambda/apip <fix_lambda_apip>`
* :doc:`lambda_thermostat/apip <fix_lambda_thermostat_apip>`
* :doc:`langevin (k) <fix_langevin>`
* :doc:`langevin/drude <fix_langevin_drude>`
* :doc:`langevin/eff <fix_langevin_eff>`

View File

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

View File

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

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

@ -0,0 +1,225 @@
Adaptive-precision interatomic potentials (APIP)
================================================
The :ref:`PKG-APIP <PKG-APIP>` enables use of adaptive-precision potentials
as described in :ref:`(Immel) <Immel2025_1>`.
In the context of this package, precision refers to the accuracy of an interatomic
potential.
Modern machine-learning (ML) potentials translate the accuracy of DFT
simulations into MD simulations, i.e., ML potentials are more accurate
compared to traditional empirical potentials.
However, this accuracy comes at a cost: there is a considerable performance
gap between the evaluation of classical and ML potentials, e.g., the force
calculation of a classical EAM potential is 100-1000 times faster compared
to the ML-based ACE method.
The evaluation time difference results in a conflict between large time and
length scales on the one hand and accuracy on the other.
This conflict is resolved by an APIP model for simulations, in which the highest precision
is required only locally but not globally.
An APIP model uses a precise but
expensive ML potential only for a subset of atoms, while a fast
potential is used for the remaining atoms.
Whether the precise or the fast potential is used is determined
by a continuous switching parameter :math:`\lambda_i` that can be defined for each
atom :math:`i`.
The switching parameter can be adjusted dynamically during a simulation or
kept constant as explained below.
The potential energy :math:`E_i` of an atom :math:`i` described by 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 how the potential energies are weighted.
Adaptive-precision saves computation time when the computation of the
precise potential is not required for many atoms, i.e., when
:math:`\lambda_i=1` applies for many atoms.
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>`.
The switching parameter :math:`\lambda_i` that combines the two potentials
can be dynamically calculated during a
simulation.
Alternatively, one can set a constant switching parameter before the start
of a simulation.
To run a simulation with an adaptive-precision potential, one needs the
following components:
.. tabs::
.. tab:: dynamic switching parameter
#. :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/fast/apip <pair_pace_apip>`.
#. A precise potential: :doc:`pace/precise/apip <pair_pace_apip>`.
#. :doc:`pair_style lambda/input/apip <pair_lambda_input_apip>` to calculate :math:`\lambda_i^\text{input}`, from which :math:`\lambda_i` is calculated.
#. :doc:`fix lambda/apip <fix_lambda_apip>` to calculate the switching parameter :math:`\lambda_i`.
#. :doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>` 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/apip <fix_lambda_thermostat_apip>` to conserve the energy when switching parameters change.
#. :doc:`fix atom_weight/apip <fix_atom_weight_apip>` 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.
.. tab:: constant switching parameter
#. :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/fast/apip <pair_pace_apip>`.
#. A precise potential: :doc:`pace/precise/apip <pair_pace_apip>`.
#. :doc:`set <set>` command to set the switching parameter :math:`\lambda_i`.
#. :doc:`pair_style hybrid/overlay <pair_hybrid>` to combine the previously mentioned pair_styles.
#. :doc:`fix atom_weight/apip <fix_atom_weight_apip>` 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>`.
.. tabs::
.. tab:: dynamic switching parameter
Lines like these would appear in the input script:
.. code-block:: LAMMPS
atom_style apip
comm_style tiled
pair_style hybrid/overlay eam/fs/apip pace/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
fix 2 all lambda/apip 2.5 3.0 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
fix 3 all lambda_thermostat/apip N_rescaling 200
fix 4 all atom_weight/apip 100 eam ace lambda/input lambda/zone all
variable myweight atom f_4
fix 5 all balance 100 1.1 rcb weight var myweight
First, the :doc:`atom_style apip <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.
.. tab:: constant switching parameter
Lines like these would appear in the input script:
.. code-block:: LAMMPS
atom_style apip
comm_style tiled
pair_style hybrid/overlay eam/fs/apip pace/precise/apip
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu.yace Cu
# calculate lambda somehow
variable lambda atom ...
set group all apip/lambda v_lambda
fix 4 all atom_weight/apip 100 eam ace lambda/input lambda/zone all
variable myweight atom f_4
fix 5 all balance 100 1.1 rcb weight var myweight
First, the :doc:`atom_style apip <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 is defined via
:doc:`pair_style hybrid/overlay <pair_hybrid>`.
The switching parameter :math:`\lambda_i` of the adaptive-precision
EAM-ACE potential is set via the :doc:`set command <set>`.
The parameter is not updated during the simulation.
Therefore, the potential is conservative.
The fixes ensure that the weight for the load balancing is 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:`AMOEBA <PKG-AMOEBA>`
* :ref:`APIP <PKG-APIP>`
* :ref:`ASPHERE <PKG-ASPHERE>`
* :ref:`ATC <PKG-ATC>`
* :ref:`AWPMD <PKG-AWPMD>`
@ -186,6 +187,60 @@ 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.
In the context of this package, precision refers to the accuracy of an interatomic
potential.
.. _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 atom_weight/apip <fix_atom_weight_apip>`
* :doc:`fix lambda/apip <fix_lambda_apip>`
* :doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`
* :doc:`pair_style eam/apip <pair_eam_apip>`
* :doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`
* :doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`
* :doc:`pair_style pace/apip <pair_pace_apip>`
----------
.. _PKG-ASPHERE:
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>`
- amoeba
- no
* - :ref:`APIP <PKG-APIP>`
- adaptive-precision interatomic potentials
- :doc:`Howto APIP <Howto_apip>`
- ``PACKAGES/apip``
- ext
* - :ref:`ASPHERE <PKG-ASPHERE>`
- aspherical particle models
- :doc:`Howto spherical <Howto_spherical>`

View File

@ -10,7 +10,7 @@ Syntax
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::
@ -117,6 +117,10 @@ the Additional Information section below.
- *bond* + "angle data"
- :ref:`MOLECULE <PKG-MOLECULE>`
- bead-spring polymers with stiffness
* - *apip*
- *atomic* + apip_lambda, apip_lambda_required, apip_lambda_input, apip_lambda_const, apip_lambda_input_ta, apip_e_fast, apip_e_precise, apip_f_const_lambda, apip_f_dyn_lambda
- :ref:`APIP <PKG-APIP>`
- adaptive-precision interatomic potentials(APIP), see :doc:`APIP howto <Howto_apip>`
* - *atomic*
- tag, type, x, v, f, image, mask
-

View File

@ -34,6 +34,8 @@ Syntax
i_name, d_name, i2_name[I], d2_name[I],
vfrac, s0, espin, eradius, ervel, erforce,
rho, drho, e, de, cv, buckling,
apip_lambda, apip_lambda_input, apip_e_fast,
apip_e_precise
.. parsed-literal::
@ -70,6 +72,13 @@ Syntax
*i2_name[I]* = Ith column of custom integer array with name
*d2_name[I]* = Ith column of custom floating-point array with name
.. parsed-literal::
APIP package per-atom properties:
*apip_lambda* = switching parameter
*apip_lambda_input* = input used to calculate the switching parameter
*apip_e_fast,apip_e_precise* = potential energies mixed by the adaptive-precision potential
.. parsed-literal::
PERI package per-atom properties:
@ -162,6 +171,22 @@ segment particles and define the end points of each line segment.
*corner2z*, *corner3x*, *corner3y*, *corner3z*, are defined for
triangular particles and define the corner points of each triangle.
The accessible quantities from the :doc:`APIP package <Howto_apip>` are
explained in the doc pages of this package in detail.
In short: *apip_lambda* is the switching parameter :math:`\lambda\in[0,1]`,
that is calculated from *apip_lambda_input* and that mixes the energies
of a fast (*apip_e_fast*) and a precise (*apip_e_precise*) potential
into an adaptive-precision energy.
.. note::
The energy according to the fast and the precise potential are only
computed for the subset of atoms, for which it is required, i.e.,
for an atom :math:`i` with :math:`\lambda_i=1` one does not need
:math:`E_i^\text{precise}` and with :math:`\lambda_i=0` one does
not need :math:`E_i^\text{fast}`.
In addition, the various per-atom quantities listed above for specific
packages are only accessible by this command.

View File

@ -201,6 +201,7 @@ accelerated styles exist.
* :doc:`append/atoms <fix_append_atoms>` - append atoms to a running simulation
* :doc:`atc <fix_atc>` - initiates a coupled MD/FE simulation
* :doc:`atom/swap <fix_atom_swap>` - Monte Carlo atom type swapping
* :doc:`atom_weight/apip <fix_atom_weight_apip>` - 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/chunk <fix_ave_chunk>` - compute per-chunk time-averaged quantities
* :doc:`ave/correlate <fix_ave_correlate>` - compute/output time correlations
@ -270,6 +271,7 @@ accelerated styles exist.
* :doc:`imd <fix_imd>` - implements the "Interactive MD" (IMD) protocol
* :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:`lambda/apip <fix_lambda_apip>` - compute switching parameter, that controls the precision of an :doc:`APIP potential <Howto_apip>`
* :doc:`langevin <fix_langevin>` - Langevin temperature control
* :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
@ -278,6 +280,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/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:`lambda_thermostat/apip <fix_lambda_thermostat_apip>` - apply energy conserving correction for an :doc:`APIP potential <Howto_apip>`
* :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/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 atom_weight/apip
fix atom_weight/apip command
============================
Syntax
""""""
.. code-block:: LAMMPS
fix ID group-ID atom_weight/apip nevery fast_potential precise_potential lambda_input lambda_zone group_lambda_input [no_rescale]
* ID, group-ID are documented in :doc:`fix <fix>` command
* atom_weight/apip = 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/apip or float for constant time
* lambda_zone = *lambda/zone* for a time measurement of pair_style lambda/zone/apip 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 atom_weight/apip 50 eam ace lambda/input lambda/zone all
fix 2 all atom_weight/apip 50 1e-05 0.0004 4e-06 4e-06 all
fix 2 all atom_weight/apip 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/apip <pair_lambda_input_apip>`.
A spatial switching zone, that ensures a smooth transition between two
different interatomic potentials, can be calculated by
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`.
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 atom_weight/apip ${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/apip for one atom
#. average compute time of lambda/zone/apip 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/apip <fix_lambda_apip>`,
:doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`,
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`,
:doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`,
: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_apip.rst Normal file
View File

@ -0,0 +1,262 @@
.. index:: fix lambda/apip
fix lambda/apip command
=======================
Syntax
""""""
.. code-block:: LAMMPS
fix ID group-ID lambda/apip thr_lo thr_hi keyword args ...
* ID, group-ID are documented in :doc:`fix <fix>` command
* lambda/apip = 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 neglected (>= 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/apip 3.0 3.5 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda 0.01
fix 2 mobile lambda/apip 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/apip <pair_lambda_input_apip>`.
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/apip <pair_lambda_input_apip>`, 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/apip <pair_lambda_zone_apip>`, 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/precise/apip <pair_pace_apip>`.
The input, from which the switching parameter is calculated, is provided
by :doc:`pair lambda/input/csp/apip <pair_lambda_input_apip>`.
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/apip <pair_lambda_zone_apip>`.
.. code-block:: LAMMPS
pair_style hybrid/overlay eam/fs/apip pace/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu_precise.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
fix 2 all lambda/apip 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/apip <pair_lambda_zone_apip>`,
:doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`
:doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`,
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/apip
fix lambda_thermostat/apip command
==================================
Syntax
""""""
.. code-block:: LAMMPS
fix ID group-ID lambda_thermostat/apip keyword values ...
* ID, group-ID are documented in :doc:`fix <fix>` command
* lambda_thermostat/apip = 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/apip
fix 2 all lambda_thermostat/apip N_rescaling 100
fix 2 all lambda_thermostat/apip seed 42
fix 2 all lambda_thermostat/apip 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/apip <fix_lambda_apip>`,
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`,
:doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`
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)

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

@ -0,0 +1,127 @@
.. 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/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu_precise.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
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
dynamically during a simulation by :doc:`fix lambda/apip <fix_lambda_apip>`
or set prior to a simulation via :doc:`set <set>`.
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/precise/apip <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/apip <fix_lambda_apip>`,
:doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`,
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`,
:doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`
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/apip
.. index:: pair_style lambda/input/csp/apip
pair_style lambda/input/apip command
====================================
Syntax
""""""
.. code-block:: LAMMPS
pair_style lambda/input/apip cutoff
* lambda/input/apip = style name of this pair style
* cutoff = global cutoff (distance units)
pair_style lambda/input/csp/apip command
========================================
Syntax
""""""
.. code-block:: LAMMPS
pair_style lambda/input/csp/apip lattice keyword args
* lambda/input/csp/apip = 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 neighboring atoms are considered (> 0)
*N_buffer* args = N_buffer
N_buffer = number of additional neighbors, which are included in the j-j+N/2 calculation
Examples
""""""""
.. code-block:: LAMMPS
pair_style lambda/input/csp/apip fcc
pair_style lambda/input/csp/apip fcc cutoff 5.0
pair_style lambda/input/csp/apip bcc cutoff 5.0 N_buffer 2
pair_style lambda/input/csp/apip 14
Description
"""""""""""
This pair_styles calculates :math:`\lambda_i^\text{input}(t)`, which
is required for :doc:`fix lambda/apip <fix_lambda_apip>`.
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/apip <fix_lambda_apip>`, as this computation
takes time and this pair_style can be included in the load-balancing via
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`.
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/precise/apip <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/apip <fix_lambda_apip>`,
whereas the spatial
transition zone of the switching parameter is calculated by
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`.
.. code-block:: LAMMPS
pair_style hybrid/overlay eam/fs/apip pace/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu_precise.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
fix 2 all lambda/apip 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/apip <fix_lambda_apip>`,
:doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`,
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`
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/apip
pair_style lambda/zone/apip command
===================================
Syntax
""""""
.. code-block:: LAMMPS
pair_style lambda/zone/apip cutoff
* lambda/zone/apip = style name of this pair style
* cutoff = global cutoff (distance units)
Examples
""""""""
.. code-block:: LAMMPS
pair_style lambda/zone/apip 12.0
Description
"""""""""""
This pair_style calculates :math:`\lambda_{\text{min},i}`, which
is required for :doc:`fix lambda/apip <fix_lambda_apip>`.
The meaning of :math:`\lambda_{\text{min},i}` is documented in
:doc:`fix lambda/apip <fix_lambda_apip>`, as this pair_style is for use with
:doc:`fix lambda/apip <fix_lambda_apip>` 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/apip <fix_lambda_apip>` 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/apip <fix_lambda_apip>`.
The computation of :math:`\lambda_{\text{min},i}` is done by this
pair_style instead of by :doc:`fix lambda/apip <fix_lambda_apip>`, as this computation
takes time and this pair_style can be included in the load-balancing via
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`.
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/precise/apip <pair_pace_apip>`.
The input, from which the switching parameter is calculated, is provided
by :doc:`pair lambda/input/csp/apip <pair_lambda_input_apip>`.
The switching parameter is calculated by :doc:`fix lambda/apip <fix_lambda_apip>`,
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/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu_precise.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
fix 2 all lambda/apip 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/apip <fix_lambda_apip>`,
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`
:doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`pair_style pace/apip <pair_pace_apip>`,
:doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`,
Default
"""""""
none

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

@ -0,0 +1,147 @@
.. index:: pair_style pace/apip
.. index:: pair_style pace/fast/apip
.. index:: pair_style pace/precise/apip
pair_style pace/apip command
============================
pair_style pace/fast/apip command
=================================
pair_style pace/precise/apip command
====================================
Constant precision variant: *pace*
Syntax
""""""
.. code-block:: LAMMPS
pair_style pace/apip ... keyword values ...
pair_style pace/fast/apip ... keyword values ...
pair_style pace/precise/apip ... 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/fast/apip pace/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * pace/fast/apip Cu_fast.yace Cu
pair_coeff * * pace/precise/apip Cu_precise.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
pair_style hybrid/overlay eam/fs/apip pace/precise/apip lambda/input/csp/apip fcc cutoff 5.0 lambda/zone/apip 12.0
pair_coeff * * eam/fs/apip Cu.eam.fs Cu
pair_coeff * * pace/precise/apip Cu_precise.yace Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
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
dynamically during a simulation by :doc:`fix lambda/apip <fix_lambda_apip>`
or set prior to a simulation via :doc:`set <set>`.
The pair style *pace/precise/apip* 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/fast/apip* 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/fast/apip* and *pace/precise/apip*
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/apip <fix_lambda_apip>`,
:doc:`fix lambda_thermostat/apip <fix_lambda_thermostat_apip>`,
:doc:`pair_style lambda/zone/apip <pair_lambda_zone_apip>`,
:doc:`pair_style lambda/input/apip <pair_lambda_input_apip>`,
:doc:`pair_style eam/apip <pair_eam_apip>`,
:doc:`fix atom_weight/apip <fix_atom_weight_apip>`
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/old <pair_eam>` - older two-site model for concentration-dependent 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/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/multi <pair_edip>` - multi-element EDIP potential
* :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:`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:`lambda/input/apip <pair_lambda_input_apip>` - constant as input for the precision calculation of an :doc:`adaptive-precision interatomic potential (APIP) <Howto_apip>`
* :doc:`lambda/input/csp/apip <pair_lambda_input_apip>` - CSP as input for the precision calculation of an :doc:`adaptive-precision interatomic potential (APIP) <Howto_apip>`
* :doc:`lambda/zone/apip <pair_lambda_zone_apip>` - transition zone of an :doc:`adaptive-precision interatomic potential <Howto_apip>`
* :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:`lennard/mdf <pair_mdf>` - LJ potential in A/B form with a taper function
@ -330,6 +335,9 @@ accelerated styles exist.
* :doc:`oxrna2/xstk <pair_oxrna2>` -
* :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/apip <pair_pace_apip>` - :doc:`adaptive-precision <Howto_apip>` version of ACE, used as precise potential
* :doc:`pace/fast/apip <pair_pace_apip>` - :doc:`adaptive-precision <Howto_apip>` version of ACE, used as fast potential
* :doc:`pace/precise/apip <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:`pod <pair_pod>` - Proper orthogonal decomposition (POD) machine-learning potential
* :doc:`peri/eps <pair_peri>` - Peridynamic EPS potential

View File

@ -16,12 +16,13 @@ Syntax
.. parsed-literal::
field = *x* or *y* or *z* or *vx* or *vy* or *vz* or *q* or *ix* or *iy* or *iz* or *fx* or *fy* or *fz*
field = *x* or *y* or *z* or *vx* or *vy* or *vz* or *q* or *ix* or *iy* or *iz* or *fx* or *fy* or *fz* or *apip_lambda*
*x*,\ *y*,\ *z* = atom coordinates
*vx*,\ *vy*,\ *vz* = velocity components
*q* = charge
*ix*,\ *iy*,\ *iz* = image flags in each dimension
*fx*,\ *fy*,\ *fz* = force components
*apip_lambda* = switching parameter of an :doc:`adaptive-precision interatomic potential <Howto_apip>`
* zero or more keyword/value pairs may be appended
* keyword = *nfile* or *box* or *timestep* or *replace* or *purge* or *trim* or *add* or *label* or *scaled* or *wrapped* or *format*

View File

@ -23,8 +23,8 @@ Syntax
* one or more keyword/value pairs may be appended
* keyword = *angle* or *angmom* or *bond* or *cc* or *charge* or
*density* or *density/disc* or *diameter* or *dihedral* or *dipole*
* keyword = *angle* or *angmom* or *apip/lambda* or *bond* or *cc* or *charge*
or *density* or *density/disc* or *diameter* or *dihedral* or *dipole*
or *dipole/random* or *dpd/theta* or *edpd/cv* or *edpd/temp* or
*epsilon* or *image* or *improper* or *length* or *mass* or *mol* or
*omega* or *quat* or *quat/random* or *radius/electron* or *shape* or
@ -41,6 +41,10 @@ Syntax
*angmom* values = Lx Ly Lz
Lx,Ly,Lz = components of angular momentum vector (distance-mass-velocity units)
any of Lx,Ly,Lz can be an atom-style variable (see below)
*apip/lambda* value = fast or precise or float
fast = switching parameter of fast potential (1)
precise = switching parameter of fast potential (0)
float = constant float or atom-style variable (between 0 and 1)
*bond* value = numeric bond type or bond type label, for all bonds between selected atoms
*cc* values = index cc
index = index of a chemical species (1 to Nspecies)
@ -632,6 +636,13 @@ atoms.
Keywords *x*, *y*, *z* set the coordinates of all selected atoms.
Keyword *apip/lambda* sets the switching parameter of an
adaptive-precision interatomic potential (:doc:`APIP <Howto_apip>`).
The precise potential is used for an atom when its switching parameter
:math:`\lambda` is 0. The fast potential is used for an atom when its
switching parameter :math:`\lambda` is 1. Both potentials are partially
used for :math:`\lambda\in(0,1)`.
Keywords *i_name*, *d_name*, *i2_name*, *d2_name* refer to custom
per-atom integer and floating-point vectors or arrays that have been
added via the :doc:`fix property/atom <fix_property_atom>` command.

View File

@ -135,6 +135,8 @@ anton
Antonelli
anysize
api
apip
APIP
apolar
Apoorva
Appl
@ -654,6 +656,7 @@ CSiC
csld
cslib
CSlib
csp
cstdio
cstdlib
cstring
@ -1384,6 +1387,7 @@ gmres
gname
gneb
GNEB
Godehard
Goerigk
Goga
Goldfarb
@ -1619,6 +1623,7 @@ Imageint
Imagemagick
imagename
imd
Immel
Impey
impl
improperlist
@ -3847,6 +3852,7 @@ Thiaville
Thibaudeau
Thijsse
Thirumalai
thr
Threadripper
threebody
thrid

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,80 @@
LAMMPS data file via write_data, version 2 Apr 2025, timestep = 0, units = metal
31 atoms
1 atom types
0 7.23 xlo xhi
0 7.23 ylo yhi
0 7.23 zlo zhi
Masses
1 63.546
Atoms # apip
1 1 3.255649577011424 2.1469408310018205 1.596546647595962 0 0 0
2 1 5.312133977687514 0.2962629940541289 1.7986410677440656 0 0 0
3 1 0.3570136530341179 1.9589665444185802 1.8227120430766197 0 0 0
4 1 1.538770434276094 1.8506888783106994 3.5984777679252336 0 0 0
5 1 5.414379916412933 1.9802551521608864 3.5428423680157133 0 0 0
6 1 3.3396207511420926 0.34696444514694835 3.3284295847601864 0 0 0
7 1 0.04692381499727436 3.470558659189874 0.20838500420697273 0 0 0
8 1 3.5507628116865004 3.419576015011443 0.09808429731968064 0 0 0
9 1 3.4359438466091845 3.3442299605630015 3.344947182361245 0 0 0
10 1 3.262172986855113 1.8898900738753799 5.6074716235080135 0 0 0
11 1 5.616180533384058 0.24272458584617107 5.728614316596936 0 0 0
12 1 0.3320737270957668 2.133631298552561 5.648234772896504 0 0 0
13 1 7.193910458696732 0.040079315967661934 7.007063468494249 -1 0 -1
14 1 1.9428459507398523 2.008894084694746 6.974381464595852 0 0 -1
15 1 5.17195441765988 1.6398976096007498 7.144624559798802 0 0 -1
16 1 3.828212487222493 0.27427274843853566 7.084083006467942 0 0 -1
17 1 0.3576896118392189 3.5593061817506357 3.8539966829342287 0 0 0
18 1 5.549388330643712 3.3771731288636446 5.733276811271104 0 0 0
19 1 1.474782367736 3.311754538948766 5.203036111913638 0 0 0
20 1 3.353885621487343 5.483140337766679 1.559656844576661 0 0 0
21 1 1.4600851633288363 5.3693400677513985 0.16901869775192754 0 0 0
22 1 1.865319826459661 3.680823307523189 1.909829542233995 0 0 0
23 1 6.919428313555163 5.704165921616911 1.5626446154218376 -1 0 0
24 1 5.286111954406888 3.9741177165543746 1.894962129368895 0 0 0
25 1 5.661441532175263 5.770831269632481 0.2726487131030991 0 0 0
26 1 5.749451385769738 5.694440631979816 3.315201684766776 0 0 0
27 1 2.1656354707333425 5.630356615286487 3.525133119978771 0 0 0
28 1 3.4802512558478638 5.136357035041954 5.613687950103865 0 0 0
29 1 6.9476459434582605 5.669871702976 5.748711917622347 -1 0 0
30 1 0.3145319513743893 6.992506749359428 3.748936483911442 0 -1 0
31 1 2.0688280140984934 7.144932953378388 5.071647430554334 0 -1 0
Velocities
1 0 0 0
2 0 0 0
3 0 0 0
4 0 0 0
5 0 0 0
6 0 0 0
7 0 0 0
8 0 0 0
9 0 0 0
10 0 0 0
11 0 0 0
12 0 0 0
13 0 0 0
14 0 0 0
15 0 0 0
16 0 0 0
17 0 0 0
18 0 0 0
19 0 0 0
20 0 0 0
21 0 0 0
22 0 0 0
23 0 0 0
24 0 0 0
25 0 0 0
26 0 0 0
27 0 0 0
28 0 0 0
29 0 0 0
30 0 0 0
31 0 0 0

View File

@ -0,0 +1,75 @@
##################################################
# 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/apip ${csp_lattice} cutoff ${csp_cutoff} lambda/zone/apip ${r_sw_hi}
pair_coeff * * eam/fs/apip ${eamfs_file} Cu
pair_coeff * * pace/apip ${ace_file2} Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
fix lambda all lambda/apip ${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/apip N_rescaling ${N_rescaling}
fix weight_atom all atom_weight/apip 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
#compute lambda all property/atom apip_lambda
#compute lambda_input all property/atom apip_lambda_input
#dump 1 all custom 10 dump/surface_ap_balance.dump.* id type x y z c_lambda c_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,54 @@
##################################################
# 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"
## 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
# set lambda
group vacancy id 145 147 148 149 150 994 997 995 1024 1026 1028 1922
group transition id 77 79 81 84 88 151 152 153 154 155 158 1089 188 189 192 1033 1039 1964 1022 1021 999 998 996 948 950 952 992 993 139 1025 1027 1029 1034 1035 1038 1088 1920 1921 1923 1924 1925 1930
group bulk subtract all vacancy transition
set group vacancy apip/lambda precise
set group transition apip/lambda 0.5
set group bulk apip/lambda fast
fix nve all nve
# Use adaptive-precision eam-ace potential with constant lambda.
# Calculate atomic weight that could be used for load balancing.
pair_style hybrid/overlay eam/fs/apip pace/apip
pair_coeff * * eam/fs/apip ${eamfs_file} Cu
pair_coeff * * pace/apip ${ace_file2} Cu
fix weight_atom all atom_weight/apip 50 eam ace 0.5 0 all
# get statistics about lambda
compute lambda all property/atom apip_lambda
compute lambda_input all property/atom apip_lambda_input
variable flag_simple atom c_lambda==1
variable flag_switch atom c_lambda<1&&c_lambda>0
variable flag_complex atom c_lambda==0
compute lambda_types all reduce sum v_flag_simple v_flag_switch v_flag_complex
thermo_style custom step etotal c_lambda_types[*]
thermo 1
run 100
# dump atoms
#write_dump all custom dump.vacancy.* id type x y z fx fy fz c_lambda c_lambda_input f_weight_atom

View File

@ -0,0 +1,101 @@
##################################################
# 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 2.0
variable r_sw_hi equal 3.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/fast/apip pace/precise/apip lambda/input/csp/apip ${csp_lattice} cutoff ${csp_cutoff} lambda/zone/apip ${r_sw_hi}
#pair_coeff * * pace/fast/apip ${ace_file1} Cu
#pair_coeff * * pace/precise/apip ${ace_file2} Cu
#pair_coeff * * lambda/input/csp/apip
#pair_coeff * * lambda/zone/apip
#fix lambda all lambda/apip ${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 atom_weight/apip 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/apip ${csp_lattice} cutoff ${csp_cutoff} lambda/zone/apip ${r_sw_hi}
pair_coeff * * eam/fs/apip ${eamfs_file} Cu
pair_coeff * * pace/apip ${ace_file2} Cu
pair_coeff * * lambda/input/csp/apip
pair_coeff * * lambda/zone/apip
fix lambda all lambda/apip ${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 atom_weight/apip 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/apip N_rescaling ${N_rescaling} store_atomic_forces 100
# get statistics about lambda
compute lambda all property/atom apip_lambda
compute lambda_input all property/atom apip_lambda_input
variable flag_simple atom c_lambda==1
variable flag_switch atom c_lambda<1&&c_lambda>0
variable flag_complex atom c_lambda==0
compute lambda_types all reduce sum v_flag_simple v_flag_switch v_flag_complex
thermo_style custom step etotal c_lambda_types[*]
thermo 1
run 100
# dump atoms
#write_dump all custom dump/vacancy.dump.* id type x y z fx fy fz c_lambda c_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/apip fcc cutoff 6.0 lambda/zone/apip 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/apip
#pair_coeff * * lambda/zone/apip
#fix lambda all lambda/apip 2.5 3.0 time_averaged_zone 4.0 12.0 110 110 min_delta_lambda $(1/110)
#fix lambda_thermostat all lambda_thermostat/apip 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,51 @@
##################################################
# 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_file string "Cu-1.yace"
## basic stuff
units metal
atom_style apip # own atom style required for APIP
timestep 0.001
## copper with a vacancy
#lattice fcc 3.615
#region box block 0 2 0 2 0 2 units lattice
#create_box 1 box
#create_atoms 1 box
#mass 1 63.546
#displace_atoms all random 0.1 0.1 0.1 42 units lattice
#delete_atoms random count 1 yes all NULL 42
#write_data data.validate
read_data data.validate
fix 1 all nve
thermo_style custom step pe fnorm fmax
# use ACE potential
pair_style pace
pair_coeff * * ${ace_file} Cu
run 0
# use adaptive-precision EAM-ACE potential with constant lambda
pair_style hybrid/overlay eam/fs/apip pace/apip
pair_coeff * * eam/fs/apip ${eamfs_file} Cu
pair_coeff * * pace/apip ${ace_file} Cu
# use ACE of adaptive-precision potential
set group all apip/lambda precise
run 0
# use EAM of adaptive-precision potential
set group all apip/lambda fast
run 0
# Use EAM potential
pair_style eam/fs
pair_coeff * * ${eamfs_file} Cu
run 0

View File

@ -0,0 +1,108 @@
LAMMPS (2 Apr 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 = 0.135 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/apip:time_per_atom
lambda: extract lambda/zone/apip: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/apip, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone/apip, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat/apip, 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.6176099 0.23789922 3.4933844e-05 0.0018769787 2.0912678e-05 3.414976e-05
20 2.1242027 0.2259695 4.2800036e-05 0.0022124427 2.4613899e-05 4.0819606e-05
30 2.1197082 0.22281117 4.3885117e-05 0.0022416604 2.4808124e-05 4.081464e-05
40 2.1149313 0.2245266 4.3328755e-05 0.0022134411 2.4645468e-05 4.0892224e-05
50 2.1066618 0.22308398 4.3884622e-05 0.0022085846 2.464161e-05 4.0909229e-05
60 2.1000306 0.22186291 4.4839801e-05 0.0022299631 2.4816945e-05 4.1173119e-05
70 2.1062658 0.22406791 4.3740709e-05 0.0022049729 2.4693417e-05 4.0963037e-05
80 2.1072933 0.22241288 4.4160464e-05 0.0022245226 2.4854938e-05 4.2491144e-05
90 2.0981181 0.22412189 4.3990788e-05 0.0022086162 2.4710948e-05 4.09478e-05
100 2.1033304 0.2237968 4.3982714e-05 0.0022112815 2.4658851e-05 4.1060551e-05
Loop time of 444.499 on 4 procs for 100 steps with 40200 atoms
Performance: 0.019 ns/day, 1234.721 hours/ns, 0.225 timesteps/s, 9.044 katom-step/s
99.0% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 67.764 | 146.15 | 378.03 |1107.5 | 32.88
Neigh | 55.764 | 59.579 | 62.94 | 43.1 | 13.40
Comm | 0.6589 | 153.57 | 302.72 |1199.1 | 34.55
Output | 0.0032423 | 6.1068 | 24.01 | 418.3 | 1.37
Modify | 8.0892 | 16.162 | 38.588 | 322.6 | 3.64
Other | | 62.93 | | | 14.16
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:07:25

View File

@ -0,0 +1,108 @@
LAMMPS (2 Apr 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 = 0.161 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/apip:time_per_atom
lambda: extract lambda/zone/apip: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/apip, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone/apip, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat/apip, 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.0018226 0.21929744 4.2082278e-05 0.0022147425 2.472285e-05 4.0694771e-05
20 1.002451 0.38193369 4.9389333e-05 0.0023722824 2.4776133e-05 2.168819e-05
30 1.0004234 0.39492198 4.8977088e-05 0.002249832 2.4610967e-05 2.1465621e-05
40 1.0012276 0.39068387 4.9886201e-05 0.0022529811 2.4663509e-05 2.1540032e-05
50 1.0013752 0.39170654 5.0527084e-05 0.002253323 2.4721079e-05 2.153442e-05
60 1.0007053 0.3758265 5.0935226e-05 0.0022581901 2.4772692e-05 2.1596641e-05
70 1.0003982 0.38524379 5.1685387e-05 0.002263544 2.495032e-05 2.1714264e-05
80 1.0022848 0.38409158 5.1897166e-05 0.0022557711 2.4755235e-05 2.1595246e-05
90 1.0012911 0.38122934 5.2440631e-05 0.0022574019 2.4795351e-05 2.1615786e-05
100 1.0005279 0.37983246 5.2871808e-05 0.0022583136 2.484618e-05 2.1569403e-05
Loop time of 279.389 on 4 procs for 100 steps with 40200 atoms
Performance: 0.031 ns/day, 776.081 hours/ns, 0.358 timesteps/s, 14.389 katom-step/s
98.6% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 148.19 | 155.34 | 175.77 | 94.7 | 55.60
Neigh | 87.542 | 87.924 | 88.444 | 3.6 | 31.47
Comm | 4.9019 | 18.932 | 33.429 | 322.2 | 6.78
Output | 0.01326 | 0.04615 | 0.075485 | 10.3 | 0.02
Modify | 9.5358 | 10.391 | 12.424 | 36.6 | 3.72
Other | | 6.756 | | | 2.42
Nlocal: 10050 ave 10283 max 9838 min
Histogram: 1 0 1 0 0 0 1 0 0 1
Nghost: 52763 ave 52975 max 52530 min
Histogram: 1 0 0 1 0 0 0 1 0 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.71459e+06 max 9.29015e+06 min
Histogram: 1 0 1 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:04:48

View File

@ -0,0 +1,108 @@
LAMMPS (2 Apr 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 = 0.134 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/apip:time_per_atom
lambda: extract lambda/zone/apip: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/apip, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone/apip, perpetual
attributes: full, newton on, ghost, cut 14
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat/apip, 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.000245 0.23168829 2.8965377e-05 0.0016346261 1.8194173e-05 2.8621987e-05
20 1.0001156 0.23012235 4.2703387e-05 0.0022174634 2.4522315e-05 4.0198928e-05
30 1.0003479 0.24170843 4.2900821e-05 0.0022358654 2.4496934e-05 4.0262644e-05
40 1.0000952 0.34743014 4.4687983e-05 0.0022336389 2.4626704e-05 2.8362965e-05
50 1.000399 0.23273413 4.4278568e-05 0.0022319529 2.4645291e-05 4.0394287e-05
60 1.000137 0.23531789 4.5212176e-05 0.0022671271 2.4758267e-05 4.0607561e-05
70 1.0001901 0.34359487 4.5647742e-05 0.0022386998 2.473236e-05 2.8357019e-05
80 1.0003173 0.23324348 4.5372627e-05 0.0022272881 2.4704053e-05 4.0760615e-05
90 1.0009828 0.2360927 4.5867574e-05 0.0022616272 2.4715122e-05 4.0548519e-05
100 1.0005337 0.34332801 4.6171376e-05 0.0022394199 2.4724815e-05 2.8552653e-05
Loop time of 385.695 on 4 procs for 100 steps with 40200 atoms
Performance: 0.022 ns/day, 1071.376 hours/ns, 0.259 timesteps/s, 10.423 katom-step/s
98.9% CPU use with 4 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 80.949 | 145.84 | 287.45 | 683.6 | 37.81
Neigh | 21.125 | 61.121 | 98.27 | 452.7 | 15.85
Comm | 23.839 | 129.54 | 261.28 | 833.4 | 33.59
Output | 0.10319 | 2.5231 | 8.6153 | 222.9 | 0.65
Modify | 5.3228 | 13.101 | 29.2 | 267.2 | 3.40
Other | | 33.57 | | | 8.70
Nlocal: 10050 ave 16825 max 3772 min
Histogram: 2 0 0 0 0 0 0 0 0 2
Nghost: 27618.8 ave 41748 max 12149 min
Histogram: 1 1 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 1.58445e+07 max 3.337e+06 min
Histogram: 2 0 0 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:06:27

View File

@ -0,0 +1,182 @@
LAMMPS (2 Apr 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 0) to (28.92 28.92 28.92)
1 by 1 by 1 MPI processor grid
reading atoms ...
2047 atoms
reading velocities ...
2047 velocities
read_data CPU = 0.009 seconds
12 atoms in group vacancy
42 atoms in group transition
1993 atoms in group bulk
Setting atom values ...
12 settings made for apip/lambda
Setting atom values ...
42 settings made for apip/lambda
Setting atom values ...
1993 settings made for apip/lambda
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: const 0.5
lambda: const 0
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 9.4
ghost atom cutoff = 9.4
binsize = 4.7, bins = 7 7 7
2 neighbor lists, perpetual/occasional/extra = 2 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
attributes: full, newton on, cut 9.4
pair build: full/bin/atomonly
stencil: full/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) = 11.08 | 11.08 | 11.08 Mbytes
Step TotEng c_lambda_types[1] c_lambda_types[2] c_lambda_types[3]
0 -7408.6798 1993 42 12
1 -7408.6798 1993 42 12
2 -7408.6798 1993 42 12
3 -7408.6798 1993 42 12
4 -7408.6798 1993 42 12
5 -7408.6798 1993 42 12
6 -7408.6799 1993 42 12
7 -7408.6799 1993 42 12
8 -7408.6799 1993 42 12
9 -7408.6799 1993 42 12
10 -7408.6799 1993 42 12
11 -7408.68 1993 42 12
12 -7408.68 1993 42 12
13 -7408.68 1993 42 12
14 -7408.68 1993 42 12
15 -7408.68 1993 42 12
16 -7408.68 1993 42 12
17 -7408.6801 1993 42 12
18 -7408.6801 1993 42 12
19 -7408.6801 1993 42 12
20 -7408.6801 1993 42 12
21 -7408.6801 1993 42 12
22 -7408.6801 1993 42 12
23 -7408.6801 1993 42 12
24 -7408.6801 1993 42 12
25 -7408.6801 1993 42 12
26 -7408.6801 1993 42 12
27 -7408.6801 1993 42 12
28 -7408.6801 1993 42 12
29 -7408.6801 1993 42 12
30 -7408.6801 1993 42 12
31 -7408.6801 1993 42 12
32 -7408.68 1993 42 12
33 -7408.68 1993 42 12
34 -7408.68 1993 42 12
35 -7408.68 1993 42 12
36 -7408.68 1993 42 12
37 -7408.68 1993 42 12
38 -7408.68 1993 42 12
39 -7408.68 1993 42 12
40 -7408.6801 1993 42 12
41 -7408.6801 1993 42 12
42 -7408.6801 1993 42 12
43 -7408.6801 1993 42 12
44 -7408.6801 1993 42 12
45 -7408.6801 1993 42 12
46 -7408.6801 1993 42 12
47 -7408.6801 1993 42 12
48 -7408.6801 1993 42 12
49 -7408.6802 1993 42 12
50 -7408.6802 1993 42 12
51 -7408.6802 1993 42 12
52 -7408.6802 1993 42 12
53 -7408.6802 1993 42 12
54 -7408.6802 1993 42 12
55 -7408.6802 1993 42 12
56 -7408.6802 1993 42 12
57 -7408.6802 1993 42 12
58 -7408.6802 1993 42 12
59 -7408.6801 1993 42 12
60 -7408.6801 1993 42 12
61 -7408.6801 1993 42 12
62 -7408.6801 1993 42 12
63 -7408.6801 1993 42 12
64 -7408.68 1993 42 12
65 -7408.68 1993 42 12
66 -7408.68 1993 42 12
67 -7408.68 1993 42 12
68 -7408.68 1993 42 12
69 -7408.6799 1993 42 12
70 -7408.6799 1993 42 12
71 -7408.6799 1993 42 12
72 -7408.6799 1993 42 12
73 -7408.6799 1993 42 12
74 -7408.6799 1993 42 12
75 -7408.6798 1993 42 12
76 -7408.6798 1993 42 12
77 -7408.6798 1993 42 12
78 -7408.6798 1993 42 12
79 -7408.6798 1993 42 12
80 -7408.6798 1993 42 12
81 -7408.6799 1993 42 12
82 -7408.6799 1993 42 12
83 -7408.6799 1993 42 12
84 -7408.6799 1993 42 12
85 -7408.6799 1993 42 12
86 -7408.6799 1993 42 12
87 -7408.6799 1993 42 12
88 -7408.68 1993 42 12
89 -7408.68 1993 42 12
90 -7408.68 1993 42 12
91 -7408.68 1993 42 12
92 -7408.68 1993 42 12
93 -7408.68 1993 42 12
94 -7408.68 1993 42 12
95 -7408.68 1993 42 12
96 -7408.68 1993 42 12
97 -7408.6801 1993 42 12
98 -7408.6801 1993 42 12
99 -7408.6801 1993 42 12
100 -7408.6801 1993 42 12
Loop time of 1.65093 on 1 procs for 100 steps with 2047 atoms
Performance: 5.233 ns/day, 4.586 hours/ns, 60.572 timesteps/s, 123.991 katom-step/s
99.8% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 1.6272 | 1.6272 | 1.6272 | 0.0 | 98.56
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0.012716 | 0.012716 | 0.012716 | 0.0 | 0.77
Output | 0.0078409 | 0.0078409 | 0.0078409 | 0.0 | 0.47
Modify | 0.0013654 | 0.0013654 | 0.0013654 | 0.0 | 0.08
Other | | 0.001839 | | | 0.11
Nlocal: 2047 ave 2047 max 2047 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 7790 ave 7790 max 7790 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 0 ave 0 max 0 min
Histogram: 1 0 0 0 0 0 0 0 0 0
FullNghs: 648022 ave 648022 max 648022 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 648022
Ave neighs/atom = 316.57157
Neighbor list builds = 0
Dangerous builds = 0
Total wall time: 0:00:01

View File

@ -0,0 +1,193 @@
LAMMPS (2 Apr 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 0) to (28.92 28.92 28.92)
1 by 1 by 1 MPI processor grid
reading atoms ...
2047 atoms
reading velocities ...
2047 velocities
read_data CPU = 0.009 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/apip:time_per_atom
lambda: extract lambda/zone/apip: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/apip is not used. (../fix_lambda_apip.cpp:248)
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 9.4
ghost atom cutoff = 9.4
binsize = 4.7, bins = 7 7 7
4 neighbor lists, perpetual/occasional/extra = 4 0 0
(1) pair eam/fs/apip, perpetual, trim from (3)
attributes: full, newton on, cut 7.50679
pair build: trim
stencil: none
bin: none
(2) pair pace/apip, perpetual
attributes: full, newton on, cut 9.4
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(3) pair lambda/input/csp/apip, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone/apip, perpetual
attributes: full, newton on, ghost
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) = 49.37 | 49.37 | 49.37 Mbytes
Step TotEng c_lambda_types[1] c_lambda_types[2] c_lambda_types[3]
0 -7408.7649 1993 42 12
1 -7408.7649 1993 42 12
2 -7408.7649 1993 42 12
3 -7408.765 1993 42 12
4 -7408.7657 1993 42 12
5 -7408.7785 1993 42 12
6 -7408.7769 1993 42 12
7 -7408.778 1993 42 12
8 -7408.7862 1993 42 12
9 -7408.79 1993 42 12
10 -7408.7912 1993 42 12
11 -7408.8005 1993 42 12
12 -7408.8012 1993 42 12
13 -7408.8016 1993 42 12
14 -7408.8123 1993 42 12
15 -7408.8154 1993 42 12
16 -7408.8144 1993 42 12
17 -7408.8271 1993 42 12
18 -7408.8277 1993 42 12
19 -7408.8289 1993 42 12
20 -7408.8352 1993 42 12
21 -7408.8376 1993 42 12
22 -7408.841 1993 42 12
23 -7408.8507 1993 42 12
24 -7408.8496 1993 42 12
25 -7408.8544 1993 42 12
26 -7408.8645 1993 42 12
27 -7408.8665 1993 42 12
28 -7408.8713 1993 42 12
29 -7408.8763 1993 42 12
30 -7408.8812 1993 42 12
31 -7408.8842 1993 42 12
32 -7408.8905 1993 42 12
33 -7408.8947 1993 42 12
34 -7408.9048 1993 42 12
35 -7408.9099 1993 42 12
36 -7408.9101 1993 42 12
37 -7408.9159 1993 42 12
38 -7408.9256 1993 42 12
39 -7408.9241 1993 42 12
40 -7408.9342 1993 42 12
41 -7408.9423 1993 42 12
42 -7408.9402 1993 42 12
43 -7408.9452 1993 42 12
44 -7408.9548 1993 42 12
45 -7408.9543 1993 42 12
46 -7408.9607 1993 42 12
47 -7408.9699 1993 42 12
48 -7408.9751 1993 42 12
49 -7408.978 1993 42 12
50 -7408.9797 1993 42 12
51 -7408.9851 1993 42 12
52 -7408.9937 1993 42 12
53 -7408.9977 1993 42 12
54 -7408.9961 1993 42 12
55 -7409.0011 1993 42 12
56 -7409.0098 1993 42 12
57 -7409.0132 1993 42 12
58 -7409.0173 1993 42 12
59 -7409.0174 1993 42 12
60 -7409.0204 1993 42 12
61 -7409.0259 1993 42 12
62 -7409.0324 1993 42 12
63 -7409.0365 1993 42 12
64 -7409.0407 1993 42 12
65 -7409.0407 1993 42 12
66 -7409.0428 1993 42 12
67 -7409.0437 1993 42 12
68 -7409.0437 1993 42 12
69 -7409.0502 1993 42 12
70 -7409.0558 1993 42 12
71 -7409.0572 1993 42 12
72 -7409.0624 1993 42 12
73 -7409.0686 1993 42 12
74 -7409.0721 1993 42 12
75 -7409.075 1993 42 12
76 -7409.0751 1993 42 12
77 -7409.0728 1993 42 12
78 -7409.0732 1993 42 12
79 -7409.0764 1993 42 12
80 -7409.077 1993 42 12
81 -7409.0879 1993 42 12
82 -7409.0898 1993 42 12
83 -7409.0922 1993 42 12
84 -7409.0916 1993 42 12
85 -7409.0928 1993 42 12
86 -7409.0944 1993 42 12
87 -7409.1058 1993 42 12
88 -7409.1084 1993 42 12
89 -7409.1103 1993 42 12
90 -7409.1121 1993 42 12
91 -7409.1145 1993 42 12
92 -7409.1133 1993 42 12
93 -7409.1166 1993 42 12
94 -7409.12 1993 42 12
95 -7409.119 1993 42 12
96 -7409.1208 1993 42 12
97 -7409.1217 1993 42 12
98 -7409.1229 1993 42 12
99 -7409.1235 1993 42 12
100 -7409.1206 1993 42 12
Loop time of 2.01276 on 1 procs for 100 steps with 2047 atoms
Performance: 4.293 ns/day, 5.591 hours/ns, 49.683 timesteps/s, 101.701 katom-step/s
99.8% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 1.985 | 1.985 | 1.985 | 0.0 | 98.62
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0.012367 | 0.012367 | 0.012367 | 0.0 | 0.61
Output | 0.0080612 | 0.0080612 | 0.0080612 | 0.0 | 0.40
Modify | 0.0053111 | 0.0053111 | 0.0053111 | 0.0 | 0.26
Other | | 0.002 | | | 0.10
Nlocal: 2047 ave 2047 max 2047 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 7790 ave 7790 max 7790 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 0 ave 0 max 0 min
Histogram: 1 0 0 0 0 0 0 0 0 0
FullNghs: 291196 ave 291196 max 291196 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 291196
Ave neighs/atom = 142.25501
Neighbor list builds = 0
Dangerous builds = 0
Total wall time: 0:00:02

View File

@ -0,0 +1,197 @@
LAMMPS (2 Apr 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 0) to (28.92 28.92 28.92)
1 by 1 by 1 MPI processor grid
reading atoms ...
2047 atoms
reading velocities ...
2047 velocities
read_data CPU = 0.009 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/apip:time_per_atom
lambda: extract lambda/zone/apip: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 = 9.4
ghost atom cutoff = 9.4
binsize = 4.7, bins = 7 7 7
5 neighbor lists, perpetual/occasional/extra = 5 0 0
(1) pair eam/fs/apip, perpetual, trim from (3)
attributes: full, newton on, cut 7.50679
pair build: trim
stencil: none
bin: none
(2) pair pace/apip, perpetual
attributes: full, newton on, cut 9.4
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(3) pair lambda/input/csp/apip, perpetual, trim from (2)
attributes: full, newton on, cut 8
pair build: trim
stencil: none
bin: none
(4) pair lambda/zone/apip, perpetual
attributes: full, newton on, ghost
pair build: full/bin/ghost
stencil: full/ghost/bin/3d
bin: standard
(5) fix lambda_thermostat/apip, perpetual, copy from (2)
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) = 52.93 | 52.93 | 52.93 Mbytes
Step TotEng c_lambda_types[1] c_lambda_types[2] c_lambda_types[3]
0 -7408.7649 1993 42 12
1 -7408.7649 1993 42 12
2 -7408.7649 1993 42 12
3 -7408.765 1993 42 12
4 -7408.765 1993 42 12
5 -7408.765 1993 42 12
6 -7408.765 1993 42 12
7 -7408.765 1993 42 12
8 -7408.765 1993 42 12
9 -7408.765 1993 42 12
10 -7408.7651 1993 42 12
11 -7408.7651 1993 42 12
12 -7408.7651 1993 42 12
13 -7408.7651 1993 42 12
14 -7408.7651 1993 42 12
15 -7408.7651 1993 42 12
16 -7408.7652 1993 42 12
17 -7408.7652 1993 42 12
18 -7408.7652 1993 42 12
19 -7408.7652 1993 42 12
20 -7408.7652 1993 42 12
21 -7408.7652 1993 42 12
22 -7408.7652 1993 42 12
23 -7408.7652 1993 42 12
24 -7408.7652 1993 42 12
25 -7408.7652 1993 42 12
26 -7408.7652 1993 42 12
27 -7408.7652 1993 42 12
28 -7408.7652 1993 42 12
29 -7408.7652 1993 42 12
30 -7408.7652 1993 42 12
31 -7408.7652 1993 42 12
32 -7408.7652 1993 42 12
33 -7408.7652 1993 42 12
34 -7408.7652 1993 42 12
35 -7408.7652 1993 42 12
36 -7408.7652 1993 42 12
37 -7408.7652 1993 42 12
38 -7408.7652 1993 42 12
39 -7408.7652 1993 42 12
40 -7408.7652 1993 42 12
41 -7408.7652 1993 42 12
42 -7408.7652 1993 42 12
43 -7408.7652 1993 42 12
44 -7408.7652 1993 42 12
45 -7408.7652 1993 42 12
46 -7408.7652 1993 42 12
47 -7408.7653 1993 42 12
48 -7408.7653 1993 42 12
49 -7408.7653 1993 42 12
50 -7408.7653 1993 42 12
51 -7408.7653 1993 42 12
52 -7408.7653 1993 42 12
53 -7408.7653 1993 42 12
54 -7408.7653 1993 42 12
55 -7408.7653 1993 42 12
56 -7408.7653 1993 42 12
57 -7408.7653 1993 42 12
58 -7408.7653 1993 42 12
59 -7408.7653 1993 42 12
60 -7408.7652 1993 42 12
61 -7408.7652 1993 42 12
62 -7408.7652 1993 42 12
63 -7408.7652 1993 42 12
64 -7408.7652 1993 42 12
65 -7408.7651 1993 42 12
66 -7408.7651 1993 42 12
67 -7408.7651 1993 42 12
68 -7408.7651 1993 42 12
69 -7408.7651 1993 42 12
70 -7408.765 1993 42 12
71 -7408.765 1993 42 12
72 -7408.765 1993 42 12
73 -7408.765 1993 42 12
74 -7408.765 1993 42 12
75 -7408.765 1993 42 12
76 -7408.765 1993 42 12
77 -7408.765 1993 42 12
78 -7408.765 1993 42 12
79 -7408.765 1993 42 12
80 -7408.765 1993 42 12
81 -7408.765 1993 42 12
82 -7408.765 1993 42 12
83 -7408.765 1993 42 12
84 -7408.765 1993 42 12
85 -7408.765 1993 42 12
86 -7408.765 1993 42 12
87 -7408.765 1993 42 12
88 -7408.765 1993 42 12
89 -7408.7651 1993 42 12
90 -7408.7651 1993 42 12
91 -7408.7651 1993 42 12
92 -7408.7651 1993 42 12
93 -7408.7651 1993 42 12
94 -7408.7651 1993 42 12
95 -7408.7651 1993 42 12
96 -7408.7651 1993 42 12
97 -7408.7651 1993 42 12
98 -7408.7651 1993 42 12
99 -7408.7651 1993 42 12
100 -7408.7651 1993 42 12
Loop time of 2.19492 on 1 procs for 100 steps with 2047 atoms
Performance: 3.936 ns/day, 6.097 hours/ns, 45.560 timesteps/s, 93.261 katom-step/s
99.8% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 2.0683 | 2.0683 | 2.0683 | 0.0 | 94.23
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0.012156 | 0.012156 | 0.012156 | 0.0 | 0.55
Output | 0.0084851 | 0.0084851 | 0.0084851 | 0.0 | 0.39
Modify | 0.10386 | 0.10386 | 0.10386 | 0.0 | 4.73
Other | | 0.002096 | | | 0.10
Nlocal: 2047 ave 2047 max 2047 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 7790 ave 7790 max 7790 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 0 ave 0 max 0 min
Histogram: 1 0 0 0 0 0 0 0 0 0
FullNghs: 291196 ave 291196 max 291196 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 291196
Ave neighs/atom = 142.25501
Neighbor list builds = 0
Dangerous builds = 0
Total wall time: 0:00:02

View File

@ -0,0 +1,201 @@
LAMMPS (2 Apr 2025 - Development - )
Reading data file ...
orthogonal box = (0 0 0) to (7.23 7.23 7.23)
1 by 1 by 1 MPI processor grid
reading atoms ...
31 atoms
reading velocities ...
31 velocities
read_data CPU = 0.002 seconds
ACE version: 2023.11.25
Recursive evaluator is used
Loading Cu-1.yace
Total number of basis functions
Cu: 15 (r=1) 9 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 5.9
ghost atom cutoff = 5.9
binsize = 2.95, bins = 3 3 3
1 neighbor lists, perpetual/occasional/extra = 1 0 0
(1) pair pace, perpetual
attributes: full, newton on
pair build: full/bin/atomonly
stencil: full/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) = 4.908 | 4.908 | 4.908 Mbytes
Step PotEng Fnorm Fmax
0 -98.699376 49.367618 19.563052
Loop time of 6.74e-07 on 1 procs for 0 steps with 31 atoms
148.4% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 0 | 0 | 0 | 0.0 | 0.00
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0 | 0 | 0 | 0.0 | 0.00
Output | 0 | 0 | 0 | 0.0 | 0.00
Modify | 0 | 0 | 0 | 0.0 | 0.00
Other | | 6.74e-07 | | |100.00
Nlocal: 31 ave 31 max 31 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 616 ave 616 max 616 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 0 ave 0 max 0 min
Histogram: 1 0 0 0 0 0 0 0 0 0
FullNghs: 2132 ave 2132 max 2132 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 2132
Ave neighs/atom = 68.774194
Neighbor list builds = 0
Dangerous builds = 0
ACE version: 2023.11.25
Recursive evaluator is used by ACE
Loading Cu-1.yace
Total number of basis functions
Cu: 15 (r=1) 9 (r>1)
Mapping LAMMPS atom type #1(Cu) -> ACE species type #0
Setting atom values ...
31 settings made for apip/lambda
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 7.50679
ghost atom cutoff = 7.50679
binsize = 3.753395, bins = 2 2 2
2 neighbor lists, perpetual/occasional/extra = 2 0 0
(1) pair eam/fs/apip, perpetual
attributes: full, newton on, cut 7.50679
pair build: full/bin/atomonly
stencil: full/bin/3d
bin: standard
(2) pair pace/apip, 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) = 5.564 | 5.564 | 5.564 Mbytes
Step PotEng Fnorm Fmax
0 -98.699376 49.367618 19.563052
Loop time of 5.62e-07 on 1 procs for 0 steps with 31 atoms
177.9% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 0 | 0 | 0 | 0.0 | 0.00
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0 | 0 | 0 | 0.0 | 0.00
Output | 0 | 0 | 0 | 0.0 | 0.00
Modify | 0 | 0 | 0 | 0.0 | 0.00
Other | | 5.62e-07 | | |100.00
Nlocal: 31 ave 31 max 31 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 957 ave 957 max 957 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 0 ave 0 max 0 min
Histogram: 1 0 0 0 0 0 0 0 0 0
FullNghs: 4558 ave 4558 max 4558 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 4558
Ave neighs/atom = 147.03226
Neighbor list builds = 0
Dangerous builds = 0
Setting atom values ...
31 settings made for apip/lambda
Setting up Verlet run ...
Unit style : metal
Current step : 0
Time step : 0.001
Per MPI rank memory allocation (min/avg/max) = 5.564 | 5.564 | 5.564 Mbytes
Step PotEng Fnorm Fmax
0 -85.025323 53.839249 22.182149
Loop time of 5.49e-07 on 1 procs for 0 steps with 31 atoms
182.1% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 0 | 0 | 0 | 0.0 | 0.00
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0 | 0 | 0 | 0.0 | 0.00
Output | 0 | 0 | 0 | 0.0 | 0.00
Modify | 0 | 0 | 0 | 0.0 | 0.00
Other | | 5.49e-07 | | |100.00
Nlocal: 31 ave 31 max 31 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 957 ave 957 max 957 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 0 ave 0 max 0 min
Histogram: 1 0 0 0 0 0 0 0 0 0
FullNghs: 4558 ave 4558 max 4558 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 4558
Ave neighs/atom = 147.03226
Neighbor list builds = 0
Dangerous builds = 0
Neighbor list info ...
update: every = 1 steps, delay = 0 steps, check = yes
max neighbors/atom: 2000, page size: 100000
master list distance cutoff = 7.50679
ghost atom cutoff = 7.50679
binsize = 3.753395, bins = 2 2 2
1 neighbor lists, perpetual/occasional/extra = 1 0 0
(1) pair eam/fs, perpetual
attributes: half, newton on
pair build: half/bin/atomonly/newton
stencil: half/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) = 5.183 | 5.183 | 5.183 Mbytes
Step PotEng Fnorm Fmax
0 -85.025323 53.839249 22.182149
Loop time of 4.77e-07 on 1 procs for 0 steps with 31 atoms
0.0% CPU use with 1 MPI tasks x no OpenMP threads
MPI task timing breakdown:
Section | min time | avg time | max time |%varavg| %total
---------------------------------------------------------------
Pair | 0 | 0 | 0 | 0.0 | 0.00
Neigh | 0 | 0 | 0 | 0.0 | 0.00
Comm | 0 | 0 | 0 | 0.0 | 0.00
Output | 0 | 0 | 0 | 0.0 | 0.00
Modify | 0 | 0 | 0 | 0.0 | 0.00
Other | | 4.77e-07 | | |100.00
Nlocal: 31 ave 31 max 31 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Nghost: 957 ave 957 max 957 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Neighs: 2279 ave 2279 max 2279 min
Histogram: 1 0 0 0 0 0 0 0 0 0
Total # of neighbors = 2279
Ave neighs/atom = 73.516129
Neighbor list builds = 0
Dangerous builds = 0
Total wall time: 0:00:00

26
src/.gitignore vendored
View File

@ -109,6 +109,32 @@
/pair_pace_extrapolation.cpp
/pair_pace_extrapolation.h
/atom_vec_apip.cpp
/atom_vec_apip.h
/fix_lambda_apip.cpp
/fix_lambda_apip.h
/fix_lambda_thermostat_apip.cpp
/fix_lambda_thermostat_apip.h
/pair_lambda_input_apip.cpp
/pair_lambda_input_apip.h
/pair_lambda_input_csp_apip.cpp
/pair_lambda_input_csp_apip.h
/pair_eam_fs_apip.cpp
/pair_eam_fs_apip.h
/pair_eam_apip.cpp
/pair_eam_apip.h
/pair_lambda_zone_apip.cpp
/pair_lambda_zone_apip.h
/pair_pace_apip.cpp
/pair_pace_apip.h
/pair_pace_precise_apip.cpp
/pair_pace_precise_apip.h
/pair_pace_fast_apip.cpp
/pair_pace_fast_apip.h
/fix_atom_weight_apip.cpp
/fix_atom_weight_apip.h
/pair_pod.cpp
/pair_pod.h
/eapod.cpp

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

@ -0,0 +1,40 @@
# 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
}
# some styles in APIP have depend on the ML-PACE package
if (test $1 = 1) then
if (test ! -e ../pair_pace.cpp) then
echo "Must install ML-PACE package with APIP package"
exit 1
fi
fi
for file in *.cpp *.h; do
action ${file}
done

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 .

124
src/APIP/atom_vec_apip.cpp Normal file
View File

@ -0,0 +1,124 @@
/* ----------------------------------------------------------------------
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 *apip_lambda, *apip_lambda_input, *apip_lambda_input_ta, *apip_lambda_const, *apip_e_fast,
*apip_e_precise, **apip_f_const_lambda, **apip_f_dyn_lambda;
int *apip_lambda_required;
atom->apip_lambda_flag = 1;
atom->apip_lambda_input_flag = 1;
atom->apip_lambda_input_ta_flag = 1;
atom->apip_lambda_const_flag = 1;
atom->apip_lambda_required_flag = 1;
atom->apip_e_fast_flag = 1;
atom->apip_e_precise_flag = 1;
atom->apip_f_const_lambda_flag = 1;
atom->apip_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 = {"apip_lambda", "apip_lambda_required", "apip_lambda_input", "apip_lambda_input_ta",
"apip_lambda_const"};
fields_comm = {"apip_lambda", "apip_lambda_required", "apip_lambda_input_ta",
"apip_lambda_const"};
fields_comm_vel = {};
fields_border = {"apip_lambda", "apip_lambda_required", "apip_lambda_input_ta",
"apip_lambda_const"};
fields_border_vel = {};
fields_exchange = {"apip_lambda", "apip_lambda_required", "apip_lambda_input_ta",
"apip_lambda_const"};
fields_restart = {"apip_lambda", "apip_lambda_required", "apip_lambda_input",
"apip_lambda_input_ta", "apip_lambda_const"};
fields_create = {};
fields_grow = {
"apip_lambda", "apip_lambda_required", "apip_lambda_input",
"apip_lambda_input_ta", "apip_lambda_const", "apip_e_fast",
"apip_e_precise", "apip_f_const_lambda", "apip_f_dyn_lambda"}; // allocates memory
fields_reverse = {"apip_f_const_lambda",
"apip_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()
{
apip_lambda = atom->apip_lambda;
apip_lambda_required = atom->apip_lambda_required;
apip_lambda_input = atom->apip_lambda_input;
apip_lambda_input_ta = atom->apip_lambda_input_ta;
apip_lambda_const = atom->apip_lambda_const;
apip_e_fast = atom->apip_e_fast;
apip_e_precise = atom->apip_e_precise;
apip_f_const_lambda = atom->apip_f_const_lambda;
apip_f_dyn_lambda = atom->apip_f_dyn_lambda;
}
/* ----------------------------------------------------------------------
modify what AtomVec::data_atom() just unpacked
or initialize other atom quantities
------------------------------------------------------------------------- */
void AtomVecApip::data_atom_post(int ilocal)
{
apip_lambda[ilocal] = 0;
apip_lambda_const[ilocal] = 0;
apip_lambda_required[ilocal] = ApipLambdaRequired::UNKNOWN;
apip_lambda_input[ilocal] = 0;
apip_lambda_input_ta[ilocal] = 0;
apip_e_fast[ilocal] = 0;
apip_e_precise[ilocal] = 0;
apip_f_const_lambda[ilocal][0] = 0;
apip_f_const_lambda[ilocal][1] = 0;
apip_f_const_lambda[ilocal][2] = 0;
apip_f_dyn_lambda[ilocal][0] = 0;
apip_f_dyn_lambda[ilocal][1] = 0;
apip_f_dyn_lambda[ilocal][2] = 0;
}
/* ----------------------------------------------------------------------
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(&apip_f_const_lambda[n][0], 0, 3 * nbytes);
memset(&apip_f_dyn_lambda[n][0], 0, 3 * nbytes);
memset(&apip_lambda_required[n], 0, nbytes / sizeof(double) * sizeof(int));
}

59
src/APIP/atom_vec_apip.h Normal file
View File

@ -0,0 +1,59 @@
/* -*- 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;
void data_atom_post(int) override;
protected:
double *apip_lambda, *apip_lambda_input, *apip_lambda_const, *apip_lambda_input_ta, *apip_e_fast,
*apip_e_precise, **apip_f_const_lambda, **apip_f_dyn_lambda;
int *apip_lambda_required;
};
#ifndef LMP_ATOM_APIP_LAMBDA_REQUIRED
#define LMP_ATOM_APIP_LAMBDA_REQUIRED
namespace ApipLambdaRequired {
enum {
UNKNOWN = 0,
SIMPLE = 1 << 0,
NO_SIMPLE = 1 << 1,
COMPLEX = 1 << 2,
NO_COMPLEX = 1 << 3,
};
} // namespace ApipLambdaRequired
#endif
} // namespace LAMMPS_NS
#endif
#endif

View File

@ -0,0 +1,554 @@
/* ----------------------------------------------------------------------
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_atom_weight_apip.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;
FixAtomWeightAPIP::FixAtomWeightAPIP(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/fast/apip: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/apip: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/apip: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, "atom_weight/apip: 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, "atom_weight/apip: unknown argument {}", arg[iarg]);
}
}
// check arguments
if (nevery < 1) error->all(FLERR, "atom_weight/apip: nevery > 0 required");
if (!atom->apip_lambda_required_flag)
error->all(FLERR, "atom_weight/apip: 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, "atom_weight/apip: 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, "atom_weight/apip: simple time cannot be extracted with {} from {}",
time_simple_extract_name, force->pair_style);
} else {
if (time_simple_atom < 0)
error->all(FLERR, "atom_weight/apip: time_simple_atom needs to be non-negative instead of {}",
time_simple_atom);
}
if (time_complex_extract_name) {
if (force->pair->extract(time_complex_extract_name, useless_dim) == nullptr)
error->all(FLERR, "atom_weight/apip: complex time cannot be extracted with {} from {}",
time_complex_extract_name, force->pair_style);
} else {
if (time_complex_atom < 0)
error->all(FLERR,
"atom_weight/apip: time_complex_atom needs to be non-negative instead of {}",
time_complex_atom);
}
if (time_group_extract_name) {
if (force->pair->extract(time_group_extract_name, useless_dim) == nullptr)
error->all(FLERR, "atom_weight/apip: group time cannot be extracted with {} from {}",
time_group_extract_name, force->pair_style);
} else {
if (time_group_atom < 0)
error->all(FLERR, "atom_weight/apip: time_group_atom needs to be non-negative instead of {}",
time_group_atom);
}
if (time_lambda_extract_name) {
if (force->pair->extract(time_lambda_extract_name, useless_dim) == nullptr)
error->all(FLERR, "atom_weight/apip: lambda time cannot be extracted with {} from {}",
time_lambda_extract_name, force->pair_style);
} else {
if (time_lambda_atom < 0)
error->all(FLERR, "atom_weight/apip: time_lambda_atom needs to be non-negative 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.
*/
FixAtomWeightAPIP::~FixAtomWeightAPIP()
{
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 FixAtomWeightAPIP::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 FixAtomWeightAPIP::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 FixAtomWeightAPIP::init()
{
int counter = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "atom_weight/apip") == 0) counter++;
}
if (counter > 1) error->all(FLERR, "More than one atom_weight/apip fix");
// get ptr to fix lambda
counter = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "lambda/apip") == 0) {
fix_lambda = modify->fix[i];
counter++;
}
}
if (counter > 1) error->all(FLERR, "More than one fix lambda");
if (counter == 0 && (time_lambda_extract_name || time_lambda_atom > 0))
error->all(FLERR, "fix lambda required to approximate weight of pair style lambda/zone");
// 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, "atom_weight/apip: 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, "atom_weight/apip: 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 FixAtomWeightAPIP::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 FixAtomWeightAPIP::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 FixAtomWeightAPIP::pre_exchange()
{
if (update->ntimestep % peratom_freq != 0) { return; }
calc_work_per_particle();
}
/**
* Update output pointer for output.
*/
void FixAtomWeightAPIP::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 FixAtomWeightAPIP::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->apip_lambda_required;
int nlocal = atom->nlocal;
// assume a homogeneous time per simple and complex particle
n_simple = n_complex = 0;
for (int i = 0; i < nlocal; i++) {
work_atom = 0;
if (lambda_required[i] & ApipLambdaRequired::SIMPLE) {
work_atom += time_simple_atom;
n_simple++;
}
if (lambda_required[i] & ApipLambdaRequired::COMPLEX) {
work_atom += time_complex_atom;
n_complex++;
}
weight[i] = work_atom;
}
n_group = 0;
if (time_group_atom > 0) {
for (int i = 0; i < nlocal; i++) {
if (mask[i] & time_group_bit) {
weight[i] += time_group_atom;
n_group++;
}
}
}
n_lambda = 0;
if (time_lambda_atom > 0) {
// 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, "atom_weight/apip: extracting fix_lambda:lambda_input_history failed");
for (int i = 0; i < nlocal; i++) {
if (lambda_input_history[i][histlen_lambda_input + 1] != 1) {
weight[i] += time_lambda_atom;
n_lambda++;
}
}
}
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 FixAtomWeightAPIP::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(atom_weight/apip,FixAtomWeightAPIP);
// clang-format on
#else
#ifndef LMP_FIX_ATOM_WEIGHT_APIP_H
#define LMP_FIX_ATOM_WEIGHT_APIP_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 FixAtomWeightAPIP : public Fix {
public:
FixAtomWeightAPIP(class LAMMPS *, int, char **);
~FixAtomWeightAPIP() 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

View File

@ -0,0 +1,784 @@
/* ----------------------------------------------------------------------
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_apip.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";
/* ---------------------------------------------------------------------- */
FixLambdaAPIP::FixLambdaAPIP(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->apip_lambda_const_flag) {
error->all(FLERR, "fix lambda requires atomic style with lambda_const.");
}
if (!atom->apip_lambda_flag) {
error->all(FLERR, "fix lambda requires atomic style with lambda.");
}
if (!atom->apip_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 FixLambdaAPIP::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;
}
/* ---------------------------------------------------------------------- */
FixLambdaAPIP::~FixLambdaAPIP()
{
// 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 FixLambdaAPIP::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 FixLambdaAPIP::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/apip") == 0) count++;
}
if (count > 1) error->all(FLERR, "More than one fix lambda.");
// warn if there is no fix lambda_thermostat/apip
if (comm->me == 0 && modify->get_fix_by_style("lambda_thermostat/apip").size() == 0)
error->warning(FLERR,
"The energy is not conserved when lambda changes as fix lambda_thermostat/apip "
"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 = (PairLambdaInputAPIP *) pair_tmp;
// lambda/zone
pair_tmp = force->pair_match("lambda/zone/apip", 1);
if (!pair_tmp) error->all(FLERR, "fix lambda requires a `pair lambda`");
pair_lambda_zone = (PairLambdaZoneAPIP *) 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 FixLambdaAPIP::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 FixLambdaAPIP::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 FixLambdaAPIP::post_integrate()
{
double *lambda, *lambda_const, *lambda_input_ta;
lambda = atom->apip_lambda;
lambda_const = atom->apip_lambda_const;
lambda_input_ta = atom->apip_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_apip.cpp
calculate_lambda_input_ta();
}
/**
* write stats at end of step
*/
void FixLambdaAPIP::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 FixLambdaAPIP::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->apip_lambda;
lambda_const = atom->apip_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 FixLambdaAPIP::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 FixLambdaAPIP::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->apip_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 FixLambdaAPIP::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->apip_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 FixLambdaAPIP::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->apip_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 FixLambdaAPIP::calculate_lambda_input_ta()
{
int i, nlocal;
int *mask;
double *lambda_input_ta = atom->apip_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 FixLambdaAPIP::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 FixLambdaAPIP::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 FixLambdaAPIP::pack_forward_comm(int n, int *list, double *buf, int /*pbc_flag*/, int * /*pbc*/)
{
int i, j, m;
double *lambda_input_ta = atom->apip_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->apip_lambda[j];
buf[m++] = atom->apip_lambda_const[j];
}
}
return m;
}
/**
* Recv lambda from neighbours.
*/
void FixLambdaAPIP::unpack_forward_comm(int n, int first, double *buf)
{
int i, m, last;
double *lambda_input_ta = atom->apip_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->apip_lambda[i] = buf[m++];
atom->apip_lambda_const[i] = buf[m++];
}
}
}
/**
* store scalar history information
*/
void FixLambdaAPIP::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 FixLambdaAPIP::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 *FixLambdaAPIP::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_apip.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/apip,FixLambdaAPIP);
// clang-format on
#else
#ifndef LMP_FIX_LAMBDA_APIP_H
#define LMP_FIX_LAMBDA_APIP_H
#include "fix.h"
#include "pair_lambda_input_apip.h"
#include "pair_lambda_zone_apip.h"
namespace LAMMPS_NS {
class FixLambdaAPIP : public Fix {
friend class PairLambdaInputAPIP;
friend class PairLambdaZoneAPIP;
public:
FixLambdaAPIP(class LAMMPS *, int, char **);
~FixLambdaAPIP() 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 PairLambdaInputAPIP *pair_lambda_input;
class PairLambdaZoneAPIP *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,635 @@
/* ----------------------------------------------------------------------
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_apip.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
/* ---------------------------------------------------------------------- */
FixLambdaThermostatAPIP::FixLambdaThermostatAPIP(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/apip: seed requires one argument");
seed = utils::inumeric(FLERR, arg[iarg + 1], false, lmp);
if (seed <= 0) { error->all(FLERR, "fix lambda_thermostat/apip seed <= 0"); }
iarg++;
} else if (strcmp(arg[iarg], "store_atomic_forces") == 0) {
if (iarg + 1 >= narg)
error->all(FLERR, "fix lambda_thermostat/apip: 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/apip: 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/apip: 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/apip: unknown argument {}", arg[iarg]);
}
// error checks
if (!atom->apip_e_fast_flag) {
error->all(FLERR, "fix lambda_thermostat/apip requires atomic style with e_simple.");
}
if (!atom->apip_e_precise_flag) {
error->all(FLERR, "fix lambda_thermostat/apip requires atomic style with e_complex.");
}
if (!atom->apip_lambda_const_flag) {
error->all(FLERR, "fix lambda_thermostat/apip requires atomic style with lambda_const.");
}
if (!atom->apip_lambda_flag) {
error->all(FLERR, "fix lambda_thermostat/apip requires atomic style with lambda.");
}
if (!atom->apip_f_const_lambda_flag) {
error->all(FLERR, "fix lambda_thermostat/apip requires atomic style with f_const_lambda.");
}
if (!atom->apip_f_dyn_lambda_flag) {
error->all(FLERR, "fix lambda_thermostat/apip requires atomic style with f_dyn_lambda.");
}
if (rescaling_N_neighbours <= 1)
error->all(FLERR, "fix lambda_thermostat/apip: 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/apip: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;
}
/* ---------------------------------------------------------------------- */
FixLambdaThermostatAPIP::~FixLambdaThermostatAPIP()
{
memory->destroy(energy_change_atom);
memory->destroy(peratom_stats);
memory->destroy(local_numneigh);
memory->sfree(local_firstneigh);
memory->destroy(jlist_copy);
delete[] ipage;
}
/* ---------------------------------------------------------------------- */
int FixLambdaThermostatAPIP::setmask()
{
int mask = 0;
mask |= POST_FORCE;
mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostatAPIP::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/apip") == 0) counter++;
if (counter > 1)
error->all(FLERR, "fix lambda_thermostat/apip: more than one fix lambda_thermostat/apip");
// 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/apip: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 FixLambdaThermostatAPIP::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 FixLambdaThermostatAPIP::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/apip:numneigh");
local_firstneigh =
(int **) memory->smalloc(nmax_list * sizeof(int *), "lambda_thermostat/apip: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 FixLambdaThermostatAPIP::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 FixLambdaThermostatAPIP::post_force(int /*vflag*/)
{
init_peratom_stats();
calculate_energy_change();
}
/* ---------------------------------------------------------------------- */
void FixLambdaThermostatAPIP::end_of_step()
{
apply_thermostat();
}
/**
* Init per-atom array with zeros.
*/
void FixLambdaThermostatAPIP::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/apip: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 FixLambdaThermostatAPIP::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->apip_e_fast;
double *e_complex = atom->apip_e_precise;
double *lambda_const = atom->apip_lambda_const;
double *lambda = atom->apip_lambda;
double **f_const_lambda = atom->apip_f_const_lambda;
double **f_dyn_lambda = atom->apip_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/apip: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 FixLambdaThermostatAPIP::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->apip_e_fast;
double *e_complex = atom->apip_e_precise;
double *lambda_const = atom->apip_lambda_const;
double *lambda = atom->apip_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/apip: 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/apip: 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 FixLambdaThermostatAPIP::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 FixLambdaThermostatAPIP::reset_dt()
{
dtf = 0.5 * update->dt * force->ftm2v;
}
/* ----------------------------------------------------------------------
memory usage of local atom-based arrays
------------------------------------------------------------------------- */
double FixLambdaThermostatAPIP::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/apip,FixLambdaThermostatAPIP);
// clang-format on
#else
#ifndef LMP_FIX_LAMBDA_THERMOSTAT_APIP_H
#define LMP_FIX_LAMBDA_THERMOSTAT_APIP_H
#include "fix.h"
#include <random>
namespace LAMMPS_NS {
class FixLambdaThermostatAPIP : public Fix {
public:
FixLambdaThermostatAPIP(class LAMMPS *, int, char **);
~FixLambdaThermostatAPIP() 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->apip_lambda;
int *lambda_required = atom->apip_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->apip_f_const_lambda;
f_dyn_lambda = atom->apip_f_dyn_lambda;
e_simple = atom->apip_e_fast;
lambda_const = atom->apip_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] & ApipLambdaRequired::NO_SIMPLE)
continue;
else if (!(lambda_required[i] & ApipLambdaRequired::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] |= ApipLambdaRequired::SIMPLE;
// set neighbour list
for (jj = 0; jj < jnum; jj++) {
j = jlist[jj];
j &= NEIGHMASK;
if (j < nlocal) lambda_required[j] |= ApipLambdaRequired::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] |= ApipLambdaRequired::SIMPLE;
// set lambda also for non-ghost j
if (j < nlocal) lambda_required[j] |= ApipLambdaRequired::SIMPLE;
break;
}
}
}
// SIMPLE not set -> set NO_SIMPLE
if (!(lambda_required[i] & ApipLambdaRequired::SIMPLE)) {
// go to next atom
lambda_required[i] |= ApipLambdaRequired::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->apip_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->apip_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/apip or not and set
* lambda_thermostat.
*/
void PairEAMAPIP::setup()
{
if (modify->get_fix_by_style("^lambda_thermostat/apip$").size() == 0) {
lambda_thermostat = false;
} else {
lambda_thermostat = true;
if (!atom->apip_lambda_const_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with lambda_const for a local thermostat.");
if (!atom->apip_e_fast_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with e_simple for a local thermostat.");
if (!atom->apip_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->apip_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_apip.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;
/* ---------------------------------------------------------------------- */
PairLambdaInputAPIP::PairLambdaInputAPIP(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;
}
/* ---------------------------------------------------------------------- */
PairLambdaInputAPIP::~PairLambdaInputAPIP()
{
if (copymode) return;
if (allocated) {
memory->destroy(setflag);
memory->destroy(cutsq);
memory->destroy(cut);
}
}
/* ---------------------------------------------------------------------- */
void PairLambdaInputAPIP::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 PairLambdaInputAPIP::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 PairLambdaInputAPIP::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 PairLambdaInputAPIP::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 PairLambdaInputAPIP::init_style()
{
if (!atom->apip_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/apip") == 0) {
fix_lambda = (FixLambdaAPIP *) 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 PairLambdaInputAPIP::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->apip_lambda_input.
* Count the number of computations and measure the compute time for
* fix atom_weight/apip.
*/
int PairLambdaInputAPIP::calculate_lambda_input()
{
int i, ii, inum;
int *ilist;
inum = list->inum;
ilist = list->ilist;
double *lambda_input = atom->apip_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 PairLambdaInputAPIP::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 *PairLambdaInputAPIP::extract(const char *str, int &dim)
{
dim = 0;
if (strcmp(str, "lambda/input/apip: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/apip,PairLambdaInputAPIP);
// clang-format on
#else
#ifndef LMP_PAIR_LAMBDA_INPUT_APIP_H
#define LMP_PAIR_LAMBDA_INPUT_APIP_H
#include "fix_lambda_apip.h"
#include "pair.h"
namespace LAMMPS_NS {
class PairLambdaInputAPIP : public Pair {
friend class FixLambdaAPIP;
public:
PairLambdaInputAPIP(class LAMMPS *);
~PairLambdaInputAPIP() 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 FixLambdaAPIP *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_apip.h"
#include "atom.h"
#include "error.h"
#include "memory.h"
#include "neigh_list.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairLambdaInputCSPAPIP::PairLambdaInputCSPAPIP(LAMMPS *lmp) :
PairLambdaInputAPIP(lmp), distsq(nullptr), nearest(nullptr)
{
// set defaults
nnn = 0;
nnn_buffer = 0;
maxneigh = 0;
cut_csp_sq = -1;
}
/* ---------------------------------------------------------------------- */
PairLambdaInputCSPAPIP::~PairLambdaInputCSPAPIP()
{
if (copymode) return;
memory->destroy(distsq);
memory->destroy(nearest);
}
/* ----------------------------------------------------------------------
global settings
------------------------------------------------------------------------- */
void PairLambdaInputCSPAPIP::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->apip_lambda_input.
* Count the number of computations and measure the compute time for
* fix atom_weight/apip.
*/
int PairLambdaInputCSPAPIP::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->apip_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 PairLambdaInputCSPAPIP::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 PairLambdaInputCSPAPIP::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/apip,PairLambdaInputCSPAPIP);
// clang-format on
#else
#ifndef LMP_PAIR_LAMBDA_INPUT_CSP_APIP_H
#define LMP_PAIR_LAMBDA_INPUT_CSP_APIP_H
#include "pair_lambda_input_apip.h"
namespace LAMMPS_NS {
class PairLambdaInputCSPAPIP : virtual public PairLambdaInputAPIP {
public:
PairLambdaInputCSPAPIP(class LAMMPS *);
~PairLambdaInputCSPAPIP();
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,311 @@
/* ----------------------------------------------------------------------
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_apip.h"
#include "atom.h"
#include "error.h"
#include "fix.h"
#include "memory.h"
#include "modify.h"
#include "neigh_list.h"
#include "neighbor.h"
using namespace LAMMPS_NS;
/* ---------------------------------------------------------------------- */
PairLambdaZoneAPIP::PairLambdaZoneAPIP(LAMMPS *lmp) :
Pair(lmp), 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;
}
/* ---------------------------------------------------------------------- */
PairLambdaZoneAPIP::~PairLambdaZoneAPIP()
{
if (copymode) return;
if (allocated) {
memory->destroy(setflag);
memory->destroy(cutsq);
memory->destroy(cut);
}
if (nmax_ta > 0) memory->destroy(lambda_ta);
}
/* ---------------------------------------------------------------------- */
void PairLambdaZoneAPIP::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 PairLambdaZoneAPIP::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 PairLambdaZoneAPIP::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 PairLambdaZoneAPIP::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 PairLambdaZoneAPIP::init_style()
{
if (!atom->apip_lambda_input_ta_flag)
error->all(FLERR, "pair_lambda_zone requires an atom style with lambda_input_ta");
// find fix lambda/apip
class Fix * fix_lambda = nullptr;
int count = 0;
for (int i = 0; i < modify->nfix; i++) {
if (strcmp(modify->fix[i]->style, "lambda/apip") == 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 PairLambdaZoneAPIP::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 PairLambdaZoneAPIP::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->apip_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 PairLambdaZoneAPIP::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 PairLambdaZoneAPIP::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 *PairLambdaZoneAPIP::extract(const char *str, int &dim)
{
dim = 0;
if (strcmp(str, "lambda/zone/apip:time_per_atom") == 0) {
calculate_time_per_atom();
return (void *) &time_per_atom;
}
return nullptr;
}

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)
------------------------------------------------------------------------- */
#ifdef PAIR_CLASS
// clang-format off
PairStyle(lambda/zone/apip,PairLambdaZoneAPIP);
// clang-format on
#else
#ifndef LMP_PAIR_LAMBDA_ZONE_APIP_H
#define LMP_PAIR_LAMBDA_ZONE_APIP_H
#include "pair.h"
namespace LAMMPS_NS {
class PairLambdaZoneAPIP : public Pair {
friend class FixLambdaAPIP;
public:
PairLambdaZoneAPIP(class LAMMPS *);
~PairLambdaZoneAPIP() 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:
// 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 the 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] |= ApipLambdaRequired::NO_COMPLEX;
return 1;
}
lambda_required[i] |= ApipLambdaRequired::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->apip_e_precise 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->apip_e_precise;
}
/* ---------------------------------------------------------------------- */
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->apip_lambda;
int *lambda_required = atom->apip_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->apip_f_const_lambda;
f_dyn_lambda = atom->apip_f_dyn_lambda;
e_ref = get_e_ref_ptr();
lambda_const = atom->apip_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->apip_lambda_required_flag)
error->all(FLERR, "pair style pace/apip requires an atom style with lambda_required.");
if (!atom->apip_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/apip 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/apip$").size() == 0) {
lambda_thermostat = false;
} else {
lambda_thermostat = true;
if (!atom->apip_lambda_const_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with lambda_const for a local thermostat.");
if (!atom->apip_e_fast_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with e_simple for a local thermostat.");
if (!atom->apip_e_precise_flag)
error->all(
FLERR,
"Pair style pace/apip requires an atom style with e_complex for a local thermostat.");
if (!atom->apip_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->apip_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_fast_apip.h"
#include "atom.h"
#include "atom_vec_apip.h"
using namespace LAMMPS_NS;
PairPACEFastAPIP::PairPACEFastAPIP(LAMMPS *lmp) : PairPACEAPIP(lmp) {}
/**
* Set lambda_required based on lambda and lambda_const
* @return true if this calculation is not required
*/
int PairPACEFastAPIP::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] |= ApipLambdaRequired::NO_SIMPLE;
return 1;
}
lambda_required[i] |= ApipLambdaRequired::SIMPLE;
return 0;
}
/**
* @return prefactor lambda which is used for a fast ACE potential
*/
double PairPACEFastAPIP::compute_factor_lambda(double lambda)
{
return lambda;
}
/**
* @return atom->apip_e_fast which is used for a fast ACE potential
*/
double *PairPACEFastAPIP::get_e_ref_ptr()
{
return atom->apip_e_fast;
}
/* ----------------------------------------------------------------------
extract method for extracting value of scale variable
---------------------------------------------------------------------- */
void *PairPACEFastAPIP::extract(const char *str, int &dim)
{
dim = 2;
if (strcmp(str, "scale") == 0) return (void *) scale;
dim = 0;
if (strcmp(str, "pace/fast/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/fast/apip,PairPACEFastAPIP);
// clang-format on
#else
#ifndef LMP_PAIR_PACE_FAST_APIP_H
#define LMP_PAIR_PACE_FAST_APIP_H
#include "pair_pace_apip.h"
namespace LAMMPS_NS {
class PairPACEFastAPIP : public PairPACEAPIP {
public:
PairPACEFastAPIP(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_precise_apip.h"
#include "atom.h"
#include "atom_vec_apip.h"
using namespace LAMMPS_NS;
PairPACEPreciseAPIP::PairPACEPreciseAPIP(LAMMPS *lmp) : PairPACEAPIP(lmp) {}
/**
* Set lambda_required based on lambda and lambda_const
* @return true if this calculation is not required
*/
int PairPACEPreciseAPIP::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] |= ApipLambdaRequired::NO_COMPLEX;
return 1;
}
lambda_required[i] |= ApipLambdaRequired::COMPLEX;
return 0;
}
/**
* @return prefactor 1-lambda which is used for a precise ACE potential
*/
double PairPACEPreciseAPIP::compute_factor_lambda(double lambda)
{
return 1 - lambda;
}
/**
* @return atom->apip_e_precise which is used for a precise ACE potential
*/
double *PairPACEPreciseAPIP::get_e_ref_ptr()
{
return atom->apip_e_precise;
}
/* ----------------------------------------------------------------------
extract method for extracting value of scale variable
---------------------------------------------------------------------- */
void *PairPACEPreciseAPIP::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/precise/apip,PairPACEPreciseAPIP);
// clang-format on
#else
#ifndef LMP_PAIR_PACE_PRECISE_APIP_H
#define LMP_PAIR_PACE_PRECISE_APIP_H
#include "pair_pace_apip.h"
namespace LAMMPS_NS {
class PairPACEPreciseAPIP : public PairPACEAPIP {
public:
PairPACEPreciseAPIP(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

@ -53,6 +53,7 @@ PACKAGE = \
kspace \
adios \
amoeba \
apip \
asphere \
awpmd \
bocs \
@ -219,6 +220,7 @@ PACKLIB = \
python \
voronoi \
adios \
apip \
atc \
awpmd \
colvars \
@ -246,6 +248,7 @@ PACKINT = atc awpmd colvars electrode gpu kokkos lepton ml-pod poems
PACKEXT = \
adios \
apip \
h5md \
kim \
machdyn \

View File

@ -220,6 +220,12 @@ Atom::Atom(LAMMPS *_lmp) : Pointers(_lmp), atom_style(nullptr), avec(nullptr), a
area = ed = em = epsilon = curvature = q_scaled = nullptr;
// APIP package
apip_lambda_const = apip_lambda = apip_lambda_input = apip_lambda_input_ta = apip_e_fast = apip_e_precise = nullptr;
apip_lambda_required = nullptr;
apip_f_const_lambda = apip_f_dyn_lambda = nullptr;
// end of customization section
// --------------------------------------------------------------------
@ -575,6 +581,18 @@ void Atom::peratom_create()
add_peratom("curvature",&curvature,DOUBLE,0);
add_peratom("q_scaled",&q_scaled,DOUBLE,0);
// APIP package
add_peratom("apip_lambda",&apip_lambda,DOUBLE,0);
add_peratom("apip_lambda_required",&apip_lambda_required,INT,0);
add_peratom("apip_lambda_input",&apip_lambda_input,DOUBLE,0);
add_peratom("apip_lambda_input_ta",&apip_lambda_input_ta,DOUBLE,0);
add_peratom("apip_e_fast",&apip_e_fast,DOUBLE,0);
add_peratom("apip_e_precise",&apip_e_precise,DOUBLE,0);
add_peratom("apip_lambda_const",&apip_lambda_const,DOUBLE,0);
add_peratom("apip_f_const_lambda",&apip_f_const_lambda,DOUBLE,3,1);
add_peratom("apip_f_dyn_lambda",&apip_f_dyn_lambda,DOUBLE,3,1);
// end of customization section
// --------------------------------------------------------------------
}
@ -658,6 +676,7 @@ void Atom::set_atomflag_defaults()
contact_radius_flag = smd_data_9_flag = smd_stress_flag = 0;
eff_plastic_strain_flag = eff_plastic_strain_rate_flag = 0;
nspecial15_flag = 0;
apip_lambda_flag = apip_e_fast_flag = apip_e_precise_flag = apip_lambda_input_flag = apip_lambda_input_ta_flag = apip_lambda_required_flag = apip_f_const_lambda_flag = apip_f_dyn_lambda_flag = apip_lambda_const_flag = 0;
pdscale = 1.0;
}
@ -3167,6 +3186,18 @@ void *Atom::extract(const char *name)
if (strcmp(name,"curvature") == 0) return (void *) curvature;
if (strcmp(name,"q_scaled") == 0) return (void *) q_scaled;
// APIP package
if (strcmp(name,"apip_lambda") == 0) return (void *) apip_lambda;
if (strcmp(name,"apip_lambda_required") == 0) return (void *) apip_lambda_required;
if (strcmp(name,"apip_lambda_input") == 0) return (void *) apip_lambda_input;
if (strcmp(name,"apip_lambda_input_ta") == 0) return (void *) apip_lambda_input_ta;
if (strcmp(name,"apip_e_fast") == 0) return (void *) apip_e_fast;
if (strcmp(name,"apip_e_precise") == 0) return (void *) apip_e_precise;
if (strcmp(name,"apip_f_const_lambda") == 0) return (void *) apip_f_const_lambda;
if (strcmp(name,"apip_f_dyn_lambda") == 0) return (void *) apip_f_dyn_lambda;
if (strcmp(name,"apip_lambda_const") == 0) return (void *) apip_lambda_const;
// end of customization section
// --------------------------------------------------------------------
@ -3325,6 +3356,17 @@ int Atom::extract_datatype(const char *name)
if (strcmp(name,"curvature") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"q_unscaled") == 0) return LAMMPS_DOUBLE;
// APIP package
if (strcmp(name,"apip_lambda") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"apip_lambda_required") == 0) return LAMMPS_INT;
if (strcmp(name,"apip_lambda_input") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"apip_lambda_input_ta") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"apip_e_fast") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"apip_e_precise") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"apip_lambda_const") == 0) return LAMMPS_DOUBLE;
if (strcmp(name,"apip_f_const_lambda") == 0) return LAMMPS_DOUBLE_2D;
if (strcmp(name,"apip_f_dyn_lambda") == 0) return LAMMPS_DOUBLE_2D;
// end of customization section
// --------------------------------------------------------------------
@ -3461,6 +3503,18 @@ int Atom::extract_size(const char *name, int type)
if (strcmp(name, "smd_data_9") == 0) return 9;
if (strcmp(name, "smd_stress") == 0) return 6;
// APIP package
if (strcmp(name, "apip_lambda") == 0) return nlocal;
if (strcmp(name, "apip_lambda_required") == 0) return nlocal;
if (strcmp(name, "apip_lambda_input") == 0) return nlocal;
if (strcmp(name, "apip_lambda_input_ta") == 0) return nlocal;
if (strcmp(name, "apip_e_fast") == 0) return nlocal;
if (strcmp(name, "apip_e_precise") == 0) return nlocal;
if (strcmp(name, "apip_lambda_const") == 0) return nlocal;
if (strcmp(name, "apip_f_const_lambda") == 0) return nall;
if (strcmp(name, "apip_f_dyn_lambda") == 0) return nall;
}
// custom arrays

View File

@ -179,6 +179,11 @@ class Atom : protected Pointers {
double *area, *ed, *em, *epsilon, *curvature, *q_scaled;
// APIP package
double *apip_lambda, *apip_lambda_input, *apip_lambda_input_ta, *apip_e_fast, *apip_e_precise, **apip_f_const_lambda, **apip_f_dyn_lambda, *apip_lambda_const;
int *apip_lambda_required;
// end of customization section
// --------------------------------------------------------------------
@ -227,6 +232,10 @@ class Atom : protected Pointers {
int dielectric_flag;
// APIP package
int apip_lambda_flag, apip_e_fast_flag, apip_e_precise_flag, apip_lambda_input_flag, apip_lambda_input_ta_flag, apip_lambda_required_flag, apip_f_const_lambda_flag, apip_f_dyn_lambda_flag, apip_lambda_const_flag;
// end of customization section
// --------------------------------------------------------------------

View File

@ -331,6 +331,25 @@ ComputePropertyAtom::ComputePropertyAtom(LAMMPS *lmp, int narg, char **arg) :
error->all(FLERR,"Compute property/atom {} requires atom style tri", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_corner3z;
// APIP package
} else if (strcmp(arg[iarg],"apip_lambda") == 0) {
if (!atom->apip_lambda_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_apip_lambda;
} else if (strcmp(arg[iarg],"apip_lambda_input") == 0) {
if (!atom->apip_lambda_input_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_apip_lambda_input;
} else if (strcmp(arg[iarg],"apip_e_fast") == 0) {
if (!atom->apip_e_fast_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_apip_e_fast;
} else if (strcmp(arg[iarg],"apip_e_precise") == 0) {
if (!atom->apip_e_precise_flag)
error->all(FLERR,"Compute property/atom {} is not available", arg[iarg]);
pack_choice[i] = &ComputePropertyAtom::pack_apip_e_precise;
// custom per-atom vector or array
} else if (utils::strmatch(arg[iarg],"^[id]2?_")) {
@ -1564,6 +1583,66 @@ void ComputePropertyAtom::pack_tqz(int n)
/* ---------------------------------------------------------------------- */
void ComputePropertyAtom::pack_apip_lambda(int n)
{
double *lambda = atom->apip_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_apip_lambda_input(int n)
{
double *lambda_input = atom->apip_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_apip_e_fast(int n)
{
double *e_simple = atom->apip_e_fast;
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_apip_e_precise(int n)
{
double *e_complex = atom->apip_e_precise;
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)
{
AtomVecLine::Bonus *bonus = avec_line->bonus;

View File

@ -137,6 +137,11 @@ class ComputePropertyAtom : public Compute {
void pack_d2name(int);
void pack_atom_style(int);
void pack_apip_lambda(int);
void pack_apip_lambda_input(int);
void pack_apip_e_fast(int);
void pack_apip_e_precise(int);
};
} // namespace LAMMPS_NS

View File

@ -815,6 +815,7 @@ void ReadDump::process_atoms()
double **x = atom->x;
double **v = atom->v;
double *q = atom->q;
double *apip_lambda = atom->apip_lambda;
double **f = atom->f;
tagint *tag = atom->tag;
imageint *image = atom->image;
@ -864,6 +865,9 @@ void ReadDump::process_atoms()
case Reader::Q:
q[m] = fields[i][ifield];
break;
case Reader::APIP_LAMBDA:
apip_lambda[m] = fields[i][ifield];
break;
case Reader::VY:
v[m][1] = fields[i][ifield];
break;
@ -979,6 +983,7 @@ void ReadDump::process_atoms()
tag = atom->tag;
v = atom->v;
q = atom->q;
apip_lambda = atom->apip_lambda;
image = atom->image;
// set atom attributes from other dump file fields
@ -1003,6 +1008,9 @@ void ReadDump::process_atoms()
case Reader::Q:
q[m] = fields[i][ifield];
break;
case Reader::APIP_LAMBDA:
apip_lambda[m] = fields[i][ifield];
break;
case Reader::IX:
xbox = static_cast<int> (fields[i][ifield]);
break;
@ -1165,6 +1173,8 @@ int ReadDump::fields_and_keywords(int narg, char **arg)
if (type < 0) break;
if (type == Reader::Q && !atom->q_flag)
error->all(FLERR,"Read dump of charge property that isn't supported by atom style");
if (type == Reader::APIP_LAMBDA && !atom->apip_lambda_flag)
error->all(FLERR,"Read dump of apip_lambda property that isn't supported by atom style");
fieldtype[nfield++] = type;
iarg++;
}
@ -1290,6 +1300,7 @@ int ReadDump::whichtype(char *str)
else if (strcmp(str,"vy") == 0) type = Reader::VY;
else if (strcmp(str,"vz") == 0) type = Reader::VZ;
else if (strcmp(str,"q") == 0) type = Reader::Q;
else if (strcmp(str,"apip_lambda") == 0) type = Reader::APIP_LAMBDA;
else if (strcmp(str,"ix") == 0) type = Reader::IX;
else if (strcmp(str,"iy") == 0) type = Reader::IY;
else if (strcmp(str,"iz") == 0) type = Reader::IZ;

View File

@ -22,7 +22,7 @@ namespace LAMMPS_NS {
class Reader : protected Pointers {
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, IX, IY, IZ, FX, FY, FZ, APIP_LAMBDA };
enum { UNSET, NOSCALE_NOWRAP, NOSCALE_WRAP, SCALE_NOWRAP, SCALE_WRAP };
Reader(class LAMMPS *);

View File

@ -419,6 +419,9 @@ bigint ReaderNative::read_header(double box[3][3], int &boxinfo, int &triclinic,
fieldindex[i] = find_label("iy", labels);
else if (fieldtype[i] == IZ)
fieldindex[i] = find_label("iz", labels);
else if (fieldtype[i] == APIP_LAMBDA)
fieldindex[i] = find_label("apip_lambda", labels);
}
// set fieldflag = -1 if any unfound fields

View File

@ -46,7 +46,7 @@ enum{SETCOMMAND,FIXSET}; // also used in FixSet class
enum{ATOM_SELECT,MOL_SELECT,TYPE_SELECT,GROUP_SELECT,REGION_SELECT};
enum{ANGLE,ANGMOM,BOND,CC,CHARGE,DENSITY,DIAMETER,DIHEDRAL,DIPOLE,
enum{ANGLE,ANGMOM,APIP_LAMBDA,BOND,CC,CHARGE,DENSITY,DIAMETER,DIHEDRAL,DIPOLE,
DIPOLE_RANDOM,DPD_THETA,EDPD_CV,EDPD_TEMP,EPSILON,IMAGE,IMPROPER,LENGTH,
MASS,MOLECULE,OMEGA,QUAT,QUAT_RANDOM,RADIUS_ELECTRON,SHAPE,
SMD_CONTACT_RADIUS,SMD_MASS_DENSITY,SPH_CV,SPH_E,SPH_RHO,
@ -208,6 +208,10 @@ void Set::process_args(int caller_flag, int narg, char **arg)
action->keyword = ANGMOM;
process_angmom(iarg,narg,arg,action);
invoke_choice[naction++] = &Set::invoke_angmom;
} else if (strcmp(arg[iarg],"apip/lambda") == 0) {
action->keyword = APIP_LAMBDA;
process_apip_lambda(iarg,narg,arg,action);
invoke_choice[naction++] = &Set::invoke_apip_lambda;
} else if (strcmp(arg[iarg],"bond") == 0) {
action->keyword = BOND;
process_bond(iarg,narg,arg,action);
@ -1066,6 +1070,43 @@ void Set::invoke_angmom(Action *action)
/* ---------------------------------------------------------------------- */
void Set::process_apip_lambda(int &iarg, int narg, char **arg, Action *action)
{
if (!atom->apip_lambda_flag)
error->all(FLERR,"Cannot set attribute {} for atom style {}", arg[iarg], atom->get_style());
if (iarg+2 > narg) utils::missing_cmd_args(FLERR, "set apip/lambda", error);
if (strcmp(arg[iarg+1],"fast") == 0) action->dvalue1 = 1;
else if (strcmp(arg[iarg+1],"precise") == 0) action->dvalue1 = 0;
else if (utils::strmatch(arg[iarg+1],"^v_")) varparse(arg[iarg+1],1,action);
else action->dvalue1 = utils::numeric(FLERR,arg[iarg+1],false,lmp);
iarg += 2;
}
void Set::invoke_apip_lambda(Action *action)
{
int nlocal = atom->nlocal;
double *apip_lambda = atom->apip_lambda;
if (action->varflag1) {
for(int i = 0; i < nlocal; i++) {
if (!select[i]) continue;
if (vec1[i] < 0 || vec1[i] > 1) error->one(FLERR,"apip/lambda {} not in [0,1]", vec1[i]);
apip_lambda[i] = vec1[i];
}
} else {
double lambda = action->dvalue1;
if (lambda < 0 || lambda > 1) error->all(FLERR,"apip/lambda {} not in [0,1]", lambda);
for(int i = 0; i < nlocal; i++) {
if (!select[i]) continue;
apip_lambda[i] = lambda;
}
}
}
/* ---------------------------------------------------------------------- */
void Set::process_bond(int &iarg, int narg, char **arg, Action *action)
{
if (atom->avec->bonds_allow == 0)

View File

@ -89,6 +89,7 @@ class Set : public Command {
void process_angle(int &, int, char **, Action *);
void process_angmom(int &, int, char **, Action *);
void process_apip_lambda(int &, int, char **, Action *);
void process_bond(int &, int, char **, Action *);
void process_cc(int &, int, char **, Action *);
void process_charge(int &, int, char **, Action *);
@ -141,6 +142,7 @@ class Set : public Command {
void invoke_angle(Action *);
void invoke_angmom(Action *);
void invoke_apip_lambda(Action *);
void invoke_bond(Action *);
void invoke_cc(Action *);
void invoke_charge(Action *);