From 8b031ab609336956d59aaa8ff2b54ffac7a7d009 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 14:21:12 +0000 Subject: [PATCH 01/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10099 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- lib/meam/Makefile.gfortran | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/meam/Makefile.gfortran b/lib/meam/Makefile.gfortran index a1a829ef75..fe7de2200d 100644 --- a/lib/meam/Makefile.gfortran +++ b/lib/meam/Makefile.gfortran @@ -11,7 +11,7 @@ SHELL = /bin/sh # which file will be copied to Makefile.lammps -EXTRAMAKE = Makefile.lammps.installed +EXTRAMAKE = Makefile.lammps.gfortran # ------ FILES ------ From 33e5f0e99ad0484347304c502d928ef4bd87ea44 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 19:43:07 +0000 Subject: [PATCH 02/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10100 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/Section_howto.html | 11 +++++++++++ doc/Section_howto.txt | 11 +++++++++++ doc/thermo_style.html | 8 ++++---- doc/thermo_style.txt | 8 ++++---- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/doc/Section_howto.html b/doc/Section_howto.html index 7d8677f8e2..3b63c4fa43 100644 --- a/doc/Section_howto.html +++ b/doc/Section_howto.html @@ -1359,6 +1359,17 @@ output. In each case, the compute, fix, or variable must generate global values for input to the thermo_style custom command.

+

Note that thermodynamic output values can be "extensive" or +"intensive". The former scale with the number of atoms in the system +(e.g. total energy), the latter do not (e.g. temperature). The +setting for thermo_modify norm determines whether +extensive quantities are normalized or not. Computes and fixes +produce either extensive or intensive values; see their individual doc +pages for details. Equal-style variables produce only +intensive values; you can include a division by "natoms" in the +formula if desired, to make an extensive calculation produce an +intensive result. +

Dump file output

Dump file output is specified by the dump and diff --git a/doc/Section_howto.txt b/doc/Section_howto.txt index f8254f90b9..49ad45fbda 100644 --- a/doc/Section_howto.txt +++ b/doc/Section_howto.txt @@ -1348,6 +1348,17 @@ output. In each case, the compute, fix, or variable must generate global values for input to the "thermo_style custom"_dump.html command. +Note that thermodynamic output values can be "extensive" or +"intensive". The former scale with the number of atoms in the system +(e.g. total energy), the latter do not (e.g. temperature). The +setting for "thermo_modify norm"_thermo_modify.html determines whether +extensive quantities are normalized or not. Computes and fixes +produce either extensive or intensive values; see their individual doc +pages for details. "Equal-style variables"_variable.html produce only +intensive values; you can include a division by "natoms" in the +formula if desired, to make an extensive calculation produce an +intensive result. + Dump file output :h5,link(dump) Dump file output is specified by the "dump"_dump.html and diff --git a/doc/thermo_style.html b/doc/thermo_style.html index 78bcc3038e..fe94a4d8d5 100644 --- a/doc/thermo_style.html +++ b/doc/thermo_style.html @@ -308,10 +308,10 @@ equal-style variables can be referenced. See the or they can invoke other computes, fixes, or variables when evaluated, so this is a very general means of creating thermodynamic output.

-

See Section_modify for information on how to add -new compute and fix styles to LAMMPS to calculate quantities that can -then be referenced with these keywords to generate thermodynamic -output. +

Note that equal-style variables are assumed to be "intensive" global +quantities, which are thus printed as-is, without normalization by +thermo_style custom. You can include a division by "natoms" in the +variable formula if this is not the case.


diff --git a/doc/thermo_style.txt b/doc/thermo_style.txt index 29acefe6ab..a4f06ed840 100644 --- a/doc/thermo_style.txt +++ b/doc/thermo_style.txt @@ -302,10 +302,10 @@ equal-style variables can be referenced. See the or they can invoke other computes, fixes, or variables when evaluated, so this is a very general means of creating thermodynamic output. -See "Section_modify"_Section_modify.html for information on how to add -new compute and fix styles to LAMMPS to calculate quantities that can -then be referenced with these keywords to generate thermodynamic -output. +Note that equal-style variables are assumed to be "intensive" global +quantities, which are thus printed as-is, without normalization by +thermo_style custom. You can include a division by "natoms" in the +variable formula if this is not the case. :line From e0211874ac3cdbc545fa0477085aee113b8a972b Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 19:49:41 +0000 Subject: [PATCH 03/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10101 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/USER-MISC/pair_list.cpp | 3 +++ src/USER-MISC/pair_list.h | 38 +++++++++++++++++++++++++++++++++++++ src/atom.h | 6 ++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/USER-MISC/pair_list.cpp b/src/USER-MISC/pair_list.cpp index d1e2e93ce1..d23215a9b9 100644 --- a/src/USER-MISC/pair_list.cpp +++ b/src/USER-MISC/pair_list.cpp @@ -375,6 +375,9 @@ void PairList::init_style() if (atom->tag_enable == 0) error->all(FLERR,"Pair style list requires atom IDs"); + if (atom->map_style == 0) + error->all(FLERR,"Pair style list requires an atom map"); + if (offset_flag) { for (int n=0; n < npairs; ++n) { list_parm_t &par = params[n]; diff --git a/src/USER-MISC/pair_list.h b/src/USER-MISC/pair_list.h index 8869a52f52..9d199f5ba7 100644 --- a/src/USER-MISC/pair_list.h +++ b/src/USER-MISC/pair_list.h @@ -74,5 +74,43 @@ class PairList : public Pair { /* ERROR/WARNING messages: +E: Not all pairs processed in pair_style list + +Not all interacting pairs for which coefficients were found. This can be intentional +and then you need to set the 'nocheck' option. If not, it usually means that the +communication cutoff is too small. This can be ameliorated by either increasing +the cutoff in the pair_style command or the communication cutoff. + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Cannot open pair list file + +Self-explanatory. The file with the list of pairs cannot be open for reading. +Check the path and permissions. + +E: Incorrectly formatted ... + +Self-explanatory. The content of the pair list file does not match the documented +format. Please re-read the documentation and carefully compare it to your file. + +E: Unknown pair list potential style + +Self-explanatory. You requested a potential type that is not yet implemented or have a typo. + +E: Incorrect args for pair coefficients + +Self-explanatory. Check the input script or data file. + +E: Pair style list requires atom IDs + +Self-explanatory. The pairs in the list are identified via atom IDs, so they need to be present. + +E: Pair style list requires an atom map + +Self-explanatory. Atoms are looked up via an atom map. Create one using the atom_style map command. */ diff --git a/src/atom.h b/src/atom.h index 375417942f..4d617585ae 100644 --- a/src/atom.h +++ b/src/atom.h @@ -1,4 +1,4 @@ -/* ---------------------------------------------------------------------- +/* -*- c++ -*- ---------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov @@ -185,10 +185,12 @@ class Atom : protected Pointers { // functions for global to local ID mapping // map lookup function inlined for efficiency + // return -1 if no map defined inline int map(int global) { if (map_style == 1) return map_array[global]; - else return map_find_hash(global); + else if (map_style == 2) return map_find_hash(global); + else return -1; }; void map_init(); From 0d6f0c69fd62079c1a653b5472e6e63d2619b59b Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 19:57:07 +0000 Subject: [PATCH 04/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10102 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/pair_sph_taitwater_morris.html | 2 +- doc/pair_sph_taitwater_morris.txt | 2 +- doc/pair_tersoff_zbl.html | 2 +- doc/pair_tersoff_zbl.txt | 2 +- doc/tad.html | 2 +- doc/tad.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/pair_sph_taitwater_morris.html b/doc/pair_sph_taitwater_morris.html index 4abffb4c53..4a2568ff79 100644 --- a/doc/pair_sph_taitwater_morris.html +++ b/doc/pair_sph_taitwater_morris.html @@ -78,6 +78,6 @@ LAMMPS section for more info. -

(Morris) Morris, Fox, Zhu, J Comp Physics, 136, 214–226 (1997). +

(Morris) Morris, Fox, Zhu, J Comp Physics, 136, 214-226 (1997).

diff --git a/doc/pair_sph_taitwater_morris.txt b/doc/pair_sph_taitwater_morris.txt index 8bedfd4991..e6c5a6bb20 100644 --- a/doc/pair_sph_taitwater_morris.txt +++ b/doc/pair_sph_taitwater_morris.txt @@ -74,6 +74,6 @@ LAMMPS"_Section_start.html#start_3 section for more info. :line :link(Morris) -[(Morris)] Morris, Fox, Zhu, J Comp Physics, 136, 214–226 (1997). +[(Morris)] Morris, Fox, Zhu, J Comp Physics, 136, 214-226 (1997). diff --git a/doc/pair_tersoff_zbl.html b/doc/pair_tersoff_zbl.html index 9aa63e8be0..50d4b0993d 100644 --- a/doc/pair_tersoff_zbl.html +++ b/doc/pair_tersoff_zbl.html @@ -157,7 +157,7 @@ using the Tersoff_2 mixing rules:

-

Values not shown are determined by the first atom type. Finally, the +

Values not shown are determined by the first atom type. Finally, the Tersoff_2 parameters R and S must be converted to the LAMMPS parameters R and D (R is different in both forms), using the following relations: R=(R'+S')/2 and D=(S'-R')/2, where the primes indicate the diff --git a/doc/pair_tersoff_zbl.txt b/doc/pair_tersoff_zbl.txt index 40d17db4c1..d39e713678 100644 --- a/doc/pair_tersoff_zbl.txt +++ b/doc/pair_tersoff_zbl.txt @@ -153,7 +153,7 @@ using the Tersoff_2 mixing rules: :c,image(Eqs/pair_tersoff_2.jpg) -Values not shown are determined by the first atom type. Finally, the +Values not shown are determined by the first atom type. Finally, the Tersoff_2 parameters R and S must be converted to the LAMMPS parameters R and D (R is different in both forms), using the following relations: R=(R'+S')/2 and D=(S'-R')/2, where the primes indicate the diff --git a/doc/tad.html b/doc/tad.html index 459bdb1c1d..97ce1745e4 100644 --- a/doc/tad.html +++ b/doc/tad.html @@ -308,7 +308,7 @@ dt/reset and fix deposit. -

(Voter) Sørensen and Voter, J Chem Phys, 112, 9599 (2000) +

(Voter) Sorensen and Voter, J Chem Phys, 112, 9599 (2000)

diff --git a/doc/tad.txt b/doc/tad.txt index e62e92a664..acc3355bf2 100644 --- a/doc/tad.txt +++ b/doc/tad.txt @@ -298,7 +298,7 @@ The option defaults are {min} = 0.1 0.1 40 50, {neb} = 0.01 100 100 :line :link(Voter) -[(Voter)] Sørensen and Voter, J Chem Phys, 112, 9599 (2000) +[(Voter)] Sorensen and Voter, J Chem Phys, 112, 9599 (2000) :link(Voter2) [(Voter2)] Voter, Montalenti, Germann, Annual Review of Materials From 2dbe3f80a2e886e4c28399bb726583a7dcf71533 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 20:02:55 +0000 Subject: [PATCH 05/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10103 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/ASPHERE/pair_gayberne.cpp | 26 ++++----- src/ASPHERE/pair_line_lj.cpp | 8 +-- src/ASPHERE/pair_resquared.cpp | 20 +++---- src/ASPHERE/pair_tri_lj.cpp | 8 +-- src/BODY/compute_body_local.cpp | 2 +- src/BODY/pair_body.cpp | 8 +-- src/CLASS2/angle_class2.cpp | 22 +++---- src/CLASS2/bond_class2.cpp | 8 +-- src/CLASS2/dihedral_class2.cpp | 64 ++++++++++----------- src/CLASS2/improper_class2.cpp | 16 +++--- src/CLASS2/pair_lj_class2.cpp | 8 +-- src/CLASS2/pair_lj_class2_coul_cut.cpp | 12 ++-- src/CLASS2/pair_lj_class2_coul_long.cpp | 10 ++-- src/COLLOID/pair_colloid.cpp | 12 ++-- src/DIPOLE/pair_lj_cut_dipole_cut.cpp | 12 ++-- src/DIPOLE/pair_lj_long_dipole_long.cpp | 10 ++-- src/GPU/fix_gpu.cpp | 2 +- src/GRANULAR/pair_gran_hertz_history.cpp | 12 ++-- src/GRANULAR/pair_gran_hooke_history.cpp | 12 ++-- src/KSPACE/pair_born_coul_long.cpp | 16 +++--- src/KSPACE/pair_buck_coul_long.cpp | 12 ++-- src/KSPACE/pair_buck_long_coul_long.cpp | 12 ++-- src/KSPACE/pair_coul_long.cpp | 2 +- src/KSPACE/pair_lj_charmm_coul_long.cpp | 14 ++--- src/KSPACE/pair_lj_cut_coul_long.cpp | 10 ++-- src/KSPACE/pair_lj_cut_tip4p_long.cpp | 14 ++--- src/KSPACE/pair_lj_long_coul_long.cpp | 10 ++-- src/KSPACE/pair_lj_long_tip4p_long.cpp | 14 ++--- src/MANYBODY/fix_qeq_comb.cpp | 4 +- src/MANYBODY/pair_airebo.cpp | 6 +- src/MC/pair_dsmc.cpp | 16 +++--- src/MOLECULE/angle_charmm.cpp | 8 +-- src/MOLECULE/angle_cosine.cpp | 2 +- src/MOLECULE/angle_cosine_squared.cpp | 4 +- src/MOLECULE/angle_harmonic.cpp | 4 +- src/MOLECULE/angle_table.cpp | 2 +- src/MOLECULE/bond_fene.cpp | 8 +-- src/MOLECULE/bond_fene_expand.cpp | 10 ++-- src/MOLECULE/bond_harmonic.cpp | 4 +- src/MOLECULE/bond_morse.cpp | 6 +- src/MOLECULE/bond_nonlinear.cpp | 6 +- src/MOLECULE/bond_quartic.cpp | 10 ++-- src/MOLECULE/bond_table.cpp | 2 +- src/MOLECULE/dihedral_charmm.cpp | 8 +-- src/MOLECULE/dihedral_harmonic.cpp | 6 +- src/MOLECULE/dihedral_helix.cpp | 6 +- src/MOLECULE/dihedral_multi_harmonic.cpp | 10 ++-- src/MOLECULE/dihedral_opls.cpp | 8 +-- src/MOLECULE/improper_cvff.cpp | 6 +- src/MOLECULE/improper_harmonic.cpp | 4 +- src/MOLECULE/pair_hbond_dreiding_lj.cpp | 20 +++---- src/MOLECULE/pair_hbond_dreiding_morse.cpp | 14 ++--- src/MOLECULE/pair_lj_charmm_coul_charmm.cpp | 16 +++--- src/MOLECULE/pair_lj_cut_tip4p_cut.cpp | 20 +++---- src/PERI/pair_peri_pmb.cpp | 8 +-- src/REAX/pair_reax.cpp | 10 ++-- src/USER-AWPMD/pair_awpmd_cut.cpp | 10 ++-- src/USER-CG-CMM/angle_sdk.cpp | 8 +-- src/USER-CG-CMM/pair_cmm_common.cpp | 14 ++--- src/USER-CG-CMM/pair_lj_sdk.cpp | 8 +-- src/USER-CG-CMM/pair_lj_sdk_coul_long.cpp | 10 ++-- src/USER-EFF/pair_eff_cut.cpp | 22 +++---- src/USER-MISC/angle_cosine_shift.cpp | 4 +- src/USER-MISC/angle_cosine_shift_exp.cpp | 6 +- src/USER-MISC/angle_dipole.cpp | 4 +- src/USER-MISC/angle_fourier.cpp | 8 +-- src/USER-MISC/angle_fourier_simple.cpp | 6 +- src/USER-MISC/angle_quartic.cpp | 8 +-- src/USER-MISC/bond_harmonic_shift.cpp | 6 +- src/USER-MISC/bond_harmonic_shift_cut.cpp | 6 +- src/USER-MISC/dihedral_cosine_shift_exp.cpp | 6 +- src/USER-MISC/dihedral_fourier.cpp | 8 +-- src/USER-MISC/dihedral_nharmonic.cpp | 4 +- src/USER-MISC/dihedral_quadratic.cpp | 4 +- src/USER-MISC/dihedral_table.cpp | 2 +- src/USER-MISC/improper_cossq.cpp | 4 +- src/USER-MISC/improper_fourier.cpp | 10 ++-- src/USER-MISC/improper_ring.cpp | 4 +- src/USER-MISC/pair_coul_diel.cpp | 10 ++-- src/USER-MISC/pair_gauss_cut.cpp | 10 ++-- src/USER-MISC/pair_list.cpp | 18 +++--- src/USER-MISC/pair_lj_sf.cpp | 8 +-- src/USER-MISC/pair_lj_sf_dipole_sf.cpp | 12 ++-- src/USER-SPH/pair_sph_heatconduction.cpp | 4 +- src/USER-SPH/pair_sph_idealgas.cpp | 4 +- src/USER-SPH/pair_sph_lj.cpp | 4 +- src/USER-SPH/pair_sph_rhosum.cpp | 4 +- src/USER-SPH/pair_sph_taitwater.cpp | 8 +-- src/USER-SPH/pair_sph_taitwater_morris.cpp | 8 +-- src/atom.cpp | 4 +- src/balance.cpp | 6 +- src/fix_langevin.cpp | 2 +- src/fix_restrain.cpp | 18 +++--- src/force.cpp | 13 +++-- src/force.h | 4 +- src/lattice.cpp | 46 +++++++-------- src/pair_born.cpp | 12 ++-- src/pair_born_coul_wolf.cpp | 18 +++--- src/pair_buck.cpp | 10 ++-- src/pair_buck_coul_cut.cpp | 14 ++--- src/pair_coul_cut.cpp | 4 +- src/pair_coul_debye.cpp | 4 +- src/pair_coul_dsf.cpp | 4 +- src/pair_coul_wolf.cpp | 4 +- src/pair_dpd.cpp | 12 ++-- src/pair_dpd_tstat.cpp | 12 ++-- src/pair_lj96_cut.cpp | 2 +- src/pair_lj_cubic.cpp | 4 +- src/pair_lj_cut.cpp | 8 +-- src/pair_lj_cut_coul_cut.cpp | 12 ++-- src/pair_lj_cut_coul_debye.cpp | 6 +- src/pair_lj_cut_coul_dsf.cpp | 12 ++-- src/pair_lj_expand.cpp | 10 ++-- src/pair_lj_gromacs.cpp | 12 ++-- src/pair_lj_gromacs_coul_gromacs.cpp | 12 ++-- src/pair_lj_smooth.cpp | 12 ++-- src/pair_mie_cut.cpp | 12 ++-- src/pair_morse.cpp | 10 ++-- src/pair_soft.cpp | 6 +- src/pair_table.cpp | 4 +- src/pair_yukawa.cpp | 8 +-- 121 files changed, 588 insertions(+), 585 deletions(-) diff --git a/src/ASPHERE/pair_gayberne.cpp b/src/ASPHERE/pair_gayberne.cpp index 4aa0436109..4bc4cbc08c 100755 --- a/src/ASPHERE/pair_gayberne.cpp +++ b/src/ASPHERE/pair_gayberne.cpp @@ -256,10 +256,10 @@ void PairGayBerne::settings(int narg, char **arg) { if (narg != 4) error->all(FLERR,"Illegal pair_style command"); - gamma = force->numeric(arg[0]); - upsilon = force->numeric(arg[1])/2.0; - mu = force->numeric(arg[2]); - cut_global = force->numeric(arg[3]); + gamma = force->numeric(FLERR,arg[0]); + upsilon = force->numeric(FLERR,arg[1])/2.0; + mu = force->numeric(FLERR,arg[2]); + cut_global = force->numeric(FLERR,arg[3]); // reset cutoffs that have been explicitly set @@ -285,17 +285,17 @@ void PairGayBerne::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); - double eia_one = force->numeric(arg[4]); - double eib_one = force->numeric(arg[5]); - double eic_one = force->numeric(arg[6]); - double eja_one = force->numeric(arg[7]); - double ejb_one = force->numeric(arg[8]); - double ejc_one = force->numeric(arg[9]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); + double eia_one = force->numeric(FLERR,arg[4]); + double eib_one = force->numeric(FLERR,arg[5]); + double eic_one = force->numeric(FLERR,arg[6]); + double eja_one = force->numeric(FLERR,arg[7]); + double ejb_one = force->numeric(FLERR,arg[8]); + double ejc_one = force->numeric(FLERR,arg[9]); double cut_one = cut_global; - if (narg == 11) cut_one = force->numeric(arg[10]); + if (narg == 11) cut_one = force->numeric(FLERR,arg[10]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/ASPHERE/pair_line_lj.cpp b/src/ASPHERE/pair_line_lj.cpp index 37f68d8c7f..27d63e5ddb 100644 --- a/src/ASPHERE/pair_line_lj.cpp +++ b/src/ASPHERE/pair_line_lj.cpp @@ -335,7 +335,7 @@ void PairLineLJ::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -361,11 +361,11 @@ void PairLineLJ::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/ASPHERE/pair_resquared.cpp b/src/ASPHERE/pair_resquared.cpp index 22881d35e5..2eb0a52820 100755 --- a/src/ASPHERE/pair_resquared.cpp +++ b/src/ASPHERE/pair_resquared.cpp @@ -246,7 +246,7 @@ void PairRESquared::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -272,17 +272,17 @@ void PairRESquared::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); - double eia_one = force->numeric(arg[4]); - double eib_one = force->numeric(arg[5]); - double eic_one = force->numeric(arg[6]); - double eja_one = force->numeric(arg[7]); - double ejb_one = force->numeric(arg[8]); - double ejc_one = force->numeric(arg[9]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); + double eia_one = force->numeric(FLERR,arg[4]); + double eib_one = force->numeric(FLERR,arg[5]); + double eic_one = force->numeric(FLERR,arg[6]); + double eja_one = force->numeric(FLERR,arg[7]); + double ejb_one = force->numeric(FLERR,arg[8]); + double ejc_one = force->numeric(FLERR,arg[9]); double cut_one = cut_global; - if (narg == 11) cut_one = force->numeric(arg[10]); + if (narg == 11) cut_one = force->numeric(FLERR,arg[10]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/ASPHERE/pair_tri_lj.cpp b/src/ASPHERE/pair_tri_lj.cpp index 55ef3c9c8b..9d7c3fef6f 100644 --- a/src/ASPHERE/pair_tri_lj.cpp +++ b/src/ASPHERE/pair_tri_lj.cpp @@ -419,7 +419,7 @@ void PairTriLJ::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -445,11 +445,11 @@ void PairTriLJ::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/BODY/compute_body_local.cpp b/src/BODY/compute_body_local.cpp index fef644a5ec..732b801d0f 100644 --- a/src/BODY/compute_body_local.cpp +++ b/src/BODY/compute_body_local.cpp @@ -50,7 +50,7 @@ ComputeBodyLocal::ComputeBodyLocal(LAMMPS *lmp, int narg, char **arg) : if (strcmp(arg[iarg],"type") == 0) which[nvalues++] = TYPE; else { which[nvalues] = INDEX; - index[nvalues] = force->inumeric(arg[iarg]) - 1; + index[nvalues] = force->inumeric(FLERR,arg[iarg]) - 1; nvalues++; } } diff --git a/src/BODY/pair_body.cpp b/src/BODY/pair_body.cpp index 5f9b4faeaa..494bc49243 100644 --- a/src/BODY/pair_body.cpp +++ b/src/BODY/pair_body.cpp @@ -365,7 +365,7 @@ void PairBody::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -391,11 +391,11 @@ void PairBody::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/CLASS2/angle_class2.cpp b/src/CLASS2/angle_class2.cpp index 7aabc352b6..2eabc20b38 100644 --- a/src/CLASS2/angle_class2.cpp +++ b/src/CLASS2/angle_class2.cpp @@ -278,9 +278,9 @@ void AngleClass2::coeff(int narg, char **arg) if (strcmp(arg[1],"bb") == 0) { if (narg != 5) error->all(FLERR,"Incorrect args for angle coefficients"); - double bb_k_one = force->numeric(arg[2]); - double bb_r1_one = force->numeric(arg[3]); - double bb_r2_one = force->numeric(arg[4]); + double bb_k_one = force->numeric(FLERR,arg[2]); + double bb_r1_one = force->numeric(FLERR,arg[3]); + double bb_r2_one = force->numeric(FLERR,arg[4]); for (int i = ilo; i <= ihi; i++) { bb_k[i] = bb_k_one; @@ -293,10 +293,10 @@ void AngleClass2::coeff(int narg, char **arg) } else if (strcmp(arg[1],"ba") == 0) { if (narg != 6) error->all(FLERR,"Incorrect args for angle coefficients"); - double ba_k1_one = force->numeric(arg[2]); - double ba_k2_one = force->numeric(arg[3]); - double ba_r1_one = force->numeric(arg[4]); - double ba_r2_one = force->numeric(arg[5]); + double ba_k1_one = force->numeric(FLERR,arg[2]); + double ba_k2_one = force->numeric(FLERR,arg[3]); + double ba_r1_one = force->numeric(FLERR,arg[4]); + double ba_r2_one = force->numeric(FLERR,arg[5]); for (int i = ilo; i <= ihi; i++) { ba_k1[i] = ba_k1_one; @@ -310,10 +310,10 @@ void AngleClass2::coeff(int narg, char **arg) } else { if (narg != 5) error->all(FLERR,"Incorrect args for angle coefficients"); - double theta0_one = force->numeric(arg[1]); - double k2_one = force->numeric(arg[2]); - double k3_one = force->numeric(arg[3]); - double k4_one = force->numeric(arg[4]); + double theta0_one = force->numeric(FLERR,arg[1]); + double k2_one = force->numeric(FLERR,arg[2]); + double k3_one = force->numeric(FLERR,arg[3]); + double k4_one = force->numeric(FLERR,arg[4]); // convert theta0 from degrees to radians diff --git a/src/CLASS2/bond_class2.cpp b/src/CLASS2/bond_class2.cpp index c0f75dfdf6..19b4916210 100644 --- a/src/CLASS2/bond_class2.cpp +++ b/src/CLASS2/bond_class2.cpp @@ -134,10 +134,10 @@ void BondClass2::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double r0_one = force->numeric(arg[1]); - double k2_one = force->numeric(arg[2]); - double k3_one = force->numeric(arg[3]); - double k4_one = force->numeric(arg[4]); + double r0_one = force->numeric(FLERR,arg[1]); + double k2_one = force->numeric(FLERR,arg[2]); + double k3_one = force->numeric(FLERR,arg[3]); + double k4_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/CLASS2/dihedral_class2.cpp b/src/CLASS2/dihedral_class2.cpp index 69200072de..dd273aa777 100644 --- a/src/CLASS2/dihedral_class2.cpp +++ b/src/CLASS2/dihedral_class2.cpp @@ -644,10 +644,10 @@ void DihedralClass2::coeff(int narg, char **arg) if (strcmp(arg[1],"mbt") == 0) { if (narg != 6) error->all(FLERR,"Incorrect args for dihedral coefficients"); - double f1_one = force->numeric(arg[2]); - double f2_one = force->numeric(arg[3]); - double f3_one = force->numeric(arg[4]); - double r0_one = force->numeric(arg[5]); + double f1_one = force->numeric(FLERR,arg[2]); + double f2_one = force->numeric(FLERR,arg[3]); + double f3_one = force->numeric(FLERR,arg[4]); + double r0_one = force->numeric(FLERR,arg[5]); for (int i = ilo; i <= ihi; i++) { mbt_f1[i] = f1_one; @@ -661,14 +661,14 @@ void DihedralClass2::coeff(int narg, char **arg) } else if (strcmp(arg[1],"ebt") == 0) { if (narg != 10) error->all(FLERR,"Incorrect args for dihedral coefficients"); - double f1_1_one = force->numeric(arg[2]); - double f2_1_one = force->numeric(arg[3]); - double f3_1_one = force->numeric(arg[4]); - double f1_2_one = force->numeric(arg[5]); - double f2_2_one = force->numeric(arg[6]); - double f3_2_one = force->numeric(arg[7]); - double r0_1_one = force->numeric(arg[8]); - double r0_2_one = force->numeric(arg[9]); + double f1_1_one = force->numeric(FLERR,arg[2]); + double f2_1_one = force->numeric(FLERR,arg[3]); + double f3_1_one = force->numeric(FLERR,arg[4]); + double f1_2_one = force->numeric(FLERR,arg[5]); + double f2_2_one = force->numeric(FLERR,arg[6]); + double f3_2_one = force->numeric(FLERR,arg[7]); + double r0_1_one = force->numeric(FLERR,arg[8]); + double r0_2_one = force->numeric(FLERR,arg[9]); for (int i = ilo; i <= ihi; i++) { ebt_f1_1[i] = f1_1_one; @@ -686,14 +686,14 @@ void DihedralClass2::coeff(int narg, char **arg) } else if (strcmp(arg[1],"at") == 0) { if (narg != 10) error->all(FLERR,"Incorrect args for dihedral coefficients"); - double f1_1_one = force->numeric(arg[2]); - double f2_1_one = force->numeric(arg[3]); - double f3_1_one = force->numeric(arg[4]); - double f1_2_one = force->numeric(arg[5]); - double f2_2_one = force->numeric(arg[6]); - double f3_2_one = force->numeric(arg[7]); - double theta0_1_one = force->numeric(arg[8]); - double theta0_2_one = force->numeric(arg[9]); + double f1_1_one = force->numeric(FLERR,arg[2]); + double f2_1_one = force->numeric(FLERR,arg[3]); + double f3_1_one = force->numeric(FLERR,arg[4]); + double f1_2_one = force->numeric(FLERR,arg[5]); + double f2_2_one = force->numeric(FLERR,arg[6]); + double f3_2_one = force->numeric(FLERR,arg[7]); + double theta0_1_one = force->numeric(FLERR,arg[8]); + double theta0_2_one = force->numeric(FLERR,arg[9]); // convert theta0's from degrees to radians @@ -713,9 +713,9 @@ void DihedralClass2::coeff(int narg, char **arg) } else if (strcmp(arg[1],"aat") == 0) { if (narg != 5) error->all(FLERR,"Incorrect args for dihedral coefficients"); - double k_one = force->numeric(arg[2]); - double theta0_1_one = force->numeric(arg[3]); - double theta0_2_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[2]); + double theta0_1_one = force->numeric(FLERR,arg[3]); + double theta0_2_one = force->numeric(FLERR,arg[4]); // convert theta0's from degrees to radians @@ -730,9 +730,9 @@ void DihedralClass2::coeff(int narg, char **arg) } else if (strcmp(arg[1],"bb13") == 0) { if (narg != 5) error->all(FLERR,"Incorrect args for dihedral coefficients"); - double k_one = force->numeric(arg[2]); - double r10_one = force->numeric(arg[3]); - double r30_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[2]); + double r10_one = force->numeric(FLERR,arg[3]); + double r30_one = force->numeric(FLERR,arg[4]); for (int i = ilo; i <= ihi; i++) { bb13t_k[i] = k_one; @@ -745,12 +745,12 @@ void DihedralClass2::coeff(int narg, char **arg) } else { if (narg != 7) error->all(FLERR,"Incorrect args for dihedral coefficients"); - double k1_one = force->numeric(arg[1]); - double phi1_one = force->numeric(arg[2]); - double k2_one = force->numeric(arg[3]); - double phi2_one = force->numeric(arg[4]); - double k3_one = force->numeric(arg[5]); - double phi3_one = force->numeric(arg[6]); + double k1_one = force->numeric(FLERR,arg[1]); + double phi1_one = force->numeric(FLERR,arg[2]); + double k2_one = force->numeric(FLERR,arg[3]); + double phi2_one = force->numeric(FLERR,arg[4]); + double k3_one = force->numeric(FLERR,arg[5]); + double phi3_one = force->numeric(FLERR,arg[6]); // convert phi's from degrees to radians diff --git a/src/CLASS2/improper_class2.cpp b/src/CLASS2/improper_class2.cpp index 4d06ba9333..6b6d503d44 100644 --- a/src/CLASS2/improper_class2.cpp +++ b/src/CLASS2/improper_class2.cpp @@ -532,12 +532,12 @@ void ImproperClass2::coeff(int narg, char **arg) if (strcmp(arg[1],"aa") == 0) { if (narg != 8) error->all(FLERR,"Incorrect args for improper coefficients"); - double k1_one = force->numeric(arg[2]); - double k2_one = force->numeric(arg[3]); - double k3_one = force->numeric(arg[4]); - double theta0_1_one = force->numeric(arg[5]); - double theta0_2_one = force->numeric(arg[6]); - double theta0_3_one = force->numeric(arg[7]); + double k1_one = force->numeric(FLERR,arg[2]); + double k2_one = force->numeric(FLERR,arg[3]); + double k3_one = force->numeric(FLERR,arg[4]); + double theta0_1_one = force->numeric(FLERR,arg[5]); + double theta0_2_one = force->numeric(FLERR,arg[6]); + double theta0_3_one = force->numeric(FLERR,arg[7]); // convert theta0's from degrees to radians @@ -555,8 +555,8 @@ void ImproperClass2::coeff(int narg, char **arg) } else { if (narg != 3) error->all(FLERR,"Incorrect args for improper coefficients"); - double k0_one = force->numeric(arg[1]); - double chi0_one = force->numeric(arg[2]); + double k0_one = force->numeric(FLERR,arg[1]); + double chi0_one = force->numeric(FLERR,arg[2]); // convert chi0 from degrees to radians diff --git a/src/CLASS2/pair_lj_class2.cpp b/src/CLASS2/pair_lj_class2.cpp index 7006450b28..da7dbb7941 100644 --- a/src/CLASS2/pair_lj_class2.cpp +++ b/src/CLASS2/pair_lj_class2.cpp @@ -162,7 +162,7 @@ void PairLJClass2::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -187,11 +187,11 @@ void PairLJClass2::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/CLASS2/pair_lj_class2_coul_cut.cpp b/src/CLASS2/pair_lj_class2_coul_cut.cpp index 1b193bccae..76b7188758 100644 --- a/src/CLASS2/pair_lj_class2_coul_cut.cpp +++ b/src/CLASS2/pair_lj_class2_coul_cut.cpp @@ -188,9 +188,9 @@ void PairLJClass2CoulCut::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[1]); + else cut_coul_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -218,13 +218,13 @@ void PairLJClass2CoulCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; double cut_coul_one = cut_coul_global; - if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(arg[4]); - if (narg == 6) cut_coul_one = force->numeric(arg[5]); + if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(FLERR,arg[4]); + if (narg == 6) cut_coul_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/CLASS2/pair_lj_class2_coul_long.cpp b/src/CLASS2/pair_lj_class2_coul_long.cpp index ba24fe1864..914a7d2c74 100644 --- a/src/CLASS2/pair_lj_class2_coul_long.cpp +++ b/src/CLASS2/pair_lj_class2_coul_long.cpp @@ -228,9 +228,9 @@ void PairLJClass2CoulLong::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[1]); + else cut_coul = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -256,11 +256,11 @@ void PairLJClass2CoulLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(arg[4]); + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/COLLOID/pair_colloid.cpp b/src/COLLOID/pair_colloid.cpp index 5ec424d581..76bd5a5dfd 100644 --- a/src/COLLOID/pair_colloid.cpp +++ b/src/COLLOID/pair_colloid.cpp @@ -244,7 +244,7 @@ void PairColloid::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -270,13 +270,13 @@ void PairColloid::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a12_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); - double d1_one = force->numeric(arg[4]); - double d2_one = force->numeric(arg[5]); + double a12_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); + double d1_one = force->numeric(FLERR,arg[4]); + double d2_one = force->numeric(FLERR,arg[5]); double cut_one = cut_global; - if (narg == 7) cut_one = force->numeric(arg[6]); + if (narg == 7) cut_one = force->numeric(FLERR,arg[6]); if (d1_one < 0.0 || d2_one < 0.0) error->all(FLERR,"Invalid d1 or d2 value for pair colloid coeff"); diff --git a/src/DIPOLE/pair_lj_cut_dipole_cut.cpp b/src/DIPOLE/pair_lj_cut_dipole_cut.cpp index 3ea3a0d353..b0c247b667 100755 --- a/src/DIPOLE/pair_lj_cut_dipole_cut.cpp +++ b/src/DIPOLE/pair_lj_cut_dipole_cut.cpp @@ -298,9 +298,9 @@ void PairLJCutDipoleCut::settings(int narg, char **arg) if (strcmp(update->unit_style,"electron") == 0) error->all(FLERR,"Cannot (yet) use 'electron' units with dipoles"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[1]); + else cut_coul_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -329,13 +329,13 @@ void PairLJCutDipoleCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; double cut_coul_one = cut_coul_global; - if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(arg[4]); - if (narg == 6) cut_coul_one = force->numeric(arg[5]); + if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(FLERR,arg[4]); + if (narg == 6) cut_coul_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/DIPOLE/pair_lj_long_dipole_long.cpp b/src/DIPOLE/pair_lj_long_dipole_long.cpp index b06cc5e33d..cbac0f2767 100755 --- a/src/DIPOLE/pair_lj_long_dipole_long.cpp +++ b/src/DIPOLE/pair_lj_long_dipole_long.cpp @@ -98,10 +98,10 @@ void PairLJLongDipoleLong::settings(int narg, char **arg) error->all(FLERR,PAIR_MISSING); if (!((ewald_order^ewald_off)&(1<<3))) error->all(FLERR,PAIR_COUL_CUT); - cut_lj_global = force->numeric(*(arg++)); + cut_lj_global = force->numeric(FLERR,*(arg++)); if (narg == 4 && (ewald_order==74)) error->all(FLERR,PAIR_CUTOFF); - if (narg == 4) cut_coul = force->numeric(*(arg++)); + if (narg == 4) cut_coul = force->numeric(FLERR,*(arg++)); else cut_coul = cut_lj_global; if (allocated) { // reset explicit cuts @@ -201,11 +201,11 @@ void PairLJLongDipoleLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(arg[4]); + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/GPU/fix_gpu.cpp b/src/GPU/fix_gpu.cpp index 17f33a0b37..9707531d16 100644 --- a/src/GPU/fix_gpu.cpp +++ b/src/GPU/fix_gpu.cpp @@ -71,7 +71,7 @@ FixGPU::FixGPU(LAMMPS *lmp, int narg, char **arg) : first_gpu = atoi(arg[4]); last_gpu = atoi(arg[5]); - _particle_split = force->numeric(arg[6]); + _particle_split = force->numeric(FLERR,arg[6]); if (_particle_split==0 || _particle_split>1) error->all(FLERR,"Illegal fix GPU command"); diff --git a/src/GRANULAR/pair_gran_hertz_history.cpp b/src/GRANULAR/pair_gran_hertz_history.cpp index 719bbae98c..66f00be97f 100644 --- a/src/GRANULAR/pair_gran_hertz_history.cpp +++ b/src/GRANULAR/pair_gran_hertz_history.cpp @@ -274,16 +274,16 @@ void PairGranHertzHistory::settings(int narg, char **arg) { if (narg != 6) error->all(FLERR,"Illegal pair_style command"); - kn = force->numeric(arg[0]); + kn = force->numeric(FLERR,arg[0]); if (strcmp(arg[1],"NULL") == 0) kt = kn * 2.0/7.0; - else kt = force->numeric(arg[1]); + else kt = force->numeric(FLERR,arg[1]); - gamman = force->numeric(arg[2]); + gamman = force->numeric(FLERR,arg[2]); if (strcmp(arg[3],"NULL") == 0) gammat = 0.5 * gamman; - else gammat = force->numeric(arg[3]); + else gammat = force->numeric(FLERR,arg[3]); - xmu = force->numeric(arg[4]); - dampflag = force->inumeric(arg[5]); + xmu = force->numeric(FLERR,arg[4]); + dampflag = force->inumeric(FLERR,arg[5]); if (dampflag == 0) gammat = 0.0; if (kn < 0.0 || kt < 0.0 || gamman < 0.0 || gammat < 0.0 || diff --git a/src/GRANULAR/pair_gran_hooke_history.cpp b/src/GRANULAR/pair_gran_hooke_history.cpp index b68a2049e4..115d19f602 100644 --- a/src/GRANULAR/pair_gran_hooke_history.cpp +++ b/src/GRANULAR/pair_gran_hooke_history.cpp @@ -333,16 +333,16 @@ void PairGranHookeHistory::settings(int narg, char **arg) { if (narg != 6) error->all(FLERR,"Illegal pair_style command"); - kn = force->numeric(arg[0]); + kn = force->numeric(FLERR,arg[0]); if (strcmp(arg[1],"NULL") == 0) kt = kn * 2.0/7.0; - else kt = force->numeric(arg[1]); + else kt = force->numeric(FLERR,arg[1]); - gamman = force->numeric(arg[2]); + gamman = force->numeric(FLERR,arg[2]); if (strcmp(arg[3],"NULL") == 0) gammat = 0.5 * gamman; - else gammat = force->numeric(arg[3]); + else gammat = force->numeric(FLERR,arg[3]); - xmu = force->numeric(arg[4]); - dampflag = force->inumeric(arg[5]); + xmu = force->numeric(FLERR,arg[4]); + dampflag = force->inumeric(FLERR,arg[5]); if (dampflag == 0) gammat = 0.0; if (kn < 0.0 || kt < 0.0 || gamman < 0.0 || gammat < 0.0 || diff --git a/src/KSPACE/pair_born_coul_long.cpp b/src/KSPACE/pair_born_coul_long.cpp index 01db5ab53d..00df5ff6e9 100644 --- a/src/KSPACE/pair_born_coul_long.cpp +++ b/src/KSPACE/pair_born_coul_long.cpp @@ -240,9 +240,9 @@ void PairBornCoulLong::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[1]); + else cut_coul = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -267,15 +267,15 @@ void PairBornCoulLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); - double rho_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double a_one = force->numeric(FLERR,arg[2]); + double rho_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); if (rho_one <= 0) error->all(FLERR,"Incorrect args for pair coefficients"); - double c_one = force->numeric(arg[5]); - double d_one = force->numeric(arg[6]); + double c_one = force->numeric(FLERR,arg[5]); + double d_one = force->numeric(FLERR,arg[6]); double cut_lj_one = cut_lj_global; - if (narg == 8) cut_lj_one = force->numeric(arg[7]); + if (narg == 8) cut_lj_one = force->numeric(FLERR,arg[7]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/KSPACE/pair_buck_coul_long.cpp b/src/KSPACE/pair_buck_coul_long.cpp index 4ee47cabae..21721a3494 100644 --- a/src/KSPACE/pair_buck_coul_long.cpp +++ b/src/KSPACE/pair_buck_coul_long.cpp @@ -228,9 +228,9 @@ void PairBuckCoulLong::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[1]); + else cut_coul = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -256,13 +256,13 @@ void PairBuckCoulLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); - double rho_one = force->numeric(arg[3]); + double a_one = force->numeric(FLERR,arg[2]); + double rho_one = force->numeric(FLERR,arg[3]); if (rho_one <= 0) error->all(FLERR,"Incorrect args for pair coefficients"); - double c_one = force->numeric(arg[4]); + double c_one = force->numeric(FLERR,arg[4]); double cut_lj_one = cut_lj_global; - if (narg == 6) cut_lj_one = force->numeric(arg[5]); + if (narg == 6) cut_lj_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/KSPACE/pair_buck_long_coul_long.cpp b/src/KSPACE/pair_buck_long_coul_long.cpp index a40970fff5..3e185bf2e3 100644 --- a/src/KSPACE/pair_buck_long_coul_long.cpp +++ b/src/KSPACE/pair_buck_long_coul_long.cpp @@ -94,10 +94,10 @@ void PairBuckLongCoulLong::settings(int narg, char **arg) error->all(FLERR,"LJ6 off not supported in pair_style buck/long/coul/long"); if (!((ewald_order^ewald_off) & (1<<1))) error->all(FLERR,"Coulomb cut not supported in pair_style buck/long/coul/coul"); - cut_buck_global = force->numeric(*(arg++)); + cut_buck_global = force->numeric(FLERR,*(arg++)); if (narg == 4 && ((ewald_order & 0x42) == 0x42)) error->all(FLERR,"Only one cutoff allowed when requesting all long"); - if (narg == 4) cut_coul = force->numeric(*arg); + if (narg == 4) cut_coul = force->numeric(FLERR,*arg); else cut_coul = cut_buck_global; if (allocated) { @@ -199,12 +199,12 @@ void PairBuckLongCoulLong::coeff(int narg, char **arg) force->bounds(*(arg++),atom->ntypes,ilo,ihi); force->bounds(*(arg++),atom->ntypes,jlo,jhi); - double buck_a_one = force->numeric(*(arg++)); - double buck_rho_one = force->numeric(*(arg++)); - double buck_c_one = force->numeric(*(arg++)); + double buck_a_one = force->numeric(FLERR,*(arg++)); + double buck_rho_one = force->numeric(FLERR,*(arg++)); + double buck_c_one = force->numeric(FLERR,*(arg++)); double cut_buck_one = cut_buck_global; - if (narg == 6) cut_buck_one = force->numeric(*(arg++)); + if (narg == 6) cut_buck_one = force->numeric(FLERR,*(arg++)); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/KSPACE/pair_coul_long.cpp b/src/KSPACE/pair_coul_long.cpp index ff554c5323..c38977ef6a 100644 --- a/src/KSPACE/pair_coul_long.cpp +++ b/src/KSPACE/pair_coul_long.cpp @@ -199,7 +199,7 @@ void PairCoulLong::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_coul = force->numeric(arg[0]); + cut_coul = force->numeric(FLERR,arg[0]); } /* ---------------------------------------------------------------------- diff --git a/src/KSPACE/pair_lj_charmm_coul_long.cpp b/src/KSPACE/pair_lj_charmm_coul_long.cpp index 8170483ecf..3bf137fe78 100644 --- a/src/KSPACE/pair_lj_charmm_coul_long.cpp +++ b/src/KSPACE/pair_lj_charmm_coul_long.cpp @@ -654,10 +654,10 @@ void PairLJCharmmCoulLong::settings(int narg, char **arg) { if (narg != 2 && narg != 3) error->all(FLERR,"Illegal pair_style command"); - cut_lj_inner = force->numeric(arg[0]); - cut_lj = force->numeric(arg[1]); + cut_lj_inner = force->numeric(FLERR,arg[0]); + cut_lj = force->numeric(FLERR,arg[1]); if (narg == 2) cut_coul = cut_lj; - else cut_coul = force->numeric(arg[2]); + else cut_coul = force->numeric(FLERR,arg[2]); } /* ---------------------------------------------------------------------- @@ -673,13 +673,13 @@ void PairLJCharmmCoulLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double eps14_one = epsilon_one; double sigma14_one = sigma_one; if (narg == 6) { - eps14_one = force->numeric(arg[4]); - sigma14_one = force->numeric(arg[5]); + eps14_one = force->numeric(FLERR,arg[4]); + sigma14_one = force->numeric(FLERR,arg[5]); } int count = 0; diff --git a/src/KSPACE/pair_lj_cut_coul_long.cpp b/src/KSPACE/pair_lj_cut_coul_long.cpp index 97772aadd4..198f3da7ec 100644 --- a/src/KSPACE/pair_lj_cut_coul_long.cpp +++ b/src/KSPACE/pair_lj_cut_coul_long.cpp @@ -597,9 +597,9 @@ void PairLJCutCoulLong::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[1]); + else cut_coul = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -625,11 +625,11 @@ void PairLJCutCoulLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(arg[4]); + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/KSPACE/pair_lj_cut_tip4p_long.cpp b/src/KSPACE/pair_lj_cut_tip4p_long.cpp index 14164c812c..930e8c0723 100644 --- a/src/KSPACE/pair_lj_cut_tip4p_long.cpp +++ b/src/KSPACE/pair_lj_cut_tip4p_long.cpp @@ -422,15 +422,15 @@ void PairLJCutTIP4PLong::settings(int narg, char **arg) { if (narg < 6 || narg > 7) error->all(FLERR,"Illegal pair_style command"); - typeO = force->inumeric(arg[0]); - typeH = force->inumeric(arg[1]); - typeB = force->inumeric(arg[2]); - typeA = force->inumeric(arg[3]); - qdist = force->numeric(arg[4]); + typeO = force->inumeric(FLERR,arg[0]); + typeH = force->inumeric(FLERR,arg[1]); + typeB = force->inumeric(FLERR,arg[2]); + typeA = force->inumeric(FLERR,arg[3]); + qdist = force->numeric(FLERR,arg[4]); - cut_lj_global = force->numeric(arg[5]); + cut_lj_global = force->numeric(FLERR,arg[5]); if (narg == 6) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[6]); + else cut_coul = force->numeric(FLERR,arg[6]); // reset cutoffs that have been explicitly set diff --git a/src/KSPACE/pair_lj_long_coul_long.cpp b/src/KSPACE/pair_lj_long_coul_long.cpp index fdf4f5ac09..96af73c172 100644 --- a/src/KSPACE/pair_lj_long_coul_long.cpp +++ b/src/KSPACE/pair_lj_long_coul_long.cpp @@ -92,10 +92,10 @@ void PairLJLongCoulLong::settings(int narg, char **arg) error->all(FLERR,"Cutoffs missing in pair_style lj/long/coul/long"); if (!((ewald_order^ewald_off) & (1<<1))) error->all(FLERR,"Coulomb cut not supported in pair_style lj/long/coul/long"); - cut_lj_global = force->numeric(*(arg++)); + cut_lj_global = force->numeric(FLERR,*(arg++)); if (narg == 4 && ((ewald_order & 0x42) == 0x42)) error->all(FLERR,"Only one cutoff allowed when requesting all long"); - if (narg == 4) cut_coul = force->numeric(*arg); + if (narg == 4) cut_coul = force->numeric(FLERR,*arg); else cut_coul = cut_lj_global; if (allocated) { @@ -195,11 +195,11 @@ void PairLJLongCoulLong::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(arg[4]); + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/KSPACE/pair_lj_long_tip4p_long.cpp b/src/KSPACE/pair_lj_long_tip4p_long.cpp index a0d55a0add..f2825c32b5 100755 --- a/src/KSPACE/pair_lj_long_tip4p_long.cpp +++ b/src/KSPACE/pair_lj_long_tip4p_long.cpp @@ -1391,16 +1391,16 @@ void PairLJLongTIP4PLong::settings(int narg, char **arg) if (!((ewald_order^ewald_off)&(1<<1))) error->all(FLERR, "Coulomb cut not supported in pair_style lj/long/tip4p/long"); - typeO = force->inumeric(arg[1]); - typeH = force->inumeric(arg[2]); - typeB = force->inumeric(arg[3]); - typeA = force->inumeric(arg[4]); - qdist = force->numeric(arg[5]); + typeO = force->inumeric(FLERR,arg[1]); + typeH = force->inumeric(FLERR,arg[2]); + typeB = force->inumeric(FLERR,arg[3]); + typeA = force->inumeric(FLERR,arg[4]); + qdist = force->numeric(FLERR,arg[5]); - cut_lj_global = force->numeric(arg[6]); + cut_lj_global = force->numeric(FLERR,arg[6]); if (narg == 8) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[7]); + else cut_coul = force->numeric(FLERR,arg[7]); // reset cutoffs that have been explicitly set diff --git a/src/MANYBODY/fix_qeq_comb.cpp b/src/MANYBODY/fix_qeq_comb.cpp index 9920d3dce8..6f724a18f6 100644 --- a/src/MANYBODY/fix_qeq_comb.cpp +++ b/src/MANYBODY/fix_qeq_comb.cpp @@ -47,8 +47,8 @@ FixQEQComb::FixQEQComb(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) size_peratom_cols = 0; peratom_freq = 1; - nevery = force->inumeric(arg[3]); - precision = force->numeric(arg[4]); + nevery = force->inumeric(FLERR,arg[3]); + precision = force->numeric(FLERR,arg[4]); if (nevery <= 0 || precision <= 0.0) error->all(FLERR,"Illegal fix qeq/comb command"); diff --git a/src/MANYBODY/pair_airebo.cpp b/src/MANYBODY/pair_airebo.cpp index 6e1edb1ab5..28939dbeb3 100644 --- a/src/MANYBODY/pair_airebo.cpp +++ b/src/MANYBODY/pair_airebo.cpp @@ -138,12 +138,12 @@ void PairAIREBO::settings(int narg, char **arg) { if (narg != 1 && narg != 3) error->all(FLERR,"Illegal pair_style command"); - cutlj = force->numeric(arg[0]); + cutlj = force->numeric(FLERR,arg[0]); ljflag = torflag = 1; if (narg == 3) { - ljflag = force->inumeric(arg[1]); - torflag = force->inumeric(arg[2]); + ljflag = force->inumeric(FLERR,arg[1]); + torflag = force->inumeric(FLERR,arg[2]); } } diff --git a/src/MC/pair_dsmc.cpp b/src/MC/pair_dsmc.cpp index 15ff2135aa..444c49f25d 100644 --- a/src/MC/pair_dsmc.cpp +++ b/src/MC/pair_dsmc.cpp @@ -209,12 +209,12 @@ void PairDSMC::settings(int narg, char **arg) if (narg != 6) error->all(FLERR,"Illegal pair_style command"); cut_global = 0.0; - max_cell_size = force->numeric(arg[0]); - seed = force->inumeric(arg[1]); - weighting = force->numeric(arg[2]); - T_ref = force->numeric(arg[3]); - recompute_vsigmamax_stride = force->inumeric(arg[4]); - vsigmamax_samples = force->inumeric(arg[5]); + max_cell_size = force->numeric(FLERR,arg[0]); + seed = force->inumeric(FLERR,arg[1]); + weighting = force->numeric(FLERR,arg[2]); + T_ref = force->numeric(FLERR,arg[3]); + recompute_vsigmamax_stride = force->inumeric(FLERR,arg[4]); + vsigmamax_samples = force->inumeric(FLERR,arg[5]); // initialize Marsaglia RNG with processor-unique seed @@ -248,10 +248,10 @@ void PairDSMC::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double sigma_one = force->numeric(arg[2]); + double sigma_one = force->numeric(FLERR,arg[2]); double cut_one = cut_global; - if (narg == 4) cut_one = force->numeric(arg[3]); + if (narg == 4) cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/angle_charmm.cpp b/src/MOLECULE/angle_charmm.cpp index 9717fcffa2..b6d2978ceb 100644 --- a/src/MOLECULE/angle_charmm.cpp +++ b/src/MOLECULE/angle_charmm.cpp @@ -198,10 +198,10 @@ void AngleCharmm::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double theta0_one = force->numeric(arg[2]); - double k_ub_one = force->numeric(arg[3]); - double r_ub_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[1]); + double theta0_one = force->numeric(FLERR,arg[2]); + double k_ub_one = force->numeric(FLERR,arg[3]); + double r_ub_one = force->numeric(FLERR,arg[4]); // convert theta0 from degrees to radians diff --git a/src/MOLECULE/angle_cosine.cpp b/src/MOLECULE/angle_cosine.cpp index 24eccb2a69..f43c7bc6e9 100644 --- a/src/MOLECULE/angle_cosine.cpp +++ b/src/MOLECULE/angle_cosine.cpp @@ -158,7 +158,7 @@ void AngleCosine::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); + double k_one = force->numeric(FLERR,arg[1]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/angle_cosine_squared.cpp b/src/MOLECULE/angle_cosine_squared.cpp index a198a2c498..578d641de3 100644 --- a/src/MOLECULE/angle_cosine_squared.cpp +++ b/src/MOLECULE/angle_cosine_squared.cpp @@ -170,8 +170,8 @@ void AngleCosineSquared::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double theta0_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double theta0_one = force->numeric(FLERR,arg[2]); // convert theta0 from degrees to radians diff --git a/src/MOLECULE/angle_harmonic.cpp b/src/MOLECULE/angle_harmonic.cpp index c996c4e866..9bb7adbebc 100644 --- a/src/MOLECULE/angle_harmonic.cpp +++ b/src/MOLECULE/angle_harmonic.cpp @@ -170,8 +170,8 @@ void AngleHarmonic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double theta0_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double theta0_one = force->numeric(FLERR,arg[2]); // convert theta0 from degrees to radians diff --git a/src/MOLECULE/angle_table.cpp b/src/MOLECULE/angle_table.cpp index 96e0c37a02..ca8b6c3e73 100644 --- a/src/MOLECULE/angle_table.cpp +++ b/src/MOLECULE/angle_table.cpp @@ -187,7 +187,7 @@ void AngleTable::settings(int narg, char **arg) else if (strcmp(arg[0],"spline") == 0) tabstyle = SPLINE; else error->all(FLERR,"Unknown table style in angle style table"); - tablength = force->inumeric(arg[1]); + tablength = force->inumeric(FLERR,arg[1]); if (tablength < 2) error->all(FLERR,"Illegal number of angle table entries"); // delete old tables, since cannot just change settings diff --git a/src/MOLECULE/bond_fene.cpp b/src/MOLECULE/bond_fene.cpp index 4dc4f59708..8eca809d1b 100644 --- a/src/MOLECULE/bond_fene.cpp +++ b/src/MOLECULE/bond_fene.cpp @@ -155,10 +155,10 @@ void BondFENE::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double r0_one = force->numeric(arg[2]); - double epsilon_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[1]); + double r0_one = force->numeric(FLERR,arg[2]); + double epsilon_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/bond_fene_expand.cpp b/src/MOLECULE/bond_fene_expand.cpp index 4783e79876..214e632795 100644 --- a/src/MOLECULE/bond_fene_expand.cpp +++ b/src/MOLECULE/bond_fene_expand.cpp @@ -161,11 +161,11 @@ void BondFENEExpand::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double r0_one = force->numeric(arg[2]); - double epsilon_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); - double shift_one = force->numeric(arg[5]); + double k_one = force->numeric(FLERR,arg[1]); + double r0_one = force->numeric(FLERR,arg[2]); + double epsilon_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); + double shift_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/bond_harmonic.cpp b/src/MOLECULE/bond_harmonic.cpp index 2eb85fa5d1..aeb50eb928 100644 --- a/src/MOLECULE/bond_harmonic.cpp +++ b/src/MOLECULE/bond_harmonic.cpp @@ -123,8 +123,8 @@ void BondHarmonic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double r0_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double r0_one = force->numeric(FLERR,arg[2]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/bond_morse.cpp b/src/MOLECULE/bond_morse.cpp index 883f4f2f09..03af2dd1c6 100644 --- a/src/MOLECULE/bond_morse.cpp +++ b/src/MOLECULE/bond_morse.cpp @@ -128,9 +128,9 @@ void BondMorse::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double d0_one = force->numeric(arg[1]); - double alpha_one = force->numeric(arg[2]); - double r0_one = force->numeric(arg[3]); + double d0_one = force->numeric(FLERR,arg[1]); + double alpha_one = force->numeric(FLERR,arg[2]); + double r0_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/bond_nonlinear.cpp b/src/MOLECULE/bond_nonlinear.cpp index a3f4520c8d..0ed5f0f4e8 100644 --- a/src/MOLECULE/bond_nonlinear.cpp +++ b/src/MOLECULE/bond_nonlinear.cpp @@ -125,9 +125,9 @@ void BondNonlinear::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double epsilon_one = force->numeric(arg[1]); - double r0_one = force->numeric(arg[2]); - double lamda_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[1]); + double r0_one = force->numeric(FLERR,arg[2]); + double lamda_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/bond_quartic.cpp b/src/MOLECULE/bond_quartic.cpp index e69ef5e6f2..d2f7a47b20 100755 --- a/src/MOLECULE/bond_quartic.cpp +++ b/src/MOLECULE/bond_quartic.cpp @@ -205,11 +205,11 @@ void BondQuartic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double b1_one = force->numeric(arg[2]); - double b2_one = force->numeric(arg[3]); - double rc_one = force->numeric(arg[4]); - double u0_one = force->numeric(arg[5]); + double k_one = force->numeric(FLERR,arg[1]); + double b1_one = force->numeric(FLERR,arg[2]); + double b2_one = force->numeric(FLERR,arg[3]); + double rc_one = force->numeric(FLERR,arg[4]); + double u0_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/bond_table.cpp b/src/MOLECULE/bond_table.cpp index 0a61969106..80b040254b 100644 --- a/src/MOLECULE/bond_table.cpp +++ b/src/MOLECULE/bond_table.cpp @@ -138,7 +138,7 @@ void BondTable::settings(int narg, char **arg) else if (strcmp(arg[0],"spline") == 0) tabstyle = SPLINE; else error->all(FLERR,"Unknown table style in bond style table"); - tablength = force->inumeric(arg[1]); + tablength = force->inumeric(FLERR,arg[1]); if (tablength < 2) error->all(FLERR,"Illegal number of bond table entries"); // delete old tables, since cannot just change settings diff --git a/src/MOLECULE/dihedral_charmm.cpp b/src/MOLECULE/dihedral_charmm.cpp index 6c07195939..97236e3320 100644 --- a/src/MOLECULE/dihedral_charmm.cpp +++ b/src/MOLECULE/dihedral_charmm.cpp @@ -331,10 +331,10 @@ void DihedralCharmm::coeff(int narg, char **arg) // arbitrary phase angle shift could be allowed, but would break // backwards compatibility and is probably not needed - double k_one = force->numeric(arg[1]); - int multiplicity_one = force->inumeric(arg[2]); - int shift_one = force->inumeric(arg[3]); - double weight_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[1]); + int multiplicity_one = force->inumeric(FLERR,arg[2]); + int shift_one = force->inumeric(FLERR,arg[3]); + double weight_one = force->numeric(FLERR,arg[4]); if (multiplicity_one < 0) error->all(FLERR,"Incorrect multiplicity arg for dihedral coefficients"); diff --git a/src/MOLECULE/dihedral_harmonic.cpp b/src/MOLECULE/dihedral_harmonic.cpp index f384bb7459..a7f353269b 100644 --- a/src/MOLECULE/dihedral_harmonic.cpp +++ b/src/MOLECULE/dihedral_harmonic.cpp @@ -277,9 +277,9 @@ void DihedralHarmonic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - int sign_one = force->inumeric(arg[2]); - int multiplicity_one = force->inumeric(arg[3]); + double k_one = force->numeric(FLERR,arg[1]); + int sign_one = force->inumeric(FLERR,arg[2]); + int multiplicity_one = force->inumeric(FLERR,arg[3]); // require sign = +/- 1 for backwards compatibility // arbitrary phase angle shift could be allowed, but would break diff --git a/src/MOLECULE/dihedral_helix.cpp b/src/MOLECULE/dihedral_helix.cpp index 04bc7b029c..fd5d72e845 100644 --- a/src/MOLECULE/dihedral_helix.cpp +++ b/src/MOLECULE/dihedral_helix.cpp @@ -285,9 +285,9 @@ void DihedralHelix::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); - double aphi_one = force->numeric(arg[1]); - double bphi_one = force->numeric(arg[2]); - double cphi_one = force->numeric(arg[3]); + double aphi_one = force->numeric(FLERR,arg[1]); + double bphi_one = force->numeric(FLERR,arg[2]); + double cphi_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/dihedral_multi_harmonic.cpp b/src/MOLECULE/dihedral_multi_harmonic.cpp index d004a4cd94..2e3b4e79dc 100644 --- a/src/MOLECULE/dihedral_multi_harmonic.cpp +++ b/src/MOLECULE/dihedral_multi_harmonic.cpp @@ -273,11 +273,11 @@ void DihedralMultiHarmonic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); - double a1_one = force->numeric(arg[1]); - double a2_one = force->numeric(arg[2]); - double a3_one = force->numeric(arg[3]); - double a4_one = force->numeric(arg[4]); - double a5_one = force->numeric(arg[5]); + double a1_one = force->numeric(FLERR,arg[1]); + double a2_one = force->numeric(FLERR,arg[2]); + double a3_one = force->numeric(FLERR,arg[3]); + double a4_one = force->numeric(FLERR,arg[4]); + double a5_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/dihedral_opls.cpp b/src/MOLECULE/dihedral_opls.cpp index 0e40843fa9..15a4b0a61e 100644 --- a/src/MOLECULE/dihedral_opls.cpp +++ b/src/MOLECULE/dihedral_opls.cpp @@ -286,10 +286,10 @@ void DihedralOPLS::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); - double k1_one = force->numeric(arg[1]); - double k2_one = force->numeric(arg[2]); - double k3_one = force->numeric(arg[3]); - double k4_one = force->numeric(arg[4]); + double k1_one = force->numeric(FLERR,arg[1]); + double k2_one = force->numeric(FLERR,arg[2]); + double k3_one = force->numeric(FLERR,arg[3]); + double k4_one = force->numeric(FLERR,arg[4]); // store 1/2 factor with prefactor diff --git a/src/MOLECULE/improper_cvff.cpp b/src/MOLECULE/improper_cvff.cpp index a0aa12832e..f031ddfdb8 100644 --- a/src/MOLECULE/improper_cvff.cpp +++ b/src/MOLECULE/improper_cvff.cpp @@ -297,9 +297,9 @@ void ImproperCvff::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - int sign_one = force->inumeric(arg[2]); - int multiplicity_one = force->inumeric(arg[3]); + double k_one = force->numeric(FLERR,arg[1]); + int sign_one = force->inumeric(FLERR,arg[2]); + int multiplicity_one = force->inumeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/MOLECULE/improper_harmonic.cpp b/src/MOLECULE/improper_harmonic.cpp index cbf5563c53..4db32456b3 100644 --- a/src/MOLECULE/improper_harmonic.cpp +++ b/src/MOLECULE/improper_harmonic.cpp @@ -242,8 +242,8 @@ void ImproperHarmonic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double chi_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double chi_one = force->numeric(FLERR,arg[2]); // convert chi from degrees to radians diff --git a/src/MOLECULE/pair_hbond_dreiding_lj.cpp b/src/MOLECULE/pair_hbond_dreiding_lj.cpp index 4239bea8a4..575836fe6d 100644 --- a/src/MOLECULE/pair_hbond_dreiding_lj.cpp +++ b/src/MOLECULE/pair_hbond_dreiding_lj.cpp @@ -278,10 +278,10 @@ void PairHbondDreidingLJ::settings(int narg, char **arg) { if (narg != 4) error->all(FLERR,"Illegal pair_style command"); - ap_global = force->inumeric(arg[0]); - cut_inner_global = force->numeric(arg[1]); - cut_outer_global = force->numeric(arg[2]); - cut_angle_global = force->numeric(arg[3]) * MY_PI/180.0; + ap_global = force->inumeric(FLERR,arg[0]); + cut_inner_global = force->numeric(FLERR,arg[1]); + cut_outer_global = force->numeric(FLERR,arg[2]); + cut_angle_global = force->numeric(FLERR,arg[3]) * MY_PI/180.0; } /* ---------------------------------------------------------------------- @@ -304,21 +304,21 @@ void PairHbondDreidingLJ::coeff(int narg, char **arg) else if (strcmp(arg[3],"j") == 0) donor_flag = 1; else error->all(FLERR,"Incorrect args for pair coefficients"); - double epsilon_one = force->numeric(arg[4]); - double sigma_one = force->numeric(arg[5]); + double epsilon_one = force->numeric(FLERR,arg[4]); + double sigma_one = force->numeric(FLERR,arg[5]); int ap_one = ap_global; - if (narg > 6) ap_one = force->inumeric(arg[6]); + if (narg > 6) ap_one = force->inumeric(FLERR,arg[6]); double cut_inner_one = cut_inner_global; double cut_outer_one = cut_outer_global; if (narg > 8) { - cut_inner_one = force->numeric(arg[7]); - cut_outer_one = force->numeric(arg[8]); + cut_inner_one = force->numeric(FLERR,arg[7]); + cut_outer_one = force->numeric(FLERR,arg[8]); } if (cut_inner_one>cut_outer_one) error->all(FLERR,"Pair inner cutoff >= Pair outer cutoff"); double cut_angle_one = cut_angle_global; - if (narg == 10) cut_angle_one = force->numeric(arg[9]) * MY_PI/180.0; + if (narg == 10) cut_angle_one = force->numeric(FLERR,arg[9]) * MY_PI/180.0; // grow params array if necessary if (nparams == maxparam) { diff --git a/src/MOLECULE/pair_hbond_dreiding_morse.cpp b/src/MOLECULE/pair_hbond_dreiding_morse.cpp index d7f0f25993..02d6bbba0d 100644 --- a/src/MOLECULE/pair_hbond_dreiding_morse.cpp +++ b/src/MOLECULE/pair_hbond_dreiding_morse.cpp @@ -230,22 +230,22 @@ void PairHbondDreidingMorse::coeff(int narg, char **arg) else if (strcmp(arg[3],"j") == 0) donor_flag = 1; else error->all(FLERR,"Incorrect args for pair coefficients"); - double d0_one = force->numeric(arg[4]); - double alpha_one = force->numeric(arg[5]); - double r0_one = force->numeric(arg[6]); + double d0_one = force->numeric(FLERR,arg[4]); + double alpha_one = force->numeric(FLERR,arg[5]); + double r0_one = force->numeric(FLERR,arg[6]); int ap_one = ap_global; - if (narg > 7) ap_one = force->inumeric(arg[7]); + if (narg > 7) ap_one = force->inumeric(FLERR,arg[7]); double cut_inner_one = cut_inner_global; double cut_outer_one = cut_outer_global; if (narg > 9) { - cut_inner_one = force->numeric(arg[8]); - cut_outer_one = force->numeric(arg[9]); + cut_inner_one = force->numeric(FLERR,arg[8]); + cut_outer_one = force->numeric(FLERR,arg[9]); } if (cut_inner_one>cut_outer_one) error->all(FLERR,"Pair inner cutoff >= Pair outer cutoff"); double cut_angle_one = cut_angle_global; - if (narg > 10) cut_angle_one = force->numeric(arg[10]) * MY_PI/180.0; + if (narg > 10) cut_angle_one = force->numeric(FLERR,arg[10]) * MY_PI/180.0; // grow params array if necessary diff --git a/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp b/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp index 60d00fb560..72a08707c5 100644 --- a/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp +++ b/src/MOLECULE/pair_lj_charmm_coul_charmm.cpp @@ -224,14 +224,14 @@ void PairLJCharmmCoulCharmm::settings(int narg, char **arg) if (narg != 2 && narg != 4) error->all(FLERR,"Illegal pair_style command"); - cut_lj_inner = force->numeric(arg[0]); - cut_lj = force->numeric(arg[1]); + cut_lj_inner = force->numeric(FLERR,arg[0]); + cut_lj = force->numeric(FLERR,arg[1]); if (narg == 2) { cut_coul_inner = cut_lj_inner; cut_coul = cut_lj; } else { - cut_coul_inner = force->numeric(arg[2]); - cut_coul = force->numeric(arg[3]); + cut_coul_inner = force->numeric(FLERR,arg[2]); + cut_coul = force->numeric(FLERR,arg[3]); } } @@ -249,13 +249,13 @@ void PairLJCharmmCoulCharmm::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double eps14_one = epsilon_one; double sigma14_one = sigma_one; if (narg == 6) { - eps14_one = force->numeric(arg[4]); - sigma14_one = force->numeric(arg[5]); + eps14_one = force->numeric(FLERR,arg[4]); + sigma14_one = force->numeric(FLERR,arg[5]); } int count = 0; diff --git a/src/MOLECULE/pair_lj_cut_tip4p_cut.cpp b/src/MOLECULE/pair_lj_cut_tip4p_cut.cpp index 72f809aec7..595680b0c6 100644 --- a/src/MOLECULE/pair_lj_cut_tip4p_cut.cpp +++ b/src/MOLECULE/pair_lj_cut_tip4p_cut.cpp @@ -412,15 +412,15 @@ void PairLJCutTIP4PCut::settings(int narg, char **arg) { if (narg < 6 || narg > 7) error->all(FLERR,"Illegal pair_style command"); - typeO = force->inumeric(arg[0]); - typeH = force->inumeric(arg[1]); - typeB = force->inumeric(arg[2]); - typeA = force->inumeric(arg[3]); - qdist = force->numeric(arg[4]); + typeO = force->inumeric(FLERR,arg[0]); + typeH = force->inumeric(FLERR,arg[1]); + typeB = force->inumeric(FLERR,arg[2]); + typeA = force->inumeric(FLERR,arg[3]); + qdist = force->numeric(FLERR,arg[4]); - cut_lj_global = force->numeric(arg[5]); + cut_lj_global = force->numeric(FLERR,arg[5]); if (narg == 6) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[6]); + else cut_coul = force->numeric(FLERR,arg[6]); cut_coulsq = cut_coul * cut_coul; cut_coulsqplus = (cut_coul + 2.0*qdist) * (cut_coul + 2.0*qdist); @@ -447,11 +447,11 @@ void PairLJCutTIP4PCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(arg[4]); + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/PERI/pair_peri_pmb.cpp b/src/PERI/pair_peri_pmb.cpp index 501c14a121..439f4b5d09 100644 --- a/src/PERI/pair_peri_pmb.cpp +++ b/src/PERI/pair_peri_pmb.cpp @@ -315,10 +315,10 @@ void PairPeriPMB::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double kspring_one = force->numeric(arg[2]); - double cut_one = force->numeric(arg[3]); - double s00_one = force->numeric(arg[4]); - double alpha_one = force->numeric(arg[5]); + double kspring_one = force->numeric(FLERR,arg[2]); + double cut_one = force->numeric(FLERR,arg[3]); + double s00_one = force->numeric(FLERR,arg[4]); + double alpha_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/REAX/pair_reax.cpp b/src/REAX/pair_reax.cpp index 7ce0295dc1..341aba65d7 100644 --- a/src/REAX/pair_reax.cpp +++ b/src/REAX/pair_reax.cpp @@ -486,10 +486,10 @@ void PairREAX::settings(int narg, char **arg) if (narg != 0 && narg !=4) error->all(FLERR,"Illegal pair_style command"); if (narg == 4) { - hbcut = force->numeric(arg[0]); - ihbnew = static_cast (force->numeric(arg[1])); - itripstaball = static_cast (force->numeric(arg[2])); - precision = force->numeric(arg[3]); + hbcut = force->numeric(FLERR,arg[0]); + ihbnew = static_cast (force->numeric(FLERR,arg[1])); + itripstaball = static_cast (force->numeric(FLERR,arg[2])); + precision = force->numeric(FLERR,arg[3]); if (hbcut <= 0.0 || (ihbnew != 0 && ihbnew != 1) || @@ -531,7 +531,7 @@ void PairREAX::coeff(int narg, char **arg) error->all(FLERR,"Cannot currently use pair reax with pair hybrid"); continue; } - map[i-2] = force->inumeric(arg[i]); + map[i-2] = force->inumeric(FLERR,arg[i]); } int n = atom->ntypes; diff --git a/src/USER-AWPMD/pair_awpmd_cut.cpp b/src/USER-AWPMD/pair_awpmd_cut.cpp index da7825398e..5c21d684d7 100644 --- a/src/USER-AWPMD/pair_awpmd_cut.cpp +++ b/src/USER-AWPMD/pair_awpmd_cut.cpp @@ -410,7 +410,7 @@ void PairAWPMDCut::allocate() void PairAWPMDCut::settings(int narg, char **arg){ if (narg < 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); ermscale=1.; width_pbc=0.; @@ -430,21 +430,21 @@ void PairAWPMDCut::settings(int narg, char **arg){ i++; if(i>=narg) error->all(FLERR,"Setting 'fix' should be followed by a number in awpmd/cut"); - wpmd->w0=force->numeric(arg[i]); + wpmd->w0=force->numeric(FLERR,arg[i]); } else if(!strcmp(arg[i],"harm")){ wpmd->constraint=AWPMD::HARM; i++; if(i>=narg) error->all(FLERR,"Setting 'harm' should be followed by a number in awpmd/cut"); - wpmd->w0=force->numeric(arg[i]); + wpmd->w0=force->numeric(FLERR,arg[i]); wpmd->set_harm_constr(wpmd->w0); } else if(!strcmp(arg[i],"pbc")){ i++; if(i>=narg) error->all(FLERR,"Setting 'pbc' should be followed by a number in awpmd/cut"); - width_pbc=force->numeric(arg[i]); + width_pbc=force->numeric(FLERR,arg[i]); } else if(!strcmp(arg[i],"relax")) wpmd->constraint=AWPMD::RELAX; @@ -452,7 +452,7 @@ void PairAWPMDCut::settings(int narg, char **arg){ i++; if(i>=narg) error->all(FLERR,"Setting 'ermscale' should be followed by a number in awpmd/cut"); - ermscale=force->numeric(arg[i]); + ermscale=force->numeric(FLERR,arg[i]); } else if(!strcmp(arg[i],"flex_press")) flexible_pressure_flag = 1; diff --git a/src/USER-CG-CMM/angle_sdk.cpp b/src/USER-CG-CMM/angle_sdk.cpp index 0c3d45bbb2..0ee3f47749 100644 --- a/src/USER-CG-CMM/angle_sdk.cpp +++ b/src/USER-CG-CMM/angle_sdk.cpp @@ -242,8 +242,8 @@ void AngleSDK::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double theta0_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double theta0_one = force->numeric(FLERR,arg[2]); double repscale_one; // backward compatibility with old cg/cmm style input: @@ -252,9 +252,9 @@ void AngleSDK::coeff(int narg, char **arg) // otherwise assume repscale 1.0, since we were using // epsilon to turn repulsion on or off. if (narg == 6) { - repscale_one = force->numeric(arg[4]); + repscale_one = force->numeric(FLERR,arg[4]); if (repscale_one > 0.0) repscale_one = 1.0; - } else if (narg == 4) repscale_one = force->numeric(arg[3]); + } else if (narg == 4) repscale_one = force->numeric(FLERR,arg[3]); else if (narg == 3) repscale_one = 1.0; else error->all(FLERR,"Incorrect args for angle coefficients"); diff --git a/src/USER-CG-CMM/pair_cmm_common.cpp b/src/USER-CG-CMM/pair_cmm_common.cpp index cf6d0a5b5e..4856f5c00e 100644 --- a/src/USER-CG-CMM/pair_cmm_common.cpp +++ b/src/USER-CG-CMM/pair_cmm_common.cpp @@ -105,13 +105,13 @@ void PairCMMCommon::settings(int narg, char **arg) { if ((narg < 1) || (narg > 3)) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[1]); + else cut_coul_global = force->numeric(FLERR,arg[1]); cut_coulsq_global = cut_coul_global*cut_coul_global; // exponential coulomb screening (optional) - if (narg == 3) kappa = force->numeric(arg[2]); + if (narg == 3) kappa = force->numeric(FLERR,arg[2]); if (fabs(kappa) < SMALL) kappa=0.0; // reset cutoffs that have been explicitly set @@ -148,13 +148,13 @@ void PairCMMCommon::coeff(int narg, char **arg) int cg_type_one=find_cg_type(arg[2]); if (cg_type_one == CG_NOT_SET) error->all(FLERR,"Error reading CG type flag."); - double epsilon_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double epsilon_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); double cut_lj_one = cut_lj_global; double cut_coul_one = cut_coul_global; - if (narg >= 6) cut_lj_one = force->numeric(arg[5]); - if (narg == 7) cut_coul_one = force->numeric(arg[6]); + if (narg >= 6) cut_lj_one = force->numeric(FLERR,arg[5]); + if (narg == 7) cut_coul_one = force->numeric(FLERR,arg[6]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-CG-CMM/pair_lj_sdk.cpp b/src/USER-CG-CMM/pair_lj_sdk.cpp index 3ea0092d16..ef53e5db16 100644 --- a/src/USER-CG-CMM/pair_lj_sdk.cpp +++ b/src/USER-CG-CMM/pair_lj_sdk.cpp @@ -241,7 +241,7 @@ void PairLJSDK::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -270,11 +270,11 @@ void PairLJSDK::coeff(int narg, char **arg) if (lj_type_one == LJ_NOT_SET) error->all(FLERR,"Cannot parse LJ type flag."); - double epsilon_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double epsilon_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); double cut_one = cut_global; - if (narg == 6) cut_one = force->numeric(arg[5]); + if (narg == 6) cut_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-CG-CMM/pair_lj_sdk_coul_long.cpp b/src/USER-CG-CMM/pair_lj_sdk_coul_long.cpp index 0a19f00581..e2a57824bf 100644 --- a/src/USER-CG-CMM/pair_lj_sdk_coul_long.cpp +++ b/src/USER-CG-CMM/pair_lj_sdk_coul_long.cpp @@ -299,9 +299,9 @@ void PairLJSDKCoulLong::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[1]); + else cut_coul = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -331,11 +331,11 @@ void PairLJSDKCoulLong::coeff(int narg, char **arg) if (lj_type_one == LJ_NOT_SET) error->all(FLERR,"Cannot parse LJ type flag."); - double epsilon_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double epsilon_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); double cut_lj_one = cut_lj_global; - if (narg == 6) cut_lj_one = force->numeric(arg[5]); + if (narg == 6) cut_lj_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-EFF/pair_eff_cut.cpp b/src/USER-EFF/pair_eff_cut.cpp index 6c728d0da0..ffd1b467e2 100644 --- a/src/USER-EFF/pair_eff_cut.cpp +++ b/src/USER-EFF/pair_eff_cut.cpp @@ -798,7 +798,7 @@ void PairEffCut::settings(int narg, char **arg) PAULI_CORE_D[14] = 0.0; PAULI_CORE_E[14] = 0.0; - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); limit_eradius_flag = 0; pressure_with_evirials_flag = 0; @@ -818,7 +818,7 @@ void PairEffCut::settings(int narg, char **arg) else if (strcmp(arg[iarg],"ecp") == 0) { iarg += 1; while (iarg < narg) { - atype = force->inumeric(arg[iarg]); + atype = force->inumeric(FLERR,arg[iarg]); if (strcmp(arg[iarg+1],"C") == 0) ecp_type[atype] = 6; else if (strcmp(arg[iarg+1],"N") == 0) ecp_type[atype] = 7; else if (strcmp(arg[iarg+1],"O") == 0) ecp_type[atype] = 8; @@ -910,19 +910,19 @@ void PairEffCut::coeff(int narg, char **arg) if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); } else { int ecp; - ecp = force->inumeric(arg[0]); + ecp = force->inumeric(FLERR,arg[0]); if (strcmp(arg[1],"s") ==0) { - PAULI_CORE_A[ecp_type[ecp]] = force->numeric(arg[2]); - PAULI_CORE_B[ecp_type[ecp]] = force->numeric(arg[3]); - PAULI_CORE_C[ecp_type[ecp]] = force->numeric(arg[4]); + PAULI_CORE_A[ecp_type[ecp]] = force->numeric(FLERR,arg[2]); + PAULI_CORE_B[ecp_type[ecp]] = force->numeric(FLERR,arg[3]); + PAULI_CORE_C[ecp_type[ecp]] = force->numeric(FLERR,arg[4]); PAULI_CORE_D[ecp_type[ecp]] = 0.0; PAULI_CORE_E[ecp_type[ecp]] = 0.0; } else if (strcmp(arg[1],"p") ==0) { - PAULI_CORE_A[ecp_type[ecp]] = force->numeric(arg[2]); - PAULI_CORE_B[ecp_type[ecp]] = force->numeric(arg[3]); - PAULI_CORE_C[ecp_type[ecp]] = force->numeric(arg[4]); - PAULI_CORE_D[ecp_type[ecp]] = force->numeric(arg[5]); - PAULI_CORE_E[ecp_type[ecp]] = force->numeric(arg[6]); + PAULI_CORE_A[ecp_type[ecp]] = force->numeric(FLERR,arg[2]); + PAULI_CORE_B[ecp_type[ecp]] = force->numeric(FLERR,arg[3]); + PAULI_CORE_C[ecp_type[ecp]] = force->numeric(FLERR,arg[4]); + PAULI_CORE_D[ecp_type[ecp]] = force->numeric(FLERR,arg[5]); + PAULI_CORE_E[ecp_type[ecp]] = force->numeric(FLERR,arg[6]); } else error->all(FLERR,"Illegal pair_coeff command"); } } diff --git a/src/USER-MISC/angle_cosine_shift.cpp b/src/USER-MISC/angle_cosine_shift.cpp index d67d11d039..a144118cb2 100644 --- a/src/USER-MISC/angle_cosine_shift.cpp +++ b/src/USER-MISC/angle_cosine_shift.cpp @@ -174,8 +174,8 @@ void AngleCosineShift::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double umin = force->numeric(arg[1]); - double theta0 = force->numeric(arg[2]); + double umin = force->numeric(FLERR,arg[1]); + double theta0 = force->numeric(FLERR,arg[2]); // k=Umin/2 diff --git a/src/USER-MISC/angle_cosine_shift_exp.cpp b/src/USER-MISC/angle_cosine_shift_exp.cpp index afdfa95daf..ee4c014b10 100644 --- a/src/USER-MISC/angle_cosine_shift_exp.cpp +++ b/src/USER-MISC/angle_cosine_shift_exp.cpp @@ -196,9 +196,9 @@ void AngleCosineShiftExp::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double umin_ = force->numeric(arg[1]); - double theta0_ = force->numeric(arg[2]); - double a_ = force->numeric(arg[3]); + double umin_ = force->numeric(FLERR,arg[1]); + double theta0_ = force->numeric(FLERR,arg[2]); + double a_ = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/angle_dipole.cpp b/src/USER-MISC/angle_dipole.cpp index c61a69fdec..0762cf24fc 100644 --- a/src/USER-MISC/angle_dipole.cpp +++ b/src/USER-MISC/angle_dipole.cpp @@ -128,8 +128,8 @@ void AngleDipole::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double gamma0_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double gamma0_one = force->numeric(FLERR,arg[2]); // convert gamma0 from degrees to radians diff --git a/src/USER-MISC/angle_fourier.cpp b/src/USER-MISC/angle_fourier.cpp index b6cbd1236d..f067141b9a 100644 --- a/src/USER-MISC/angle_fourier.cpp +++ b/src/USER-MISC/angle_fourier.cpp @@ -175,10 +175,10 @@ void AngleFourier::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double C0_one = force->numeric(arg[2]); - double C1_one = force->numeric(arg[3]); - double C2_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[1]); + double C0_one = force->numeric(FLERR,arg[2]); + double C1_one = force->numeric(FLERR,arg[3]); + double C2_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/angle_fourier_simple.cpp b/src/USER-MISC/angle_fourier_simple.cpp index 03307e1c0e..cfe5681314 100644 --- a/src/USER-MISC/angle_fourier_simple.cpp +++ b/src/USER-MISC/angle_fourier_simple.cpp @@ -190,9 +190,9 @@ void AngleFourierSimple::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double C_one = force->numeric(arg[2]); - double N_one = force->numeric(arg[3]); + double k_one = force->numeric(FLERR,arg[1]); + double C_one = force->numeric(FLERR,arg[2]); + double N_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/angle_quartic.cpp b/src/USER-MISC/angle_quartic.cpp index 6f832b1ea0..8c007143ff 100644 --- a/src/USER-MISC/angle_quartic.cpp +++ b/src/USER-MISC/angle_quartic.cpp @@ -185,10 +185,10 @@ void AngleQuartic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nangletypes,ilo,ihi); - double theta0_one = force->numeric(arg[1]); - double k2_one = force->numeric(arg[2]); - double k3_one = force->numeric(arg[3]); - double k4_one = force->numeric(arg[4]); + double theta0_one = force->numeric(FLERR,arg[1]); + double k2_one = force->numeric(FLERR,arg[2]); + double k3_one = force->numeric(FLERR,arg[3]); + double k4_one = force->numeric(FLERR,arg[4]); // convert theta0 from degrees to radians diff --git a/src/USER-MISC/bond_harmonic_shift.cpp b/src/USER-MISC/bond_harmonic_shift.cpp index c717f5c098..e8c2b7d783 100644 --- a/src/USER-MISC/bond_harmonic_shift.cpp +++ b/src/USER-MISC/bond_harmonic_shift.cpp @@ -131,9 +131,9 @@ void BondHarmonicShift::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double Umin = force->numeric(arg[1]); // energy at minimum - double r0_one = force->numeric(arg[2]); // position of minimum - double r1_one = force->numeric(arg[3]); // position where energy = 0 + double Umin = force->numeric(FLERR,arg[1]); // energy at minimum + double r0_one = force->numeric(FLERR,arg[2]); // position of minimum + double r1_one = force->numeric(FLERR,arg[3]); // position where energy = 0 int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/bond_harmonic_shift_cut.cpp b/src/USER-MISC/bond_harmonic_shift_cut.cpp index 26a4033be1..a6d9425cb8 100644 --- a/src/USER-MISC/bond_harmonic_shift_cut.cpp +++ b/src/USER-MISC/bond_harmonic_shift_cut.cpp @@ -132,9 +132,9 @@ void BondHarmonicShiftCut::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nbondtypes,ilo,ihi); - double Umin = force->numeric(arg[1]); // energy at minimum - double r0_one = force->numeric(arg[2]); // position of minimum - double r1_one = force->numeric(arg[3]); // position where energy = 0 = cutoff + double Umin = force->numeric(FLERR,arg[1]); // energy at minimum + double r0_one = force->numeric(FLERR,arg[2]); // position of minimum + double r1_one = force->numeric(FLERR,arg[3]); // position where energy = 0 = cutoff int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/dihedral_cosine_shift_exp.cpp b/src/USER-MISC/dihedral_cosine_shift_exp.cpp index 0491f87c06..ea2c5e51e7 100644 --- a/src/USER-MISC/dihedral_cosine_shift_exp.cpp +++ b/src/USER-MISC/dihedral_cosine_shift_exp.cpp @@ -275,9 +275,9 @@ void DihedralCosineShiftExp::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); - double umin_ = force->numeric(arg[1]); - double theta0_ = force->numeric(arg[2]); - double a_ = force->numeric(arg[3]); + double umin_ = force->numeric(FLERR,arg[1]); + double theta0_ = force->numeric(FLERR,arg[2]); + double a_ = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/dihedral_fourier.cpp b/src/USER-MISC/dihedral_fourier.cpp index be25a729d8..3ddf4cd246 100644 --- a/src/USER-MISC/dihedral_fourier.cpp +++ b/src/USER-MISC/dihedral_fourier.cpp @@ -310,7 +310,7 @@ void DihedralFourier::coeff(int narg, char **arg) double k_one; int multiplicity_one; double shift_one; - int nterms_one = force->inumeric(arg[1]); + int nterms_one = force->inumeric(FLERR,arg[1]); if (nterms_one < 1) error->all(FLERR,"Incorrect number of terms arg for dihedral coefficients"); @@ -328,9 +328,9 @@ void DihedralFourier::coeff(int narg, char **arg) sin_shift[i] = new double [nterms_one]; for (int j = 0; jnumeric(arg[offset+1]); - multiplicity_one = force->inumeric(arg[offset+2]); - shift_one = force->numeric(arg[offset+3]); + k_one = force->numeric(FLERR,arg[offset+1]); + multiplicity_one = force->inumeric(FLERR,arg[offset+2]); + shift_one = force->numeric(FLERR,arg[offset+3]); k[i][j] = k_one; multiplicity[i][j] = multiplicity_one; shift[i][j] = shift_one; diff --git a/src/USER-MISC/dihedral_nharmonic.cpp b/src/USER-MISC/dihedral_nharmonic.cpp index 5f5d69ec43..d06ef08a9f 100644 --- a/src/USER-MISC/dihedral_nharmonic.cpp +++ b/src/USER-MISC/dihedral_nharmonic.cpp @@ -272,7 +272,7 @@ void DihedralNHarmonic::coeff(int narg, char **arg) { if (narg < 4 ) error->all(FLERR,"Incorrect args for dihedral coefficients"); - int n = force->inumeric(arg[1]); + int n = force->inumeric(FLERR,arg[1]); if (narg != n + 2 ) error->all(FLERR,"Incorrect args for dihedral coefficients"); if (!allocated) allocate(); @@ -285,7 +285,7 @@ void DihedralNHarmonic::coeff(int narg, char **arg) a[i] = new double [n]; nterms[i] = n; for (int j = 0; j < n; j++ ) { - a[i][j] = force->numeric(arg[2+j]); + a[i][j] = force->numeric(FLERR,arg[2+j]); setflag[i] = 1; } count++; diff --git a/src/USER-MISC/dihedral_quadratic.cpp b/src/USER-MISC/dihedral_quadratic.cpp index 59a5dc1991..06391b7128 100644 --- a/src/USER-MISC/dihedral_quadratic.cpp +++ b/src/USER-MISC/dihedral_quadratic.cpp @@ -286,8 +286,8 @@ void DihedralQuadratic::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->ndihedraltypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double phi0_one= force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double phi0_one= force->numeric(FLERR,arg[2]); // require k >= 0 if (k_one < 0.0) diff --git a/src/USER-MISC/dihedral_table.cpp b/src/USER-MISC/dihedral_table.cpp index 493efcd4a3..ce5d851ca3 100644 --- a/src/USER-MISC/dihedral_table.cpp +++ b/src/USER-MISC/dihedral_table.cpp @@ -731,7 +731,7 @@ void DihedralTable::settings(int narg, char **arg) else if (strcmp(arg[0],"spline") == 0) tabstyle = SPLINE; else error->all(FLERR,"Unknown table style in dihedral style table"); - tablength = force->inumeric(arg[1]); + tablength = force->inumeric(FLERR,arg[1]); if (tablength < 3) error->all(FLERR,"Illegal number of dihedral table entries"); // delete old tables, since cannot just change settings diff --git a/src/USER-MISC/improper_cossq.cpp b/src/USER-MISC/improper_cossq.cpp index 1614deaed1..36aeb61138 100644 --- a/src/USER-MISC/improper_cossq.cpp +++ b/src/USER-MISC/improper_cossq.cpp @@ -272,8 +272,8 @@ void ImproperCossq::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double chi_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double chi_one = force->numeric(FLERR,arg[2]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/improper_fourier.cpp b/src/USER-MISC/improper_fourier.cpp index 4717052a4b..43944727ff 100644 --- a/src/USER-MISC/improper_fourier.cpp +++ b/src/USER-MISC/improper_fourier.cpp @@ -284,12 +284,12 @@ void ImproperFourier::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double C0_one = force->numeric(arg[2]); - double C1_one = force->numeric(arg[3]); - double C2_one = force->numeric(arg[4]); + double k_one = force->numeric(FLERR,arg[1]); + double C0_one = force->numeric(FLERR,arg[2]); + double C1_one = force->numeric(FLERR,arg[3]); + double C2_one = force->numeric(FLERR,arg[4]); int all_one = 1; - if ( narg == 6 ) all_one = force->inumeric(arg[5]); + if ( narg == 6 ) all_one = force->inumeric(FLERR,arg[5]); // convert w0 from degrees to radians diff --git a/src/USER-MISC/improper_ring.cpp b/src/USER-MISC/improper_ring.cpp index 031821df82..ce011c7cad 100644 --- a/src/USER-MISC/improper_ring.cpp +++ b/src/USER-MISC/improper_ring.cpp @@ -294,8 +294,8 @@ void ImproperRing ::coeff(int narg, char **arg) int ilo,ihi; force->bounds(arg[0],atom->nimpropertypes,ilo,ihi); - double k_one = force->numeric(arg[1]); - double chi_one = force->numeric(arg[2]); + double k_one = force->numeric(FLERR,arg[1]); + double chi_one = force->numeric(FLERR,arg[2]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/pair_coul_diel.cpp b/src/USER-MISC/pair_coul_diel.cpp index 8fb4ef6372..7613201f20 100644 --- a/src/USER-MISC/pair_coul_diel.cpp +++ b/src/USER-MISC/pair_coul_diel.cpp @@ -161,7 +161,7 @@ void PairCoulDiel::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -186,12 +186,12 @@ void PairCoulDiel::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - eps_s = force->numeric(arg[2]); - double rme_one =force->numeric(arg[3]); - double sigmae_one = force->numeric(arg[4]); + eps_s = force->numeric(FLERR,arg[2]); + double rme_one =force->numeric(FLERR,arg[3]); + double sigmae_one = force->numeric(FLERR,arg[4]); double cut_one = cut_global; - if (narg == 6) cut_one = force->numeric(arg[5]); + if (narg == 6) cut_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/pair_gauss_cut.cpp b/src/USER-MISC/pair_gauss_cut.cpp index 06c2501110..e277f9865d 100644 --- a/src/USER-MISC/pair_gauss_cut.cpp +++ b/src/USER-MISC/pair_gauss_cut.cpp @@ -168,7 +168,7 @@ void PairGaussCut::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -193,12 +193,12 @@ void PairGaussCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double hgauss_one = force->numeric(arg[2]); - double rmh_one = force->numeric(arg[3]); - double sigmah_one = force->numeric(arg[4]); + double hgauss_one = force->numeric(FLERR,arg[2]); + double rmh_one = force->numeric(FLERR,arg[3]); + double sigmah_one = force->numeric(FLERR,arg[4]); double cut_one = cut_global; - if (narg == 6) cut_one = force->numeric(arg[5]); + if (narg == 6) cut_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-MISC/pair_list.cpp b/src/USER-MISC/pair_list.cpp index d23215a9b9..3b6f5c2d10 100644 --- a/src/USER-MISC/pair_list.cpp +++ b/src/USER-MISC/pair_list.cpp @@ -212,7 +212,7 @@ void PairList::settings(int narg, char **arg) if (narg < 2) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[1]); + cut_global = force->numeric(FLERR,arg[1]); if (narg > 2) { if (strcmp(arg[2],"nocheck") == 0) check_flag = 0; if (strcmp(arg[2],"check") == 0) check_flag = 1; @@ -267,12 +267,12 @@ void PairList::settings(int narg, char **arg) ptr = strtok(NULL," \t\n\r\f"); if ((ptr == NULL) || (*ptr == '#')) error->all(FLERR,"Incorrectly formatted harmonic pair parameters"); - par.parm.harm.k = force->numeric(ptr); + par.parm.harm.k = force->numeric(FLERR,ptr); ptr = strtok(NULL," \t\n\r\f"); if ((ptr == NULL) || (*ptr == '#')) error->all(FLERR,"Incorrectly formatted harmonic pair parameters"); - par.parm.harm.r0 = force->numeric(ptr); + par.parm.harm.r0 = force->numeric(FLERR,ptr); ++nharm; @@ -283,17 +283,17 @@ void PairList::settings(int narg, char **arg) ptr = strtok(NULL," \t\n\r\f"); if (!ptr) error->all(FLERR,"Incorrectly formatted morse pair parameters"); - par.parm.morse.d0 = force->numeric(ptr); + par.parm.morse.d0 = force->numeric(FLERR,ptr); ptr = strtok(NULL," \t\n\r\f"); if (!ptr) error->all(FLERR,"Incorrectly formatted morse pair parameters"); - par.parm.morse.alpha = force->numeric(ptr); + par.parm.morse.alpha = force->numeric(FLERR,ptr); ptr = strtok(NULL," \t\n\r\f"); if (!ptr) error->all(FLERR,"Incorrectly formatted morse pair parameters"); - par.parm.morse.r0 = force->numeric(ptr); + par.parm.morse.r0 = force->numeric(FLERR,ptr); ++nmorse; @@ -304,12 +304,12 @@ void PairList::settings(int narg, char **arg) ptr = strtok(NULL," \t\n\r\f"); if (!ptr) error->all(FLERR,"Incorrectly formatted 12-6 LJ pair parameters"); - par.parm.lj126.epsilon = force->numeric(ptr); + par.parm.lj126.epsilon = force->numeric(FLERR,ptr); ptr = strtok(NULL," \t\n\r\f"); if (!ptr) error->all(FLERR,"Incorrectly formatted 12-6 LJ pair parameters"); - par.parm.lj126.sigma = force->numeric(ptr); + par.parm.lj126.sigma = force->numeric(FLERR,ptr); ++nlj126; @@ -320,7 +320,7 @@ void PairList::settings(int narg, char **arg) // optional cutoff parameter. if not specified use global value ptr = strtok(NULL," \t\n\r\f"); if ((ptr != NULL) && (*ptr != '#')) { - double cut = force->numeric(ptr); + double cut = force->numeric(FLERR,ptr); par.cutsq = cut*cut; } else { par.cutsq = cut_global*cut_global; diff --git a/src/USER-MISC/pair_lj_sf.cpp b/src/USER-MISC/pair_lj_sf.cpp index 2e1c92c93f..ea4b91e67a 100644 --- a/src/USER-MISC/pair_lj_sf.cpp +++ b/src/USER-MISC/pair_lj_sf.cpp @@ -172,7 +172,7 @@ void PairLJShiftedForce::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); if (cut_global <= 0.0) error->all(FLERR,"Illegal pair_style command"); @@ -201,11 +201,11 @@ void PairLJShiftedForce::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); if (cut_one <= 0.0) error->all(FLERR,"Incorrect args for pair coefficients"); diff --git a/src/USER-MISC/pair_lj_sf_dipole_sf.cpp b/src/USER-MISC/pair_lj_sf_dipole_sf.cpp index e395371b73..f81a0062a9 100755 --- a/src/USER-MISC/pair_lj_sf_dipole_sf.cpp +++ b/src/USER-MISC/pair_lj_sf_dipole_sf.cpp @@ -331,9 +331,9 @@ void PairLJSFDipoleSF::settings(int narg, char **arg) if (strcmp(update->unit_style,"electron") == 0) error->all(FLERR,"Cannot (yet) use 'electron' units with dipoles"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[1]); + else cut_coul_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -362,13 +362,13 @@ void PairLJSFDipoleSF::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; double cut_coul_one = cut_coul_global; - if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(arg[4]); - if (narg == 6) cut_coul_one = force->numeric(arg[5]); + if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(FLERR,arg[4]); + if (narg == 6) cut_coul_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-SPH/pair_sph_heatconduction.cpp b/src/USER-SPH/pair_sph_heatconduction.cpp index 5f2f3b9002..6590a0afde 100644 --- a/src/USER-SPH/pair_sph_heatconduction.cpp +++ b/src/USER-SPH/pair_sph_heatconduction.cpp @@ -175,8 +175,8 @@ void PairSPHHeatConduction::coeff(int narg, char **arg) { force->bounds(arg[0], atom->ntypes, ilo, ihi); force->bounds(arg[1], atom->ntypes, jlo, jhi); - double alpha_one = force->numeric(arg[2]); - double cut_one = force->numeric(arg[3]); + double alpha_one = force->numeric(FLERR,arg[2]); + double cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-SPH/pair_sph_idealgas.cpp b/src/USER-SPH/pair_sph_idealgas.cpp index f18cceacd8..0336aa8025 100644 --- a/src/USER-SPH/pair_sph_idealgas.cpp +++ b/src/USER-SPH/pair_sph_idealgas.cpp @@ -217,8 +217,8 @@ void PairSPHIdealGas::coeff(int narg, char **arg) { force->bounds(arg[0], atom->ntypes, ilo, ihi); force->bounds(arg[1], atom->ntypes, jlo, jhi); - double viscosity_one = force->numeric(arg[2]); - double cut_one = force->numeric(arg[3]); + double viscosity_one = force->numeric(FLERR,arg[2]); + double cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-SPH/pair_sph_lj.cpp b/src/USER-SPH/pair_sph_lj.cpp index e25e1857d8..c934f2b5de 100644 --- a/src/USER-SPH/pair_sph_lj.cpp +++ b/src/USER-SPH/pair_sph_lj.cpp @@ -225,8 +225,8 @@ void PairSPHLJ::coeff(int narg, char **arg) { force->bounds(arg[0], atom->ntypes, ilo, ihi); force->bounds(arg[1], atom->ntypes, jlo, jhi); - double viscosity_one = force->numeric(arg[2]); - double cut_one = force->numeric(arg[3]); + double viscosity_one = force->numeric(FLERR,arg[2]); + double cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-SPH/pair_sph_rhosum.cpp b/src/USER-SPH/pair_sph_rhosum.cpp index 4e1a1e52da..e1a77c5d7a 100644 --- a/src/USER-SPH/pair_sph_rhosum.cpp +++ b/src/USER-SPH/pair_sph_rhosum.cpp @@ -229,7 +229,7 @@ void PairSPHRhoSum::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR, "Illegal number of setting arguments for pair_style sph/rhosum"); - nstep = force->inumeric(arg[0]); + nstep = force->inumeric(FLERR,arg[0]); } /* ---------------------------------------------------------------------- @@ -246,7 +246,7 @@ void PairSPHRhoSum::coeff(int narg, char **arg) { force->bounds(arg[0], atom->ntypes, ilo, ihi); force->bounds(arg[1], atom->ntypes, jlo, jhi); - double cut_one = force->numeric(arg[2]); + double cut_one = force->numeric(FLERR,arg[2]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/USER-SPH/pair_sph_taitwater.cpp b/src/USER-SPH/pair_sph_taitwater.cpp index d709d4cc9b..f9c9bbc2b0 100644 --- a/src/USER-SPH/pair_sph_taitwater.cpp +++ b/src/USER-SPH/pair_sph_taitwater.cpp @@ -246,10 +246,10 @@ void PairSPHTaitwater::coeff(int narg, char **arg) { force->bounds(arg[0], atom->ntypes, ilo, ihi); force->bounds(arg[1], atom->ntypes, jlo, jhi); - double rho0_one = force->numeric(arg[2]); - double soundspeed_one = force->numeric(arg[3]); - double viscosity_one = force->numeric(arg[4]); - double cut_one = force->numeric(arg[5]); + double rho0_one = force->numeric(FLERR,arg[2]); + double soundspeed_one = force->numeric(FLERR,arg[3]); + double viscosity_one = force->numeric(FLERR,arg[4]); + double cut_one = force->numeric(FLERR,arg[5]); double B_one = soundspeed_one * soundspeed_one * rho0_one / 7.0; int count = 0; diff --git a/src/USER-SPH/pair_sph_taitwater_morris.cpp b/src/USER-SPH/pair_sph_taitwater_morris.cpp index 1a4dc70cf7..a6b89893f3 100644 --- a/src/USER-SPH/pair_sph_taitwater_morris.cpp +++ b/src/USER-SPH/pair_sph_taitwater_morris.cpp @@ -246,10 +246,10 @@ void PairSPHTaitwaterMorris::coeff(int narg, char **arg) { force->bounds(arg[0], atom->ntypes, ilo, ihi); force->bounds(arg[1], atom->ntypes, jlo, jhi); - double rho0_one = force->numeric(arg[2]); - double soundspeed_one = force->numeric(arg[3]); - double viscosity_one = force->numeric(arg[4]); - double cut_one = force->numeric(arg[5]); + double rho0_one = force->numeric(FLERR,arg[2]); + double soundspeed_one = force->numeric(FLERR,arg[3]); + double viscosity_one = force->numeric(FLERR,arg[4]); + double cut_one = force->numeric(FLERR,arg[5]); double B_one = soundspeed_one * soundspeed_one * rho0_one / 7.0; int count = 0; diff --git a/src/atom.cpp b/src/atom.cpp index 099a5098e6..6a3e869528 100644 --- a/src/atom.cpp +++ b/src/atom.cpp @@ -424,8 +424,8 @@ void Atom::modify_params(int narg, char **arg) iarg += 2; } else if (strcmp(arg[iarg],"sort") == 0) { if (iarg+3 > narg) error->all(FLERR,"Illegal atom_modify command"); - sortfreq = force->inumeric(arg[iarg+1]); - userbinsize = force->numeric(arg[iarg+2]); + sortfreq = force->inumeric(FLERR,arg[iarg+1]); + userbinsize = force->numeric(FLERR,arg[iarg+2]); if (sortfreq < 0 || userbinsize < 0.0) error->all(FLERR,"Illegal atom_modify command"); if (sortfreq >= 0 && firstgroupname) diff --git a/src/balance.cpp b/src/balance.cpp index 999a2e65fb..4638599f0a 100644 --- a/src/balance.cpp +++ b/src/balance.cpp @@ -114,7 +114,7 @@ void Balance::command(int narg, char **arg) user_xsplit[0] = 0.0; iarg++; for (int i = 1; i < procgrid[0]; i++) - user_xsplit[i] = force->numeric(arg[iarg++]); + user_xsplit[i] = force->numeric(FLERR,arg[iarg++]); user_xsplit[procgrid[0]] = 1.0; } } else if (strcmp(arg[iarg],"y") == 0) { @@ -132,7 +132,7 @@ void Balance::command(int narg, char **arg) user_ysplit[0] = 0.0; iarg++; for (int i = 1; i < procgrid[1]; i++) - user_ysplit[i] = force->numeric(arg[iarg++]); + user_ysplit[i] = force->numeric(FLERR,arg[iarg++]); user_ysplit[procgrid[1]] = 1.0; } } else if (strcmp(arg[iarg],"z") == 0) { @@ -150,7 +150,7 @@ void Balance::command(int narg, char **arg) user_zsplit[0] = 0.0; iarg++; for (int i = 1; i < procgrid[2]; i++) - user_zsplit[i] = force->numeric(arg[iarg++]); + user_zsplit[i] = force->numeric(FLERR,arg[iarg++]); user_zsplit[procgrid[2]] = 1.0; } diff --git a/src/fix_langevin.cpp b/src/fix_langevin.cpp index 5035e0b826..6f11342a36 100644 --- a/src/fix_langevin.cpp +++ b/src/fix_langevin.cpp @@ -102,7 +102,7 @@ FixLangevin::FixLangevin(LAMMPS *lmp, int narg, char **arg) : if (strcmp(arg[iarg],"angmom") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix langevin command"); if (strcmp(arg[iarg+1],"no") == 0) ascale = 0.0; - else ascale = force->numeric(arg[iarg+1]); + else ascale = force->numeric(FLERR,arg[iarg+1]); iarg += 2; } else if (strcmp(arg[iarg],"gjf") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal fix langevin command"); diff --git a/src/fix_restrain.cpp b/src/fix_restrain.cpp index e2f8bbf6f2..bd1d39f471 100644 --- a/src/fix_restrain.cpp +++ b/src/fix_restrain.cpp @@ -78,9 +78,9 @@ FixRestrain::FixRestrain(LAMMPS *lmp, int narg, char **arg) : rstyle[nrestrain] = BOND; ids[nrestrain][0] = atoi(arg[iarg+1]); ids[nrestrain][1] = atoi(arg[iarg+2]); - kstart[nrestrain] = force->numeric(arg[iarg+3]); - kstop[nrestrain] = force->numeric(arg[iarg+4]); - target[nrestrain] = force->numeric(arg[iarg+5]); + kstart[nrestrain] = force->numeric(FLERR,arg[iarg+3]); + kstop[nrestrain] = force->numeric(FLERR,arg[iarg+4]); + target[nrestrain] = force->numeric(FLERR,arg[iarg+5]); iarg += 6; } else if (strcmp(arg[iarg],"angle") == 0) { if (iarg+7 > narg) error->all(FLERR,"Illegal fix restrain command"); @@ -88,9 +88,9 @@ FixRestrain::FixRestrain(LAMMPS *lmp, int narg, char **arg) : ids[nrestrain][0] = atoi(arg[iarg+1]); ids[nrestrain][1] = atoi(arg[iarg+2]); ids[nrestrain][2] = atoi(arg[iarg+3]); - kstart[nrestrain] = force->numeric(arg[iarg+4]); - kstop[nrestrain] = force->numeric(arg[iarg+5]); - target[nrestrain] = force->numeric(arg[iarg+6]); + kstart[nrestrain] = force->numeric(FLERR,arg[iarg+4]); + kstop[nrestrain] = force->numeric(FLERR,arg[iarg+5]); + target[nrestrain] = force->numeric(FLERR,arg[iarg+6]); target[nrestrain] *= MY_PI / 180.0; iarg += 7; } else if (strcmp(arg[iarg],"dihedral") == 0) { @@ -100,9 +100,9 @@ FixRestrain::FixRestrain(LAMMPS *lmp, int narg, char **arg) : ids[nrestrain][1] = atoi(arg[iarg+2]); ids[nrestrain][2] = atoi(arg[iarg+3]); ids[nrestrain][3] = atoi(arg[iarg+4]); - kstart[nrestrain] = force->numeric(arg[iarg+5]); - kstop[nrestrain] = force->numeric(arg[iarg+6]); - target[nrestrain] = force->numeric(arg[iarg+7]); + kstart[nrestrain] = force->numeric(FLERR,arg[iarg+5]); + kstop[nrestrain] = force->numeric(FLERR,arg[iarg+6]); + target[nrestrain] = force->numeric(FLERR,arg[iarg+7]); target[nrestrain] *= MY_PI / 180.0; cos_target[nrestrain] = cos(target[nrestrain]); sin_target[nrestrain] = sin(target[nrestrain]); diff --git a/src/force.cpp b/src/force.cpp index 43ef2cf8e9..096f4ef753 100644 --- a/src/force.cpp +++ b/src/force.cpp @@ -669,17 +669,18 @@ void Force::bounds(char *str, int nmax, int &nlo, int &nhi) /* ---------------------------------------------------------------------- read a floating point value from a string generate an error if not a legitimate floating point value + called by various commands to check validity of their arguments ------------------------------------------------------------------------- */ -double Force::numeric(char *str) +double Force::numeric(const char *file, int line, char *str) { int n = strlen(str); for (int i = 0; i < n; i++) { if (isdigit(str[i])) continue; if (str[i] == '-' || str[i] == '+' || str[i] == '.') continue; if (str[i] == 'e' || str[i] == 'E') continue; - error->all(FLERR,"Expected floating point parameter in " - "input script or data file"); + error->all(file,line,"Expected floating point parameter " + "in input script or data file"); } return atof(str); @@ -688,14 +689,16 @@ double Force::numeric(char *str) /* ---------------------------------------------------------------------- read an integer value from a string generate an error if not a legitimate integer value + called by various commands to check validity of their arguments ------------------------------------------------------------------------- */ -int Force::inumeric(char *str) +int Force::inumeric(const char *file, int line, char *str) { int n = strlen(str); for (int i = 0; i < n; i++) { if (isdigit(str[i]) || str[i] == '-' || str[i] == '+') continue; - error->all(FLERR,"Expected integer parameter in input script or data file"); + error->all(file,line, + "Expected integer parameter in input script or data file"); } return atoi(str); diff --git a/src/force.h b/src/force.h index 0a84cd869b..d7165e9f06 100644 --- a/src/force.h +++ b/src/force.h @@ -95,8 +95,8 @@ class Force : protected Pointers { void set_special(int, char **); void bounds(char *, int, int &, int &); - double numeric(char *); - int inumeric(char *); + double numeric(const char *, int, char *); + int inumeric(const char *, int, char *); bigint memory_usage(); }; diff --git a/src/lattice.cpp b/src/lattice.cpp index 4fcba7cbf9..ab184a04f7 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -53,7 +53,7 @@ Lattice::Lattice(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) if (style == NONE) { if (narg != 2) error->all(FLERR,"Illegal lattice command"); - xlattice = ylattice = zlattice = atof(arg[1]); + xlattice = ylattice = zlattice = force->numeric(FLERR,arg[1]); if (xlattice <= 0.0) error->all(FLERR,"Illegal lattice command"); return; } @@ -75,7 +75,7 @@ Lattice::Lattice(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) // scale = conversion factor between lattice and box units if (narg < 2) error->all(FLERR,"Illegal lattice command"); - scale = atof(arg[1]); + scale = force->numeric(FLERR,arg[1]); if (scale <= 0.0) error->all(FLERR,"Illegal lattice command"); // set basis atoms for each style @@ -142,9 +142,9 @@ Lattice::Lattice(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) while (iarg < narg) { if (strcmp(arg[iarg],"origin") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal lattice command"); - origin[0] = atof(arg[iarg+1]); - origin[1] = atof(arg[iarg+2]); - origin[2] = atof(arg[iarg+3]); + origin[0] = force->numeric(FLERR,arg[iarg+1]); + origin[1] = force->numeric(FLERR,arg[iarg+2]); + origin[2] = force->numeric(FLERR,arg[iarg+3]); if (origin[0] < 0.0 || origin[0] >= 1.0 || origin[1] < 0.0 || origin[1] >= 1.0 || origin[2] < 0.0 || origin[2] >= 1.0) @@ -162,17 +162,17 @@ Lattice::Lattice(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) if (dim == 0) orient = orientx; else if (dim == 1) orient = orienty; else if (dim == 2) orient = orientz; - orient[0] = force->inumeric(arg[iarg+2]); - orient[1] = force->inumeric(arg[iarg+3]); - orient[2] = force->inumeric(arg[iarg+4]); + orient[0] = force->inumeric(FLERR,arg[iarg+2]); + orient[1] = force->inumeric(FLERR,arg[iarg+3]); + orient[2] = force->inumeric(FLERR,arg[iarg+4]); iarg += 5; } else if (strcmp(arg[iarg],"spacing") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal lattice command"); spaceflag = 1; - xlattice = atof(arg[iarg+1]); - ylattice = atof(arg[iarg+2]); - zlattice = atof(arg[iarg+3]); + xlattice = force->numeric(FLERR,arg[iarg+1]); + ylattice = force->numeric(FLERR,arg[iarg+2]); + zlattice = force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"a1") == 0) { @@ -180,27 +180,27 @@ Lattice::Lattice(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) if (style != CUSTOM) error->all(FLERR, "Invalid option in lattice command for non-custom style"); - a1[0] = atof(arg[iarg+1]); - a1[1] = atof(arg[iarg+2]); - a1[2] = atof(arg[iarg+3]); + a1[0] = force->numeric(FLERR,arg[iarg+1]); + a1[1] = force->numeric(FLERR,arg[iarg+2]); + a1[2] = force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"a2") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal lattice command"); if (style != CUSTOM) error->all(FLERR, "Invalid option in lattice command for non-custom style"); - a2[0] = atof(arg[iarg+1]); - a2[1] = atof(arg[iarg+2]); - a2[2] = atof(arg[iarg+3]); + a2[0] = force->numeric(FLERR,arg[iarg+1]); + a2[1] = force->numeric(FLERR,arg[iarg+2]); + a2[2] = force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"a3") == 0) { if (iarg+4 > narg) error->all(FLERR,"Illegal lattice command"); if (style != CUSTOM) error->all(FLERR, "Invalid option in lattice command for non-custom style"); - a3[0] = atof(arg[iarg+1]); - a3[1] = atof(arg[iarg+2]); - a3[2] = atof(arg[iarg+3]); + a3[0] = force->numeric(FLERR,arg[iarg+1]); + a3[1] = force->numeric(FLERR,arg[iarg+2]); + a3[2] = force->numeric(FLERR,arg[iarg+3]); iarg += 4; } else if (strcmp(arg[iarg],"basis") == 0) { @@ -208,9 +208,9 @@ Lattice::Lattice(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) if (style != CUSTOM) error->all(FLERR, "Invalid option in lattice command for non-custom style"); - double x = atof(arg[iarg+1]); - double y = atof(arg[iarg+2]); - double z = atof(arg[iarg+3]); + double x = force->numeric(FLERR,arg[iarg+1]); + double y = force->numeric(FLERR,arg[iarg+2]); + double z = force->numeric(FLERR,arg[iarg+3]); if (x < 0.0 || x >= 1.0 || y < 0.0 || y >= 1.0 || z < 0.0 || z >= 1.0) error->all(FLERR,"Illegal lattice command"); add_basis(x,y,z); diff --git a/src/pair_born.cpp b/src/pair_born.cpp index cb54bf3855..289661e032 100644 --- a/src/pair_born.cpp +++ b/src/pair_born.cpp @@ -200,15 +200,15 @@ void PairBorn::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); - double rho_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double a_one = force->numeric(FLERR,arg[2]); + double rho_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); if (rho_one <= 0) error->all(FLERR,"Incorrect args for pair coefficients"); - double c_one = force->numeric(arg[5]); - double d_one = force->numeric(arg[6]); + double c_one = force->numeric(FLERR,arg[5]); + double d_one = force->numeric(FLERR,arg[6]); double cut_one = cut_global; - if (narg == 8) cut_one = force->numeric(arg[7]); + if (narg == 8) cut_one = force->numeric(FLERR,arg[7]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_born_coul_wolf.cpp b/src/pair_born_coul_wolf.cpp index 33ff284d76..5ed0fca429 100644 --- a/src/pair_born_coul_wolf.cpp +++ b/src/pair_born_coul_wolf.cpp @@ -218,10 +218,10 @@ void PairBornCoulWolf::settings(int narg, char **arg) { if (narg < 2 || narg > 3) error->all(FLERR,"Illegal pair_style command"); - alf = force->numeric(arg[0]); - cut_lj_global = force->numeric(arg[1]); + alf = force->numeric(FLERR,arg[0]); + cut_lj_global = force->numeric(FLERR,arg[1]); if (narg == 2) cut_coul = cut_lj_global; - else cut_coul = force->numeric(arg[2]); + else cut_coul = force->numeric(FLERR,arg[2]); if (allocated) { int i,j; @@ -245,15 +245,15 @@ void PairBornCoulWolf::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); - double rho_one = force->numeric(arg[3]); - double sigma_one = force->numeric(arg[4]); + double a_one = force->numeric(FLERR,arg[2]); + double rho_one = force->numeric(FLERR,arg[3]); + double sigma_one = force->numeric(FLERR,arg[4]); if (rho_one <= 0) error->all(FLERR,"Incorrect args for pair coefficients"); - double c_one = force->numeric(arg[5]); - double d_one = force->numeric(arg[6]); + double c_one = force->numeric(FLERR,arg[5]); + double d_one = force->numeric(FLERR,arg[6]); double cut_lj_one = cut_lj_global; - if (narg == 8) cut_lj_one = force->numeric(arg[7]); + if (narg == 8) cut_lj_one = force->numeric(FLERR,arg[7]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_buck.cpp b/src/pair_buck.cpp index 056a2b4f87..b53e3415b3 100644 --- a/src/pair_buck.cpp +++ b/src/pair_buck.cpp @@ -164,7 +164,7 @@ void PairBuck::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -189,13 +189,13 @@ void PairBuck::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); - double rho_one = force->numeric(arg[3]); + double a_one = force->numeric(FLERR,arg[2]); + double rho_one = force->numeric(FLERR,arg[3]); if (rho_one <= 0) error->all(FLERR,"Incorrect args for pair coefficients"); - double c_one = force->numeric(arg[4]); + double c_one = force->numeric(FLERR,arg[4]); double cut_one = cut_global; - if (narg == 6) cut_one = force->numeric(arg[5]); + if (narg == 6) cut_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_buck_coul_cut.cpp b/src/pair_buck_coul_cut.cpp index bd77c41b61..45226eed72 100644 --- a/src/pair_buck_coul_cut.cpp +++ b/src/pair_buck_coul_cut.cpp @@ -193,9 +193,9 @@ void PairBuckCoulCut::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[1]); + else cut_coul_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -223,15 +223,15 @@ void PairBuckCoulCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); - double rho_one = force->numeric(arg[3]); + double a_one = force->numeric(FLERR,arg[2]); + double rho_one = force->numeric(FLERR,arg[3]); if (rho_one <= 0) error->all(FLERR,"Incorrect args for pair coefficients"); - double c_one = force->numeric(arg[4]); + double c_one = force->numeric(FLERR,arg[4]); double cut_lj_one = cut_lj_global; double cut_coul_one = cut_coul_global; - if (narg >= 6) cut_coul_one = cut_lj_one = force->numeric(arg[5]); - if (narg == 7) cut_coul_one = force->numeric(arg[6]); + if (narg >= 6) cut_coul_one = cut_lj_one = force->numeric(FLERR,arg[5]); + if (narg == 7) cut_coul_one = force->numeric(FLERR,arg[6]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_coul_cut.cpp b/src/pair_coul_cut.cpp index 2e4313f788..cd893a0d97 100644 --- a/src/pair_coul_cut.cpp +++ b/src/pair_coul_cut.cpp @@ -148,7 +148,7 @@ void PairCoulCut::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -175,7 +175,7 @@ void PairCoulCut::coeff(int narg, char **arg) force->bounds(arg[1],atom->ntypes,jlo,jhi); double cut_one = cut_global; - if (narg == 3) cut_one = force->numeric(arg[2]); + if (narg == 3) cut_one = force->numeric(FLERR,arg[2]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_coul_debye.cpp b/src/pair_coul_debye.cpp index 81d8a9e905..150ab12fd1 100644 --- a/src/pair_coul_debye.cpp +++ b/src/pair_coul_debye.cpp @@ -116,8 +116,8 @@ void PairCoulDebye::settings(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal pair_style command"); - kappa = force->numeric(arg[0]); - cut_global = force->numeric(arg[1]); + kappa = force->numeric(FLERR,arg[0]); + cut_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set diff --git a/src/pair_coul_dsf.cpp b/src/pair_coul_dsf.cpp index 85700f81f8..5cb156ea8d 100644 --- a/src/pair_coul_dsf.cpp +++ b/src/pair_coul_dsf.cpp @@ -174,8 +174,8 @@ void PairCoulDSF::settings(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal pair_style command"); - alpha = force->numeric(arg[0]); - cut_coul = force->numeric(arg[1]); + alpha = force->numeric(FLERR,arg[0]); + cut_coul = force->numeric(FLERR,arg[1]); } /* ---------------------------------------------------------------------- diff --git a/src/pair_coul_wolf.cpp b/src/pair_coul_wolf.cpp index 8cf821c2d7..81dd9a43e4 100644 --- a/src/pair_coul_wolf.cpp +++ b/src/pair_coul_wolf.cpp @@ -177,8 +177,8 @@ void PairCoulWolf::settings(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal pair_style command"); - alf = force->numeric(arg[0]); - cut_coul = force->numeric(arg[1]); + alf = force->numeric(FLERR,arg[0]); + cut_coul = force->numeric(FLERR,arg[1]); } /* ---------------------------------------------------------------------- diff --git a/src/pair_dpd.cpp b/src/pair_dpd.cpp index 04f216ec85..a30491743d 100644 --- a/src/pair_dpd.cpp +++ b/src/pair_dpd.cpp @@ -187,9 +187,9 @@ void PairDPD::settings(int narg, char **arg) { if (narg != 3) error->all(FLERR,"Illegal pair_style command"); - temperature = force->numeric(arg[0]); - cut_global = force->numeric(arg[1]); - seed = force->inumeric(arg[2]); + temperature = force->numeric(FLERR,arg[0]); + cut_global = force->numeric(FLERR,arg[1]); + seed = force->inumeric(FLERR,arg[2]); // initialize Marsaglia RNG with processor-unique seed @@ -220,11 +220,11 @@ void PairDPD::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a0_one = force->numeric(arg[2]); - double gamma_one = force->numeric(arg[3]); + double a0_one = force->numeric(FLERR,arg[2]); + double gamma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_dpd_tstat.cpp b/src/pair_dpd_tstat.cpp index e09d332c8e..f3f7ecc1ca 100644 --- a/src/pair_dpd_tstat.cpp +++ b/src/pair_dpd_tstat.cpp @@ -140,10 +140,10 @@ void PairDPDTstat::settings(int narg, char **arg) { if (narg != 4) error->all(FLERR,"Illegal pair_style command"); - t_start = force->numeric(arg[0]); - t_stop = force->numeric(arg[1]); - cut_global = force->numeric(arg[2]); - seed = force->inumeric(arg[3]); + t_start = force->numeric(FLERR,arg[0]); + t_stop = force->numeric(FLERR,arg[1]); + cut_global = force->numeric(FLERR,arg[2]); + seed = force->inumeric(FLERR,arg[3]); temperature = t_start; @@ -178,10 +178,10 @@ void PairDPDTstat::coeff(int narg, char **arg) force->bounds(arg[1],atom->ntypes,jlo,jhi); double a0_one = 0.0; - double gamma_one = force->numeric(arg[2]); + double gamma_one = force->numeric(FLERR,arg[2]); double cut_one = cut_global; - if (narg == 4) cut_one = force->numeric(arg[3]); + if (narg == 4) cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_lj96_cut.cpp b/src/pair_lj96_cut.cpp index 1a845a3511..907a74efc5 100644 --- a/src/pair_lj96_cut.cpp +++ b/src/pair_lj96_cut.cpp @@ -434,7 +434,7 @@ void PairLJ96Cut::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set diff --git a/src/pair_lj_cubic.cpp b/src/pair_lj_cubic.cpp index 2fd224239d..87c774c690 100644 --- a/src/pair_lj_cubic.cpp +++ b/src/pair_lj_cubic.cpp @@ -203,8 +203,8 @@ void PairLJCubic::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double rmin = sigma_one*RT6TWO; int count = 0; diff --git a/src/pair_lj_cut.cpp b/src/pair_lj_cut.cpp index 38a8261996..8e4219c53d 100644 --- a/src/pair_lj_cut.cpp +++ b/src/pair_lj_cut.cpp @@ -429,7 +429,7 @@ void PairLJCut::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -455,11 +455,11 @@ void PairLJCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_one = cut_global; - if (narg == 5) cut_one = force->numeric(arg[4]); + if (narg == 5) cut_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_lj_cut_coul_cut.cpp b/src/pair_lj_cut_coul_cut.cpp index cebae3d975..98eaecf63e 100644 --- a/src/pair_lj_cut_coul_cut.cpp +++ b/src/pair_lj_cut_coul_cut.cpp @@ -186,9 +186,9 @@ void PairLJCutCoulCut::settings(int narg, char **arg) { if (narg < 1 || narg > 2) error->all(FLERR,"Illegal pair_style command"); - cut_lj_global = force->numeric(arg[0]); + cut_lj_global = force->numeric(FLERR,arg[0]); if (narg == 1) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[1]); + else cut_coul_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -217,13 +217,13 @@ void PairLJCutCoulCut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; double cut_coul_one = cut_coul_global; - if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(arg[4]); - if (narg == 6) cut_coul_one = force->numeric(arg[5]); + if (narg >= 5) cut_coul_one = cut_lj_one = force->numeric(FLERR,arg[4]); + if (narg == 6) cut_coul_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_lj_cut_coul_debye.cpp b/src/pair_lj_cut_coul_debye.cpp index 7a892ad7f7..44f2b395b2 100644 --- a/src/pair_lj_cut_coul_debye.cpp +++ b/src/pair_lj_cut_coul_debye.cpp @@ -133,10 +133,10 @@ void PairLJCutCoulDebye::settings(int narg, char **arg) { if (narg < 2 || narg > 3) error->all(FLERR,"Illegal pair_style command"); - kappa = force->numeric(arg[0]); - cut_lj_global = force->numeric(arg[1]); + kappa = force->numeric(FLERR,arg[0]); + cut_lj_global = force->numeric(FLERR,arg[1]); if (narg == 2) cut_coul_global = cut_lj_global; - else cut_coul_global = force->numeric(arg[2]); + else cut_coul_global = force->numeric(FLERR,arg[2]); // reset cutoffs that were previously set from data file diff --git a/src/pair_lj_cut_coul_dsf.cpp b/src/pair_lj_cut_coul_dsf.cpp index 4f89a14ddc..63ec7b002b 100644 --- a/src/pair_lj_cut_coul_dsf.cpp +++ b/src/pair_lj_cut_coul_dsf.cpp @@ -207,9 +207,9 @@ void PairLJCutCoulDSF::settings(int narg, char **arg) { if (narg != 3) error->all(FLERR,"Illegal pair_style command"); - alpha = force->numeric(arg[0]); - cut_lj_global = force->numeric(arg[1]); - cut_coul = force->numeric(arg[2]); + alpha = force->numeric(FLERR,arg[0]); + cut_lj_global = force->numeric(FLERR,arg[1]); + cut_coul = force->numeric(FLERR,arg[2]); // reset cutoffs that have been explicitly set @@ -236,11 +236,11 @@ void PairLJCutCoulDSF::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(arg[4]); + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_lj_expand.cpp b/src/pair_lj_expand.cpp index 6b0175f7bf..02e148fe4e 100644 --- a/src/pair_lj_expand.cpp +++ b/src/pair_lj_expand.cpp @@ -170,7 +170,7 @@ void PairLJExpand::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -195,12 +195,12 @@ void PairLJExpand::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); - double shift_one = force->numeric(arg[4]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); + double shift_one = force->numeric(FLERR,arg[4]); double cut_one = cut_global; - if (narg == 6) cut_one = force->numeric(arg[5]); + if (narg == 6) cut_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_lj_gromacs.cpp b/src/pair_lj_gromacs.cpp index f62e2e879a..c2d96abd4b 100644 --- a/src/pair_lj_gromacs.cpp +++ b/src/pair_lj_gromacs.cpp @@ -188,8 +188,8 @@ void PairLJGromacs::settings(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal pair_style command"); - cut_inner_global = force->numeric(arg[0]); - cut_global = force->numeric(arg[1]); + cut_inner_global = force->numeric(FLERR,arg[0]); + cut_global = force->numeric(FLERR,arg[1]); if (cut_inner_global <= 0.0 || cut_inner_global > cut_global) error->all(FLERR,"Illegal pair_style command"); @@ -221,14 +221,14 @@ void PairLJGromacs::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_inner_one = cut_inner_global; double cut_one = cut_global; if (narg == 6) { - cut_inner_one = force->numeric(arg[4]); - cut_one = force->numeric(arg[5]); + cut_inner_one = force->numeric(FLERR,arg[4]); + cut_one = force->numeric(FLERR,arg[5]); } if (cut_inner_one <= 0.0 || cut_inner_one > cut_one) diff --git a/src/pair_lj_gromacs_coul_gromacs.cpp b/src/pair_lj_gromacs_coul_gromacs.cpp index 7f973708e3..c5d3bc580d 100644 --- a/src/pair_lj_gromacs_coul_gromacs.cpp +++ b/src/pair_lj_gromacs_coul_gromacs.cpp @@ -216,14 +216,14 @@ void PairLJGromacsCoulGromacs::settings(int narg, char **arg) if (narg != 2 && narg != 4) error->all(FLERR,"Illegal pair_style command"); - cut_lj_inner = force->numeric(arg[0]); - cut_lj = force->numeric(arg[1]); + cut_lj_inner = force->numeric(FLERR,arg[0]); + cut_lj = force->numeric(FLERR,arg[1]); if (narg == 2) { cut_coul_inner = cut_lj_inner; cut_coul = cut_lj; } else { - cut_coul_inner = force->numeric(arg[2]); - cut_coul = force->numeric(arg[3]); + cut_coul_inner = force->numeric(FLERR,arg[2]); + cut_coul = force->numeric(FLERR,arg[3]); } if (cut_lj_inner <= 0.0 || cut_coul_inner < 0.0) @@ -245,8 +245,8 @@ void PairLJGromacsCoulGromacs::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_lj_smooth.cpp b/src/pair_lj_smooth.cpp index 3465c3ca19..b75d3ed002 100644 --- a/src/pair_lj_smooth.cpp +++ b/src/pair_lj_smooth.cpp @@ -192,8 +192,8 @@ void PairLJSmooth::settings(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal pair_style command"); - cut_inner_global = force->numeric(arg[0]); - cut_global = force->numeric(arg[1]); + cut_inner_global = force->numeric(FLERR,arg[0]); + cut_global = force->numeric(FLERR,arg[1]); if (cut_inner_global <= 0.0 || cut_inner_global > cut_global) error->all(FLERR,"Illegal pair_style command"); @@ -225,14 +225,14 @@ void PairLJSmooth::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); double cut_inner_one = cut_inner_global; double cut_one = cut_global; if (narg == 6) { - cut_inner_one = force->numeric(arg[4]); - cut_one = force->numeric(arg[5]); + cut_inner_one = force->numeric(FLERR,arg[4]); + cut_one = force->numeric(FLERR,arg[5]); } if (cut_inner_one <= 0.0 || cut_inner_one > cut_one) diff --git a/src/pair_mie_cut.cpp b/src/pair_mie_cut.cpp index 6ae35c5204..7cb59b013f 100644 --- a/src/pair_mie_cut.cpp +++ b/src/pair_mie_cut.cpp @@ -440,7 +440,7 @@ void PairMIECut::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -466,13 +466,13 @@ void PairMIECut::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double epsilon_one = force->numeric(arg[2]); - double sigma_one = force->numeric(arg[3]); - double gamR_one = force->numeric(arg[4]); - double gamA_one = force->numeric(arg[5]); + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); + double gamR_one = force->numeric(FLERR,arg[4]); + double gamA_one = force->numeric(FLERR,arg[5]); double cut_one = cut_global; - if (narg == 7) cut_one = force->numeric(arg[6]); + if (narg == 7) cut_one = force->numeric(FLERR,arg[6]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_morse.cpp b/src/pair_morse.cpp index 080f19fe5e..21675470ba 100644 --- a/src/pair_morse.cpp +++ b/src/pair_morse.cpp @@ -154,7 +154,7 @@ void PairMorse::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -179,12 +179,12 @@ void PairMorse::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double d0_one = force->numeric(arg[2]); - double alpha_one = force->numeric(arg[3]); - double r0_one = force->numeric(arg[4]); + double d0_one = force->numeric(FLERR,arg[2]); + double alpha_one = force->numeric(FLERR,arg[3]); + double r0_one = force->numeric(FLERR,arg[4]); double cut_one = cut_global; - if (narg == 6) cut_one = force->numeric(arg[5]); + if (narg == 6) cut_one = force->numeric(FLERR,arg[5]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_soft.cpp b/src/pair_soft.cpp index 5782b20e05..a7d36416d4 100644 --- a/src/pair_soft.cpp +++ b/src/pair_soft.cpp @@ -148,7 +148,7 @@ void PairSoft::settings(int narg, char **arg) { if (narg != 1) error->all(FLERR,"Illegal pair_style command"); - cut_global = force->numeric(arg[0]); + cut_global = force->numeric(FLERR,arg[0]); // reset cutoffs that have been explicitly set @@ -174,10 +174,10 @@ void PairSoft::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double prefactor_one = force->numeric(arg[2]); + double prefactor_one = force->numeric(FLERR,arg[2]); double cut_one = cut_global; - if (narg == 4) cut_one = force->numeric(arg[3]); + if (narg == 4) cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { diff --git a/src/pair_table.cpp b/src/pair_table.cpp index 9d7d806028..13600a047a 100644 --- a/src/pair_table.cpp +++ b/src/pair_table.cpp @@ -206,7 +206,7 @@ void PairTable::settings(int narg, char **arg) else if (strcmp(arg[0],"bitmap") == 0) tabstyle = BITMAP; else error->all(FLERR,"Unknown table style in pair_style command"); - tablength = force->inumeric(arg[1]); + tablength = force->inumeric(FLERR,arg[1]); if (tablength < 2) error->all(FLERR,"Illegal number of pair table entries"); // optional keywords @@ -263,7 +263,7 @@ void PairTable::coeff(int narg, char **arg) // set table cutoff - if (narg == 5) tb->cut = force->numeric(arg[4]); + if (narg == 5) tb->cut = force->numeric(FLERR,arg[4]); else if (tb->rflag) tb->cut = tb->rhi; else tb->cut = tb->rfile[tb->ninput-1]; diff --git a/src/pair_yukawa.cpp b/src/pair_yukawa.cpp index e7682dacb4..bb4e4af242 100644 --- a/src/pair_yukawa.cpp +++ b/src/pair_yukawa.cpp @@ -151,8 +151,8 @@ void PairYukawa::settings(int narg, char **arg) { if (narg != 2) error->all(FLERR,"Illegal pair_style command"); - kappa = force->numeric(arg[0]); - cut_global = force->numeric(arg[1]); + kappa = force->numeric(FLERR,arg[0]); + cut_global = force->numeric(FLERR,arg[1]); // reset cutoffs that have been explicitly set @@ -178,10 +178,10 @@ void PairYukawa::coeff(int narg, char **arg) force->bounds(arg[0],atom->ntypes,ilo,ihi); force->bounds(arg[1],atom->ntypes,jlo,jhi); - double a_one = force->numeric(arg[2]); + double a_one = force->numeric(FLERR,arg[2]); double cut_one = cut_global; - if (narg == 4) cut_one = force->numeric(arg[3]); + if (narg == 4) cut_one = force->numeric(FLERR,arg[3]); int count = 0; for (int i = ilo; i <= ihi; i++) { From b92fa00337d0f3af54634fae3c7343e9db4e3a88 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 20:15:36 +0000 Subject: [PATCH 06/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10104 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/MANYBODY/pair_adp.cpp | 1 + src/MANYBODY/pair_airebo.cpp | 1 + src/MANYBODY/pair_bop.cpp | 2 ++ src/MANYBODY/pair_comb.cpp | 1 + src/MANYBODY/pair_eam.cpp | 1 + src/MANYBODY/pair_eam_alloy.cpp | 1 + src/MANYBODY/pair_eam_fs.cpp | 1 + src/MANYBODY/pair_eim.cpp | 1 + src/MANYBODY/pair_lcbop.cpp | 1 + src/MANYBODY/pair_sw.cpp | 1 + src/MANYBODY/pair_tersoff.cpp | 1 + src/MEAM/pair_meam.cpp | 1 + src/REAX/pair_reax.cpp | 1 + src/USER-MISC/pair_edip.cpp | 1 + src/USER-MISC/pair_meam_spline.cpp | 1 + src/USER-MISC/pair_meam_sw_spline.cpp | 1 + src/USER-MISC/pair_tersoff_table.cpp | 1 + src/USER-REAXC/pair_reax_c.cpp | 1 + src/pair.cpp | 19 ++++++++++++++++++- src/pair.h | 1 + 20 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/MANYBODY/pair_adp.cpp b/src/MANYBODY/pair_adp.cpp index 90d6eab223..4939965528 100644 --- a/src/MANYBODY/pair_adp.cpp +++ b/src/MANYBODY/pair_adp.cpp @@ -66,6 +66,7 @@ PairADP::PairADP(LAMMPS *lmp) : Pair(lmp) single_enable = 0; one_coeff = 1; + manybody_flag = 1; } /* ---------------------------------------------------------------------- diff --git a/src/MANYBODY/pair_airebo.cpp b/src/MANYBODY/pair_airebo.cpp index 28939dbeb3..8665b736a9 100644 --- a/src/MANYBODY/pair_airebo.cpp +++ b/src/MANYBODY/pair_airebo.cpp @@ -58,6 +58,7 @@ PairAIREBO::PairAIREBO(LAMMPS *lmp) : Pair(lmp) maxpage = 0; pages = NULL; nC = nH = NULL; + manybody_flag = 1; } /* ---------------------------------------------------------------------- diff --git a/src/MANYBODY/pair_bop.cpp b/src/MANYBODY/pair_bop.cpp index a66fa34d41..3410e6c3f2 100644 --- a/src/MANYBODY/pair_bop.cpp +++ b/src/MANYBODY/pair_bop.cpp @@ -62,6 +62,8 @@ PairBOP::PairBOP(LAMMPS *lmp) : Pair(lmp) { single_enable = 0; one_coeff = 1; + manybody_flag = 1; + map = NULL; pi_a = NULL; pro_delta = NULL; diff --git a/src/MANYBODY/pair_comb.cpp b/src/MANYBODY/pair_comb.cpp index f6170bdf6c..2a071b91ad 100644 --- a/src/MANYBODY/pair_comb.cpp +++ b/src/MANYBODY/pair_comb.cpp @@ -50,6 +50,7 @@ PairComb::PairComb(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nmax = 0; NCo = NULL; diff --git a/src/MANYBODY/pair_eam.cpp b/src/MANYBODY/pair_eam.cpp index 5e5f8f3930..629620101e 100644 --- a/src/MANYBODY/pair_eam.cpp +++ b/src/MANYBODY/pair_eam.cpp @@ -37,6 +37,7 @@ using namespace LAMMPS_NS; PairEAM::PairEAM(LAMMPS *lmp) : Pair(lmp) { restartinfo = 0; + manybody_flag = 1; nmax = 0; rho = NULL; diff --git a/src/MANYBODY/pair_eam_alloy.cpp b/src/MANYBODY/pair_eam_alloy.cpp index 318d6421e6..6e46f6edb9 100644 --- a/src/MANYBODY/pair_eam_alloy.cpp +++ b/src/MANYBODY/pair_eam_alloy.cpp @@ -33,6 +33,7 @@ using namespace LAMMPS_NS; PairEAMAlloy::PairEAMAlloy(LAMMPS *lmp) : PairEAM(lmp) { one_coeff = 1; + manybody_flag = 1; } /* ---------------------------------------------------------------------- diff --git a/src/MANYBODY/pair_eam_fs.cpp b/src/MANYBODY/pair_eam_fs.cpp index 875822fd5c..80f4239928 100644 --- a/src/MANYBODY/pair_eam_fs.cpp +++ b/src/MANYBODY/pair_eam_fs.cpp @@ -33,6 +33,7 @@ using namespace LAMMPS_NS; PairEAMFS::PairEAMFS(LAMMPS *lmp) : PairEAM(lmp) { one_coeff = 1; + manybody_flag = 1; } /* ---------------------------------------------------------------------- diff --git a/src/MANYBODY/pair_eim.cpp b/src/MANYBODY/pair_eim.cpp index f4418a25d2..f4f3fcc653 100644 --- a/src/MANYBODY/pair_eim.cpp +++ b/src/MANYBODY/pair_eim.cpp @@ -39,6 +39,7 @@ PairEIM::PairEIM(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; setfl = NULL; nmax = 0; diff --git a/src/MANYBODY/pair_lcbop.cpp b/src/MANYBODY/pair_lcbop.cpp index 0de291b286..06f04b1f2f 100644 --- a/src/MANYBODY/pair_lcbop.cpp +++ b/src/MANYBODY/pair_lcbop.cpp @@ -46,6 +46,7 @@ using namespace MathConst; PairLCBOP::PairLCBOP(LAMMPS *lmp) : Pair(lmp) { single_enable = 0; one_coeff = 1; + manybody_flag = 1; ghostneigh = 1; maxlocal = 0; diff --git a/src/MANYBODY/pair_sw.cpp b/src/MANYBODY/pair_sw.cpp index 7af668d37f..5efc5b7c64 100755 --- a/src/MANYBODY/pair_sw.cpp +++ b/src/MANYBODY/pair_sw.cpp @@ -43,6 +43,7 @@ PairSW::PairSW(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nelements = 0; elements = NULL; diff --git a/src/MANYBODY/pair_tersoff.cpp b/src/MANYBODY/pair_tersoff.cpp index 6c3c006b12..9e90a8902b 100755 --- a/src/MANYBODY/pair_tersoff.cpp +++ b/src/MANYBODY/pair_tersoff.cpp @@ -44,6 +44,7 @@ PairTersoff::PairTersoff(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nelements = 0; elements = NULL; diff --git a/src/MEAM/pair_meam.cpp b/src/MEAM/pair_meam.cpp index 4a3b15898c..bdfc9ea159 100644 --- a/src/MEAM/pair_meam.cpp +++ b/src/MEAM/pair_meam.cpp @@ -49,6 +49,7 @@ PairMEAM::PairMEAM(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nmax = 0; rho = rho0 = rho1 = rho2 = rho3 = frhop = NULL; diff --git a/src/REAX/pair_reax.cpp b/src/REAX/pair_reax.cpp index 341aba65d7..e8db8a2a3d 100644 --- a/src/REAX/pair_reax.cpp +++ b/src/REAX/pair_reax.cpp @@ -48,6 +48,7 @@ PairREAX::PairREAX(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; no_virial_fdotr_compute = 1; nextra = 14; diff --git a/src/USER-MISC/pair_edip.cpp b/src/USER-MISC/pair_edip.cpp index 47585562e2..bc0d75adfd 100755 --- a/src/USER-MISC/pair_edip.cpp +++ b/src/USER-MISC/pair_edip.cpp @@ -55,6 +55,7 @@ PairEDIP::PairEDIP(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nelements = 0; elements = NULL; diff --git a/src/USER-MISC/pair_meam_spline.cpp b/src/USER-MISC/pair_meam_spline.cpp index 24ca5d0a67..1684bed227 100644 --- a/src/USER-MISC/pair_meam_spline.cpp +++ b/src/USER-MISC/pair_meam_spline.cpp @@ -49,6 +49,7 @@ PairMEAMSpline::PairMEAMSpline(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nelements = 0; elements = NULL; diff --git a/src/USER-MISC/pair_meam_sw_spline.cpp b/src/USER-MISC/pair_meam_sw_spline.cpp index bf3917a769..d0385ab010 100644 --- a/src/USER-MISC/pair_meam_sw_spline.cpp +++ b/src/USER-MISC/pair_meam_sw_spline.cpp @@ -47,6 +47,7 @@ PairMEAMSWSpline::PairMEAMSWSpline(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; nelements = 0; elements = NULL; diff --git a/src/USER-MISC/pair_tersoff_table.cpp b/src/USER-MISC/pair_tersoff_table.cpp index ce25a86393..f9940d8d86 100644 --- a/src/USER-MISC/pair_tersoff_table.cpp +++ b/src/USER-MISC/pair_tersoff_table.cpp @@ -56,6 +56,7 @@ PairTersoffTable::PairTersoffTable(LAMMPS *lmp) : Pair(lmp) { single_enable = 0; one_coeff = 1; + manybody_flag = 1; nelements = 0; elements = NULL; diff --git a/src/USER-REAXC/pair_reax_c.cpp b/src/USER-REAXC/pair_reax_c.cpp index d4945afee6..1f5ee66a97 100644 --- a/src/USER-REAXC/pair_reax_c.cpp +++ b/src/USER-REAXC/pair_reax_c.cpp @@ -61,6 +61,7 @@ PairReaxC::PairReaxC(LAMMPS *lmp) : Pair(lmp) single_enable = 0; restartinfo = 0; one_coeff = 1; + manybody_flag = 1; ghostneigh = 1; system = (reax_system *) diff --git a/src/pair.cpp b/src/pair.cpp index 3908dbb324..5efa8dd602 100644 --- a/src/pair.cpp +++ b/src/pair.cpp @@ -71,6 +71,7 @@ Pair::Pair(LAMMPS *lmp) : Pointers(lmp) // pair_modify settings compute_flag = 1; + manybody_flag = 0; offset_flag = 0; mix_flag = GEOMETRIC; tail_flag = 0; @@ -172,11 +173,27 @@ void Pair::init() if (tail_flag && domain->nonperiodic && comm->me == 0) error->warning(FLERR,"Using pair tail corrections with nonperiodic system"); - if (!allocated) error->all(FLERR,"All pair coeffs are not set"); + // for manybody potentials + // check if bonded exclusions could invalidate the neighbor list + + if (manybody_flag && atom->molecular) { + int flag = 0; + if (atom->nbonds > 0 && force->special_lj[1] == 0.0 && + force->special_coul[1] == 0.0) flag = 1; + if (atom->nangles > 0 && force->special_lj[2] == 0.0 && + force->special_coul[2] == 0.0) flag = 1; + if (atom->ndihedrals > 0 && force->special_lj[3] == 0.0 && + force->special_coul[3] == 0.0) flag = 1; + if (flag && comm->me == 0) + error->warning(FLERR,"Using a manybody potential with " + "bonds/angles/dihedrals and special_bond exclusions"); + } // I,I coeffs must be set // init_one() will check if I,J is set explicitly or inferred by mixing + if (!allocated) error->all(FLERR,"All pair coeffs are not set"); + for (i = 1; i <= atom->ntypes; i++) if (setflag[i][i] == 0) error->all(FLERR,"All pair coeffs are not set"); diff --git a/src/pair.h b/src/pair.h index 72eb257c85..e1cea71ddb 100644 --- a/src/pair.h +++ b/src/pair.h @@ -95,6 +95,7 @@ class Pair : protected Pointers { unsigned int datamask_ext; int compute_flag; // 0 if skip compute() + int manybody_flag; // 1 if abort for manybody style with bonds Pair(class LAMMPS *); virtual ~Pair(); From 77866dd57800ad37660a24ba25cdc4525128b580 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 20:20:27 +0000 Subject: [PATCH 07/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10105 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/pair.h | 2 +- src/pair_hybrid.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pair.h b/src/pair.h index e1cea71ddb..89e3c4e365 100644 --- a/src/pair.h +++ b/src/pair.h @@ -46,6 +46,7 @@ class Pair : protected Pointers { int restartinfo; // 1 if pair style writes restart info int respa_enable; // 1 if inner/middle/outer rRESPA routines int one_coeff; // 1 if allows only one coeff * * call + int manybody_flag; // 1 if a manybody potential int no_virial_fdotr_compute; // 1 if does not invoke virial_fdotr_compute() int writedata; // 1 if writes coeffs to data file int ghostneigh; // 1 if pair style needs neighbors of ghosts @@ -95,7 +96,6 @@ class Pair : protected Pointers { unsigned int datamask_ext; int compute_flag; // 0 if skip compute() - int manybody_flag; // 1 if abort for manybody style with bonds Pair(class LAMMPS *); virtual ~Pair(); diff --git a/src/pair_hybrid.cpp b/src/pair_hybrid.cpp index 8821c2cb4b..f40345dfb7 100644 --- a/src/pair_hybrid.cpp +++ b/src/pair_hybrid.cpp @@ -285,6 +285,7 @@ void PairHybrid::flags() // single_enable = 1 if any sub-style is set // respa_enable = 1 if any sub-style is set + // manybody_flag = 1 if any sub-style is set // no_virial_fdotr_compute = 1 if any sub-style is set // ghostneigh = 1 if any sub-style is set // ewaldflag, pppmflag, msmflag, dispersionflag, tip4pflag = 1 @@ -294,6 +295,7 @@ void PairHybrid::flags() for (m = 0; m < nstyles; m++) { if (styles[m]->single_enable) single_enable = 1; if (styles[m]->respa_enable) respa_enable = 1; + if (styles[m]->manybody_flag) manybody_flag = 1; if (styles[m]->no_virial_fdotr_compute) no_virial_fdotr_compute = 1; if (styles[m]->ghostneigh) ghostneigh = 1; if (styles[m]->ewaldflag) ewaldflag = 1; From 34b028f059a5861fec0716543a4eeb421fd1aef5 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:24:14 +0000 Subject: [PATCH 08/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10107 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/MOLECULE/pair_hbond_dreiding_lj.cpp | 27 ++++++++++++++-------- src/MOLECULE/pair_hbond_dreiding_morse.cpp | 25 ++++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/MOLECULE/pair_hbond_dreiding_lj.cpp b/src/MOLECULE/pair_hbond_dreiding_lj.cpp index 575836fe6d..381629e161 100644 --- a/src/MOLECULE/pair_hbond_dreiding_lj.cpp +++ b/src/MOLECULE/pair_hbond_dreiding_lj.cpp @@ -79,8 +79,8 @@ void PairHbondDreidingLJ::compute(int eflag, int vflag) { int i,j,k,m,ii,jj,kk,inum,jnum,knum,itype,jtype,ktype; double delx,dely,delz,rsq,rsq1,rsq2,r1,r2; - double factor_hb,force_angle,force_kernel,evdwl,eng_lj,ehbond; - double c,s,a,b,ac,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + double factor_hb,force_angle,force_kernel,evdwl,eng_lj,ehbond,force_switch; + double c,s,a,b,ac,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2,d; double fi[3],fj[3],delr1[3],delr2[3]; double r2inv,r10inv; double switch1,switch2; @@ -175,14 +175,20 @@ void PairHbondDreidingLJ::compute(int eflag, int vflag) powint(c,pm.ap-1)*s; eng_lj = r10inv*(pm.lj3*r2inv - pm.lj4); + + force_switch=0.0; + if (rsq > pm.cut_innersq) { switch1 = (pm.cut_outersq-rsq) * (pm.cut_outersq-rsq) * (pm.cut_outersq + 2.0*rsq - 3.0*pm.cut_innersq) / pm.denom_vdw; switch2 = 12.0*rsq * (pm.cut_outersq-rsq) * (rsq-pm.cut_innersq) / pm.denom_vdw; - force_kernel = force_kernel*switch1 + eng_lj*switch2; - eng_lj *= switch1; + + force_kernel *= switch1; + force_angle *= switch1; + force_switch = eng_lj*switch2/rsq; + eng_lj *= switch1; } if (eflag) { @@ -193,6 +199,7 @@ void PairHbondDreidingLJ::compute(int eflag, int vflag) a = factor_hb*force_angle/s; b = factor_hb*force_kernel; + d = factor_hb*force_switch; a11 = a*c / rsq1; a12 = -a / (r1*r2); @@ -205,12 +212,12 @@ void PairHbondDreidingLJ::compute(int eflag, int vflag) vz1 = a11*delr1[2] + a12*delr2[2]; vz2 = a22*delr2[2] + a12*delr1[2]; - fi[0] = vx1 + b*delx; - fi[1] = vy1 + b*dely; - fi[2] = vz1 + b*delz; - fj[0] = vx2 - b*delx; - fj[1] = vy2 - b*dely; - fj[2] = vz2 - b*delz; + fi[0] = vx1 + b*delx + d*delx; + fi[1] = vy1 + b*dely + d*dely; + fi[2] = vz1 + b*delz + d*delz; + fj[0] = vx2 - b*delx - d*delx; + fj[1] = vy2 - b*dely - d*dely; + fj[2] = vz2 - b*delz - d*delz; f[i][0] += fi[0]; f[i][1] += fi[1]; diff --git a/src/MOLECULE/pair_hbond_dreiding_morse.cpp b/src/MOLECULE/pair_hbond_dreiding_morse.cpp index 02d6bbba0d..e3c26a5dc2 100644 --- a/src/MOLECULE/pair_hbond_dreiding_morse.cpp +++ b/src/MOLECULE/pair_hbond_dreiding_morse.cpp @@ -50,8 +50,8 @@ void PairHbondDreidingMorse::compute(int eflag, int vflag) { int i,j,k,m,ii,jj,kk,inum,jnum,knum,itype,jtype,ktype; double delx,dely,delz,rsq,rsq1,rsq2,r1,r2; - double factor_hb,force_angle,force_kernel,evdwl,ehbond; - double c,s,a,b,ac,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; + double factor_hb,force_angle,force_kernel,force_switch,evdwl,ehbond; + double c,s,a,b,d,ac,a11,a12,a22,vx1,vx2,vy1,vy2,vz1,vz2; double fi[3],fj[3],delr1[3],delr2[3]; double r,dr,dexp,eng_morse,switch1,switch2; int *ilist,*jlist,*klist,*numneigh,**firstneigh; @@ -143,6 +143,7 @@ void PairHbondDreidingMorse::compute(int eflag, int vflag) eng_morse = pm.d0 * (dexp*dexp - 2.0*dexp); force_kernel = pm.morse1*(dexp*dexp - dexp)/r * powint(c,pm.ap); force_angle = pm.ap * eng_morse * powint(c,pm.ap-1)*s; + force_switch = 0.0; if (rsq > pm.cut_innersq) { switch1 = (pm.cut_outersq-rsq) * (pm.cut_outersq-rsq) * @@ -150,8 +151,11 @@ void PairHbondDreidingMorse::compute(int eflag, int vflag) pm.denom_vdw; switch2 = 12.0*rsq * (pm.cut_outersq-rsq) * (rsq-pm.cut_innersq) / pm.denom_vdw; - force_kernel = force_kernel*switch1 + eng_morse*switch2; - eng_morse *= switch1; + + force_kernel *= switch1; + force_angle *= switch1; + force_switch = eng_morse*switch2/rsq; + eng_morse *= switch1; } if (eflag) { @@ -162,6 +166,7 @@ void PairHbondDreidingMorse::compute(int eflag, int vflag) a = factor_hb*force_angle/s; b = factor_hb*force_kernel; + d = factor_hb*force_switch; a11 = a*c / rsq1; a12 = -a / (r1*r2); @@ -174,12 +179,12 @@ void PairHbondDreidingMorse::compute(int eflag, int vflag) vz1 = a11*delr1[2] + a12*delr2[2]; vz2 = a22*delr2[2] + a12*delr1[2]; - fi[0] = vx1 + b*delx; - fi[1] = vy1 + b*dely; - fi[2] = vz1 + b*delz; - fj[0] = vx2 - b*delx; - fj[1] = vy2 - b*dely; - fj[2] = vz2 - b*delz; + fi[0] = vx1 + (b+d)*delx; + fi[1] = vy1 + (b+d)*dely; + fi[2] = vz1 + (b+d)*delz; + fj[0] = vx2 - (b+d)*delx; + fj[1] = vy2 - (b+d)*dely; + fj[2] = vz2 - (b+d)*delz; f[i][0] += fi[0]; f[i][1] += fi[1]; From 5e15c24733b34e94bfe4c46873309667d7dcbdb5 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:29:35 +0000 Subject: [PATCH 09/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10108 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 9f16dd3c82..5969b7cda1 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define LAMMPS_VERSION "17 Jun 2013" +#define LAMMPS_VERSION "24 Jun 2013" From 3c0e5bf25c5b9f93fbade8a5552e0d95d9faa32e Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:29:36 +0000 Subject: [PATCH 10/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10109 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/Manual.html | 2 +- doc/Manual.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Manual.html b/doc/Manual.html index fa6b7c7d28..7174aa3c2c 100644 --- a/doc/Manual.html +++ b/doc/Manual.html @@ -22,7 +22,7 @@

LAMMPS Documentation

-

17 Jun 2013 version +

24 Jun 2013 version

Version info:

diff --git a/doc/Manual.txt b/doc/Manual.txt index 4ced741213..0a0f819d87 100644 --- a/doc/Manual.txt +++ b/doc/Manual.txt @@ -18,7 +18,7 @@

LAMMPS Documentation :c,h3 -17 Jun 2013 version :c,h4 +24 Jun 2013 version :c,h4 Version info: :h4 From 069f071cf301e3660634246d1e8e7a182b7e3b21 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:36:51 +0000 Subject: [PATCH 11/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10111 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/lmptype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lmptype.h b/src/lmptype.h index 66b535dce1..8ad5f9dafa 100644 --- a/src/lmptype.h +++ b/src/lmptype.h @@ -55,7 +55,7 @@ namespace LAMMPS_NS { // default to 32-bit smallint and tagint, 64-bit bigint -#if !defined(LAMMPS_SMALLSMALL) && !defined(LAMMPS_BIGBIG) +#if !defined(LAMMPS_SMALLSMALL) && !defined(LAMMPS_BIGBIG) && !defined(LAMMPS_SMALLBIG) #define LAMMPS_SMALLBIG #endif From 92e78a2f8ee48f5413957d4dd5063dc570cbd9e8 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:36:57 +0000 Subject: [PATCH 12/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10112 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- tools/binary2txt.cpp | 25 +++++++++++++++++++++++- tools/restart2data.cpp | 43 ++++++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/tools/binary2txt.cpp b/tools/binary2txt.cpp index e0841db8a0..3662a6da1b 100644 --- a/tools/binary2txt.cpp +++ b/tools/binary2txt.cpp @@ -22,15 +22,38 @@ #include "stdio.h" #include "string.h" -// this should match setting in src/lmptype.h +// these must match settings in src/lmptype.h which builds LAMMPS with +// -DLAMMPS_SMALLBIG (the default), -DLAMMPS_BIGBIG, or -DLAMMPS_SMALLSMALL +// you can edit the tools/Makefile to enforce the same setting +// for the build of binary2txt, e.g. +// g++ -g -DLAMMPS_BIGBIG binarytxt.o -o binary2txt +// again -DLAMMPS_SMALLBIG is the default #include "stdint.h" #define __STDC_FORMAT_MACROS #include "inttypes.h" +#ifndef PRId64 +#define PRId64 "ld" +#endif + +#if !defined(LAMMPS_SMALLSMALL) && !defined(LAMMPS_BIGBIG) && !defined(LAMMPS_SMALLBIG) +#define LAMMPS_SMALLBIG +#endif + +#if defined(LAMMPS_SMALLBIG) typedef int tagint; typedef int64_t bigint; #define BIGINT_FORMAT "%" PRId64 +#elif defined(LAMMPS_SMALLSMALL) +typedef int tagint; +typedef int bigint; +#define BIGINT_FORMAT "%d" +#else /* LAMMPS_BIGBIG */ +typedef int64_t tagint; +typedef int64_t bigint; +#define BIGINT_FORMAT "%" PRId64 +#endif int main(int narg, char **arg) { diff --git a/tools/restart2data.cpp b/tools/restart2data.cpp index 8996303fad..01edf78f75 100644 --- a/tools/restart2data.cpp +++ b/tools/restart2data.cpp @@ -36,27 +36,50 @@ #define MAX_GROUP 32 #define PI (4.0*atan(1.0)) -// these must match settings in src/lmptype.h -// depending on whether you built LAMMPS with -// -DLAMMPS_SMALLBIG (the default), -DLAMMPS_BIGBIG, or -DLAMMPS_SMALLSMALL +// these must match settings in src/lmptype.h which builds LAMMPS with +// -DLAMMPS_SMALLBIG (the default), -DLAMMPS_BIGBIG, or -DLAMMPS_SMALLSMALL +// you can edit the tools/Makefile to enforce the same setting +// for the build of restart2data, e.g. +// g++ -g -DLAMMPS_BIGBIG restart2data.o -o restart2data +// again -DLAMMPS_SMALLBIG is the default #include "stdint.h" #define __STDC_FORMAT_MACROS #include "inttypes.h" +#ifndef PRId64 +#define PRId64 "ld" +#endif + +#if !defined(LAMMPS_SMALLSMALL) && !defined(LAMMPS_BIGBIG) && !defined(LAMMPS_SMALLBIG) +#define LAMMPS_SMALLBIG +#endif + +#if defined(LAMMPS_SMALLBIG) typedef int tagint; typedef int64_t bigint; #define BIGINT_FORMAT "%" PRId64 - #define IMGMASK 1023 #define IMGMAX 512 #define IMGBITS 10 #define IMG2BITS 20 - -//#define IMGMASK 2097151 -//#define IMGMAX 1048576 -//#define IMGBITS 21 -//#define IMG2BITS 42 +#elif defined(LAMMPS_SMALLSMALL) +typedef int tagint; +typedef int bigint; +#define BIGINT_FORMAT "%d" +#define IMGMASK 1023 +#define IMGMAX 512 +#define IMGBITS 10 +#define IMG2BITS 20 +#else /* LAMMPS_BIGBIG */ +typedef int64_t tagint; +typedef int64_t bigint; +#define BIGINT_FORMAT "%" PRId64 +#define IMGMASK 2097151 +#define IMGMAX 1048576 +#define IMGBITS 21 +#define IMG2BITS 42 +#endif // same as write_restart.cpp @@ -544,7 +567,7 @@ int main (int narg, char **arg) void header(FILE *fp, Data &data) { - const char *version = "17 May 2012"; + const char *version = "27 June 2013"; data.triclinic = 0; From 8d699db7a887e5ba56b9c7862219a74ebc46f9e3 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:41:33 +0000 Subject: [PATCH 13/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10113 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/DIPOLE/atom_vec_dipole.cpp | 2 ++ src/MOLECULE/atom_vec_angle.cpp | 2 ++ src/MOLECULE/atom_vec_bond.cpp | 2 ++ src/MOLECULE/atom_vec_full.cpp | 2 ++ src/MOLECULE/atom_vec_molecular.cpp | 2 ++ src/PERI/atom_vec_peri.cpp | 2 ++ src/USER-AWPMD/atom_vec_wavepacket.cpp | 2 ++ src/USER-EFF/atom_vec_electron.cpp | 2 ++ src/USER-SPH/atom_vec_meso.cpp | 2 ++ src/atom_vec_atomic.cpp | 2 ++ src/atom_vec_body.cpp | 2 ++ src/atom_vec_charge.cpp | 2 ++ src/atom_vec_ellipsoid.cpp | 2 ++ src/atom_vec_line.cpp | 2 ++ src/atom_vec_sphere.cpp | 2 ++ src/atom_vec_tri.cpp | 2 ++ 16 files changed, 32 insertions(+) diff --git a/src/DIPOLE/atom_vec_dipole.cpp b/src/DIPOLE/atom_vec_dipole.cpp index 899b902039..ba7b41a12d 100644 --- a/src/DIPOLE/atom_vec_dipole.cpp +++ b/src/DIPOLE/atom_vec_dipole.cpp @@ -575,6 +575,7 @@ int AtomVecDipole::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = q[i]; @@ -660,6 +661,7 @@ int AtomVecDipole::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/MOLECULE/atom_vec_angle.cpp b/src/MOLECULE/atom_vec_angle.cpp index 909917dcac..f95ed9e83f 100644 --- a/src/MOLECULE/atom_vec_angle.cpp +++ b/src/MOLECULE/atom_vec_angle.cpp @@ -527,6 +527,7 @@ int AtomVecAngle::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = molecule[i]; @@ -649,6 +650,7 @@ int AtomVecAngle::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/MOLECULE/atom_vec_bond.cpp b/src/MOLECULE/atom_vec_bond.cpp index 8a03623993..430ec16366 100644 --- a/src/MOLECULE/atom_vec_bond.cpp +++ b/src/MOLECULE/atom_vec_bond.cpp @@ -506,6 +506,7 @@ int AtomVecBond::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = molecule[i]; @@ -612,6 +613,7 @@ int AtomVecBond::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/MOLECULE/atom_vec_full.cpp b/src/MOLECULE/atom_vec_full.cpp index 00efb3d916..b24ad15395 100644 --- a/src/MOLECULE/atom_vec_full.cpp +++ b/src/MOLECULE/atom_vec_full.cpp @@ -596,6 +596,7 @@ int AtomVecFull::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = q[i]; @@ -757,6 +758,7 @@ int AtomVecFull::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/MOLECULE/atom_vec_molecular.cpp b/src/MOLECULE/atom_vec_molecular.cpp index 8a46fa7711..073f31f483 100644 --- a/src/MOLECULE/atom_vec_molecular.cpp +++ b/src/MOLECULE/atom_vec_molecular.cpp @@ -584,6 +584,7 @@ int AtomVecMolecular::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = molecule[i]; @@ -743,6 +744,7 @@ int AtomVecMolecular::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/PERI/atom_vec_peri.cpp b/src/PERI/atom_vec_peri.cpp index 2be54b4ad9..5c898c4455 100644 --- a/src/PERI/atom_vec_peri.cpp +++ b/src/PERI/atom_vec_peri.cpp @@ -566,6 +566,7 @@ int AtomVecPeri::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = vfrac[i]; @@ -654,6 +655,7 @@ int AtomVecPeri::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/USER-AWPMD/atom_vec_wavepacket.cpp b/src/USER-AWPMD/atom_vec_wavepacket.cpp index db3dc2e54f..a57470a6c9 100644 --- a/src/USER-AWPMD/atom_vec_wavepacket.cpp +++ b/src/USER-AWPMD/atom_vec_wavepacket.cpp @@ -690,6 +690,7 @@ int AtomVecWavepacket::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = q[i]; @@ -781,6 +782,7 @@ int AtomVecWavepacket::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/USER-EFF/atom_vec_electron.cpp b/src/USER-EFF/atom_vec_electron.cpp index eb6f8eadda..e7be71ed01 100644 --- a/src/USER-EFF/atom_vec_electron.cpp +++ b/src/USER-EFF/atom_vec_electron.cpp @@ -577,6 +577,7 @@ int AtomVecElectron::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = q[i]; buf[m++] = spin[i]; @@ -658,6 +659,7 @@ int AtomVecElectron::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/USER-SPH/atom_vec_meso.cpp b/src/USER-SPH/atom_vec_meso.cpp index 3b7fdd7330..3c8c486460 100644 --- a/src/USER-SPH/atom_vec_meso.cpp +++ b/src/USER-SPH/atom_vec_meso.cpp @@ -596,6 +596,7 @@ int AtomVecMeso::pack_exchange(int i, double *buf) { buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = rho[i]; buf[m++] = e[i]; @@ -680,6 +681,7 @@ int AtomVecMeso::pack_restart(int i, double *buf) { buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_atomic.cpp b/src/atom_vec_atomic.cpp index 8c70f3bb53..cd5722caf0 100644 --- a/src/atom_vec_atomic.cpp +++ b/src/atom_vec_atomic.cpp @@ -437,6 +437,7 @@ int AtomVecAtomic::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; if (atom->nextra_grow) @@ -510,6 +511,7 @@ int AtomVecAtomic::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_body.cpp b/src/atom_vec_body.cpp index d435893ead..e92eda1e6e 100644 --- a/src/atom_vec_body.cpp +++ b/src/atom_vec_body.cpp @@ -956,6 +956,7 @@ int AtomVecBody::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = rmass[i]; @@ -1099,6 +1100,7 @@ int AtomVecBody::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_charge.cpp b/src/atom_vec_charge.cpp index 59a05b9687..78f21761b5 100644 --- a/src/atom_vec_charge.cpp +++ b/src/atom_vec_charge.cpp @@ -478,6 +478,7 @@ int AtomVecCharge::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = q[i]; @@ -555,6 +556,7 @@ int AtomVecCharge::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_ellipsoid.cpp b/src/atom_vec_ellipsoid.cpp index 37d6b306d6..8c98104b12 100755 --- a/src/atom_vec_ellipsoid.cpp +++ b/src/atom_vec_ellipsoid.cpp @@ -874,6 +874,7 @@ int AtomVecEllipsoid::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = rmass[i]; @@ -992,6 +993,7 @@ int AtomVecEllipsoid::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_line.cpp b/src/atom_vec_line.cpp index cb857b9966..d098fa3252 100644 --- a/src/atom_vec_line.cpp +++ b/src/atom_vec_line.cpp @@ -753,6 +753,7 @@ int AtomVecLine::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = molecule[i]; @@ -859,6 +860,7 @@ int AtomVecLine::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_sphere.cpp b/src/atom_vec_sphere.cpp index 32aeb5139e..3dcd527f20 100644 --- a/src/atom_vec_sphere.cpp +++ b/src/atom_vec_sphere.cpp @@ -760,6 +760,7 @@ int AtomVecSphere::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = radius[i]; @@ -845,6 +846,7 @@ int AtomVecSphere::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; diff --git a/src/atom_vec_tri.cpp b/src/atom_vec_tri.cpp index 50de1c28cd..53e6c258aa 100644 --- a/src/atom_vec_tri.cpp +++ b/src/atom_vec_tri.cpp @@ -1028,6 +1028,7 @@ int AtomVecTri::pack_exchange(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = molecule[i]; @@ -1172,6 +1173,7 @@ int AtomVecTri::pack_restart(int i, double *buf) buf[m++] = tag[i]; buf[m++] = type[i]; buf[m++] = mask[i]; + buf[m] = 0.0; // for valgrind *((tagint *) &buf[m++]) = image[i]; buf[m++] = v[i][0]; buf[m++] = v[i][1]; From 71a19e3883cd5b766860a8d61a8bb3dd060ad42d Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 21:44:07 +0000 Subject: [PATCH 14/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10114 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pair_lj_long_tip4p_long.cpp | 64 +++++++++++++---- src/KSPACE/pppm_disp.cpp | 16 +++-- src/USER-OMP/pair_lj_long_tip4p_long_omp.cpp | 75 ++++++++++++++++---- 3 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/KSPACE/pair_lj_long_tip4p_long.cpp b/src/KSPACE/pair_lj_long_tip4p_long.cpp index f2825c32b5..31623fc732 100755 --- a/src/KSPACE/pair_lj_long_tip4p_long.cpp +++ b/src/KSPACE/pair_lj_long_tip4p_long.cpp @@ -484,6 +484,7 @@ void PairLJLongTIP4PLong::compute_inner() int nlocal = atom->nlocal; int nall = nlocal + atom->nghost; + // atom->nmax > nmax will occur during setup if (atom->nmax > nmax) { nmax = atom->nmax; memory->destroy(hneigh); @@ -999,6 +1000,19 @@ void PairLJLongTIP4PLong::compute_outer(int eflag, int vflag) // initialize hneigh[2] to 0 every step int nlocal = atom->nlocal; + int nall = nlocal + atom->nghost; + + if (atom->nmax > nmax) { + nmax = atom->nmax; + memory->destroy(hneigh); + memory->create(hneigh,nmax,3,"pair:hneigh"); + memory->destroy(newsite); + memory->create(newsite,nmax,3,"pair:newsite"); + } + if (neighbor->ago == 0) { + for (i = 0; i < nall; i++) hneigh[i][0] = -1; + for (i = 0; i < nall; i++) hneigh[i][2] = 0; + } double **f = atom->f; double **x = atom->x; @@ -1236,7 +1250,6 @@ void PairLJLongTIP4PLong::compute_outer(int eflag, int vflag) cforce = forcecoul * r2inv; fvirial = (forcecoul + respa_coul) * r2inv; - double fvcf = fvirial/cforce; // if i,j are not O atoms, force is applied directly // if i or j are O atoms, force is on fictitious atom & partitioned @@ -1291,15 +1304,27 @@ void PairLJLongTIP4PLong::compute_outer(int eflag, int vflag) f[iH2][2] += fH[2]; if (vflag) { + fd[0] = delx*fvirial; + fd[1] = dely*fvirial; + fd[2] = delz*fvirial; + + fO[0] = fd[0]*(1 - alpha); + fO[1] = fd[1]*(1 - alpha); + fO[2] = fd[2]*(1 - alpha); + + fH[0] = 0.5 * alpha * fd[0]; + fH[1] = 0.5 * alpha * fd[1]; + fH[2] = 0.5 * alpha * fd[2]; + domain->closest_image(x[i],x[iH1],xH1); domain->closest_image(x[i],x[iH2],xH2); - v[0] = (x[i][0]*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0])*fvcf; - v[1] = (x[i][1]*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1])*fvcf; - v[2] = (x[i][2]*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2])*fvcf; - v[3] = (x[i][0]*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1])*fvcf; - v[4] = (x[i][0]*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2])*fvcf; - v[5] = (x[i][1]*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2])*fvcf; + v[0] = x[i][0]*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0]; + v[1] = x[i][1]*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1]; + v[2] = x[i][2]*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2]; + v[3] = x[i][0]*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1]; + v[4] = x[i][0]*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2]; + v[5] = x[i][1]*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2]; } vlist[n++] = i; vlist[n++] = iH1; @@ -1349,15 +1374,28 @@ void PairLJLongTIP4PLong::compute_outer(int eflag, int vflag) f[jH2][2] += fH[2]; if (vflag) { + + fd[0] = -delx*fvirial; + fd[1] = -dely*fvirial; + fd[2] = -delz*fvirial; + + fO[0] = fd[0]*(1 - alpha); + fO[1] = fd[1]*(1 - alpha); + fO[2] = fd[2]*(1 - alpha); + + fH[0] = 0.5 * alpha * fd[0]; + fH[1] = 0.5 * alpha * fd[1]; + fH[2] = 0.5 * alpha * fd[2]; + domain->closest_image(x[j],x[jH1],xH1); domain->closest_image(x[j],x[jH2],xH2); - v[0] += (x[j][0]*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0])*fvcf; - v[1] += (x[j][1]*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1])*fvcf; - v[2] += (x[j][2]*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2])*fvcf; - v[3] += (x[j][0]*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1])*fvcf; - v[4] += (x[j][0]*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2])*fvcf; - v[5] += (x[j][1]*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2])*fvcf; + v[0] += x[j][0]*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0]; + v[1] += x[j][1]*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1]; + v[2] += x[j][2]*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2]; + v[3] += x[j][0]*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1]; + v[4] += x[j][0]*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2]; + v[5] += x[j][1]*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2]; } vlist[n++] = j; vlist[n++] = jH1; diff --git a/src/KSPACE/pppm_disp.cpp b/src/KSPACE/pppm_disp.cpp index 6d631ba891..13ee4679c2 100755 --- a/src/KSPACE/pppm_disp.cpp +++ b/src/KSPACE/pppm_disp.cpp @@ -70,7 +70,7 @@ PPPMDisp::PPPMDisp(LAMMPS *lmp, int narg, char **arg) : KSpace(lmp, narg, arg) triclinic_support = 0; pppmflag = dispersionflag = 1; - accuracy_relative = atof(arg[0]); + accuracy_relative = fabs(force->numeric(FLERR,arg[0])); nfactors = 3; factors = new int[nfactors]; @@ -1137,7 +1137,7 @@ void PPPMDisp::compute(int eflag, int vflag) if (slabflag) slabcorr(eflag); if (function[0]) energy += energy_1; - if (function[1]) energy += energy_6; + if (function[1] + function[2]) energy += energy_6; // convert atoms back from lamda to box coords @@ -2838,12 +2838,15 @@ void PPPMDisp::calc_csum() MPI_Allreduce(neach,neach_all,ntypes+1,MPI_INT,MPI_SUM,world); // copmute csumij and csumi - + double d1, d2; if (function[1]){ for (i=1; i<=ntypes; i++) { for (j=1; j<=ntypes; j++) { csumi[i] += neach_all[j]*B[i]*B[j]; - csumij += neach_all[i]*neach_all[j]*B[i]*B[j]; + d1 = neach_all[i]*B[i]; + d2 = neach_all[j]*B[j]; + csumij += d1*d2; + //csumij += neach_all[i]*neach_all[j]*B[i]*B[j]; } } } else { @@ -2851,7 +2854,10 @@ void PPPMDisp::calc_csum() for (j=1; j<=ntypes; j++) { for (k=0; k<=6; k++) { csumi[i] += neach_all[j]*B[7*i + k]*B[7*(j+1)-k-1]; - csumij += neach_all[i]*neach_all[j]*B[7*i + k]*B[7*(j+1)-k-1]; + d1 = neach_all[i]*B[7*i + k]; + d2 = neach_all[j]*B[7*(j+1)-k-1]; + csumij += d1*d2; + //csumij += neach_all[i]*neach_all[j]*B[7*i + k]*B[7*(j+1)-k-1]; } } } diff --git a/src/USER-OMP/pair_lj_long_tip4p_long_omp.cpp b/src/USER-OMP/pair_lj_long_tip4p_long_omp.cpp index a86c077fc7..2c34e54c4d 100644 --- a/src/USER-OMP/pair_lj_long_tip4p_long_omp.cpp +++ b/src/USER-OMP/pair_lj_long_tip4p_long_omp.cpp @@ -425,6 +425,30 @@ void PairLJLongTIP4PLongOMP::compute_outer(int eflag, int vflag) const int order6 = ewald_order&(1<<6); const int nall = atom->nlocal + atom->nghost; + + // reallocate hneigh_thr & newsite_thr if necessary + // initialize hneigh_thr[0] to -1 on steps when reneighboring occured + // initialize hneigh_thr[2] to 0 every step + + if (atom->nmax > nmax) { + nmax = atom->nmax; + memory->destroy(hneigh_thr); + memory->create(hneigh_thr,nmax,"pair:hneigh_thr"); + memory->destroy(newsite_thr); + memory->create(newsite_thr,nmax,"pair:newsite_thr"); + } + + int i; + // tag entire list as completely invalid after a neighbor + // list update, since that can change the order of atoms. + if (neighbor->ago == 0) { + for (i = 0; i < nall; i++) hneigh_thr[i].a = -1; + // indicate that the coordinates for the M point need to + // be updated. this needs to be done only if neighbor list + // has been updated in compute_outer + for (i = 0; i < nall; i++) hneigh_thr[i].t = 0; + } + const int nthreads = comm->nthreads; const int inum = listouter->inum; @@ -1798,7 +1822,6 @@ void PairLJLongTIP4PLongOMP::eval_outer(int iifrom, int iito, ThrData * const th cforce = forcecoul * r2inv; fvirial = (forcecoul + respa_coul) * r2inv; - double fvcf = fvirial/cforce; // if i,j are not O atoms, force is applied directly // if i or j are O atoms, force is on fictitious atom & partitioned @@ -1853,15 +1876,28 @@ void PairLJLongTIP4PLongOMP::eval_outer(int iifrom, int iito, ThrData * const th f[iH2][2] += fH[2]; if (EVFLAG) { + + fd[0] = delx*fvirial; + fd[1] = dely*fvirial; + fd[2] = delz*fvirial; + + fO[0] = fd[0]*(1 - alpha); + fO[1] = fd[1]*(1 - alpha); + fO[2] = fd[2]*(1 - alpha); + + fH[0] = 0.5 * alpha * fd[0]; + fH[1] = 0.5 * alpha * fd[1]; + fH[2] = 0.5 * alpha * fd[2]; + domain->closest_image(&x[i].x,&x[iH1].x,xH1); domain->closest_image(&x[i].x,&x[iH2].x,xH2); - v[0] = (x[i].x*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0])*fvcf; - v[1] = (x[i].y*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1])*fvcf; - v[2] = (x[i].z*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2])*fvcf; - v[3] = (x[i].x*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1])*fvcf; - v[4] = (x[i].x*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2])*fvcf; - v[5] = (x[i].y*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2])*fvcf; + v[0] = x[i].x*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0]; + v[1] = x[i].y*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1]; + v[2] = x[i].z*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2]; + v[3] = x[i].x*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1]; + v[4] = x[i].x*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2]; + v[5] = x[i].y*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2]; } vlist[n++] = i; vlist[n++] = iH1; @@ -1911,15 +1947,28 @@ void PairLJLongTIP4PLongOMP::eval_outer(int iifrom, int iito, ThrData * const th f[jH2][2] += fH[2]; if (EVFLAG) { + + fd[0] = -delx*fvirial; + fd[1] = -dely*fvirial; + fd[2] = -delz*fvirial; + + fO[0] = fd[0]*(1 - alpha); + fO[1] = fd[1]*(1 - alpha); + fO[2] = fd[2]*(1 - alpha); + + fH[0] = 0.5 * alpha * fd[0]; + fH[1] = 0.5 * alpha * fd[1]; + fH[2] = 0.5 * alpha * fd[2]; + domain->closest_image(&x[j].x,&x[jH1].x,xH1); domain->closest_image(&x[j].x,&x[jH2].x,xH2); - v[0] += (x[j].x*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0])*fvcf; - v[1] += (x[j].y*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1])*fvcf; - v[2] += (x[j].z*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2])*fvcf; - v[3] += (x[j].x*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1])*fvcf; - v[4] += (x[j].x*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2])*fvcf; - v[5] += (x[j].y*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2])*fvcf; + v[0] += x[j].x*fO[0] + xH1[0]*fH[0] + xH2[0]*fH[0]; + v[1] += x[j].y*fO[1] + xH1[1]*fH[1] + xH2[1]*fH[1]; + v[2] += x[j].z*fO[2] + xH1[2]*fH[2] + xH2[2]*fH[2]; + v[3] += x[j].x*fO[1] + xH1[0]*fH[1] + xH2[0]*fH[1]; + v[4] += x[j].x*fO[2] + xH1[0]*fH[2] + xH2[0]*fH[2]; + v[5] += x[j].y*fO[2] + xH1[1]*fH[2] + xH2[1]*fH[2]; } vlist[n++] = j; vlist[n++] = jH1; From 5d0be9c5ffbe9358afae3682148479b5ad2955ec Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:35:39 +0000 Subject: [PATCH 15/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10115 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/image.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/image.cpp b/src/image.cpp index 5efe67b9f8..2302140e49 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -113,10 +113,7 @@ Image::Image(LAMMPS *lmp) : Pointers(lmp) backLightColor[1] = 0.9; backLightColor[2] = 0.9; - // RNG for SSAO depth shading - - if (ssao) random = new RanMars(lmp,seed+me); - else random = NULL; + random = NULL; } /* ---------------------------------------------------------------------- */ @@ -135,7 +132,7 @@ Image::~Image() memory->destroy(surfacecopy); memory->destroy(rgbcopy); - delete random; + if (random) delete random; } /* ---------------------------------------------------------------------- @@ -251,6 +248,7 @@ void Image::view_params(double boxxlo, double boxxhi, double boxylo, // adjust strength of the SSAO if (ssao) { + if (!random) random = new RanMars(lmp,seed+me); SSAORadius = maxdel * 0.05 * ssaoint; SSAOSamples = static_cast (8.0 + 32.0*ssaoint); SSAOJitter = MY_PI / 12; From 76c54d6e3a9640fb1c08035098358e013c002110 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:36:23 +0000 Subject: [PATCH 16/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10116 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/image.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/image.cpp b/src/image.cpp index 2302140e49..ceb5e62bf9 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -132,7 +132,7 @@ Image::~Image() memory->destroy(surfacecopy); memory->destroy(rgbcopy); - if (random) delete random; + delete random; } /* ---------------------------------------------------------------------- From 38112d00635b451edcccf228f686c800d17a81ea Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:45:24 +0000 Subject: [PATCH 17/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10117 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/PDF/colvars-refman-lammps.pdf | Bin 0 -> 600084 bytes doc/fix_colvars.html | 54 +++++++++++++++++----------- doc/fix_colvars.txt | 53 ++++++++++++++++----------- doc/group2ndx.html | 58 ++++++++++++++++++++++++++++++ doc/group2ndx.txt | 51 ++++++++++++++++++++++++++ 5 files changed, 176 insertions(+), 40 deletions(-) create mode 100644 doc/PDF/colvars-refman-lammps.pdf create mode 100644 doc/group2ndx.html create mode 100644 doc/group2ndx.txt diff --git a/doc/PDF/colvars-refman-lammps.pdf b/doc/PDF/colvars-refman-lammps.pdf new file mode 100644 index 0000000000000000000000000000000000000000..133ef16f79747c47dc8239bc3510c3b06e6553df GIT binary patch literal 600084 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
_Pyu7*rTW!N) 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

%w(3dm6a8F72lU)@mCTJD(@d4!ZP3ps1a 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

B&sOu<6~uJE-zy@{-2wwRoJQf9{Q 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

)}M*BFTb6n-9E{ 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

    JN=YJF!X$UBBagZXV%MftHw(_ z#juo}rip9dw4rrgqjm3^w^xckgQbh4Xp(5?10o2`^j50WY4nrnHiVvHP~I$}Lg>!0 z>sm43SIHFNNrm#TJzUV&M?3BiLyPtyHp2`t^nN6(A*LB<{`)>D)O8Y$mj!?IG9#^u z|Ab+u{mRzWk$aU&{}W^(A}u#P6m5}_?I_f(w<1GYUZrYwRz&}yZtnt)Y%ISO(8vlv zro`I6Q3}%2SzEHbnaIiuRyJX_fp$PGhRw^TaBTW0C$>s{5@OrOE4lb-O;Q|tYgsxr zHqWl?g2Sh$XnS>D zMmJjLZ)pH)a43GG_(txOvt9W%a#Pyb$aFB%(QyU2|EU@;bwX5bZ)xxr<>heEl~!T5 zjD^GS*0Z0sjLD?{|Anh`Fgta4dz24r9g@K)rPPqw4IRWWd*BZ<^5s|8rG2|rO?o>} z{9I`si0gg1&^)41p}OX3Nc5@TA%!{|#Zq+q7PjP7DkAt7mteyP6#bai(Ad7;#Cpnb zg!n%{mb0qQEIXDM0>QSt$=n<$$ymWF$u=$-V&XZ+av<{a#m3I4uHxoaw?b$ZF6&<& zIaV^3MjSkN?1-~U*5aisl}uM_&sogM%gE-8HzDPC2`hTPj9~qp84m2#?woaN8`g}1 zHoZjwnXRu5PFO4L7;ZL#KF=;Mms}Pa1nD`QO_0F)9Fr2u|Fy_tO*o4Cfiq=+i>|0smYm!RLle{+zMcbXra}6C6 zQUX`xF}!ZD)+w*UYZAPqwlbP+Psu-a zl41${aj)oFkj8Dk+qWFBc|<`)A>S^kf|uk#tRaM16gRYrR;;_zpo;|@F^F8(kB-P2 zX)mU@Jx^=KiS%LmA0m_=L^y8hxaGGu%Pa;356`*~Tw)k#x|REBhGrkwcf zM#B#hF1((VYs^* zu>bo`!Sg0?XU`QWCV(WNh6x><%QXPIQ+!7}wyu4Occ6!Srlpcy-^-AqeHhAl_$OEc zm>}K4UpCpW|A(}DijFl-v_&6t#a^+K72CFLCo8sX+qP}nwr$(iO`pAQpYCyYk8{Sj zZ-tktdiuWqujZVEtEmPu@=W_?ewP`OGl=C@+QJ5Po)!{W$ae02t5*og-X;+z}M|BUbK`c+uhG>B}%Sk+|eRq-2~j-dw90OOtH&)=QV)6|pK&?one zSXTyxpXv(#f+(=gM-^FYksV~S>GlE2m-QJu{?9 zBj|Bhkw{}~wcpj+!G!Mh5jD2I#x+r6wz$ul2;GqDdKWwE-Na_dvPHU5tX~2wDlY81 zYg7Xa*591UtCM0@{F9l3oT1=~{ah@&c*GbOFU&V~UTqI$8O0*#3a`9OZ#+cdJGb~z z{`e&-O8QR=??%S9X=5zvtMt4c#Pr*3khINvx%tA2IY`e?FF_YIQP7x)z2wbz3K{20 zTrFM*D_3u6)*Do~9`Ox>*9>s434?drr+ zn-=9EP?@6@(GF`;2%}x41`eNT+0Ri`oBRr6X0?76iM1;cPeu@&M+~sLMbyp>rl&SO zbtTDseBP{ElH!olbu|@-S`t$RMJ7XIy6k+u&NcogvMb)(T=NP zTxgCCwsyo}w(uHv7u78$heJ)o8tKOVW%8*RSQ;1lW_Px~1&|DwcDW@d;Y~vLb)Iph zRie1K7hxJ)13G74-LpUW;Yj8Va!9J=No161?S7Io$I(4^OZL(qD}?7P2IuT@Jh(Oh z$C5jC;!C&`)!IC`%hDDc-u$K+C&eZ7gwG~J4%gyf*=ZfO^4mRnc|5ORpEObj?^D1E zkJIBC;#4Y2SVyH5vT|h4%iqL>MH%zTOK5x8INV%)hQNqTg*KC?I#3#-aD<)*f8&Il zoBP)5FbLnlP5AZJjP$VHhXX`q8)=l4fksU^9i)-NWGL4cC*6E+0lE5($xtIqAq5c{ z2hC;4>Le6?eKcdMw?fD|X0s5{sf?_q17Gv<;pMT9gQ9c4rFr)Y^A9|Nb)rDCM6uW* z<1#)hRx?rz+~e3{F&VYJzxz@Bw++wO)E^WVTAI2-<-}vwRD%kYj8)eIVZL%Ut1trrtitL>6=s4BxOY< zfAK}M*78dV(>1S3l-mwv!loM7E1S87B1;oBFbJet)Sx#y5>&sW=GgzhK=JZ!oYWrS3v0_t*!S=A^TzV&+wRFQ*A@Adu*UpbCnqB#qncqp2gy-DS zT2{!Sw>g_|E%02%kp*GE6qOk)x)K*`%iz=EbPe(qusoV)lR4c8ygt!|E?5U&i;hU-Js6?pE&xzM@sQHIiYC&PaLhT#Lp+j z_ruXrV*iPw8R&mF`e!HcXGQ*JeUFLue`_=|+fQr9|K@0B)*oQ~CrH!%NyzN(O4MT+td7a)5ZVLQ1K7v{df8O@8!?`_1*s|zdv(s z|F8A_$S))9KOOkr<(G~5|5|<>Kou7>-l;{C$zTwH2TpQC>yO8k3(>OhZ6_D}xt~7uapbsh#>@CretZ>UaelB$t z*#w<84d6x;CqPU=0qb_PjZYpX2&5xOKVX|~0>uD~eI6k?7_BfXKR#Kvci~Qwtx)bp zAqc3ahX+WHRTdCLK{ys1ZVz~v9Vj{JIrPpwCMl`6Crf!BD9sp1$v=n?l4D@w+1%3K`ONAYoJH#@QIR z4}tr}hX>P`Y-9A56q1z2nNNZBAKetCcf+8-2;boLzBvGSym+#j^5_7!0K6X9TA9}SbxHuy-;V9RDe_4+FaTpdCIJ;X zx?NqqV?m#@V|M{R-a)uPgva3lp1ljKVUR)D^|^DtuK{0B?!4zerzyW_PQRZ67dmun zbd0Wap1%8kIr;Z}e6i{lTm<$-1X2SrVBLH>F$I6%7;1$eEsS4wSEiBC8gU};QrCEo zK|Ct@zwu}M${`b3-v0g84xREqONOQIa})68VFB=w67-o2!(bByM+X;1TBOn~2|vO4 z`7Wx|LfChDyaO2;2G}D?v=WUz?=~Tg3=hyJS%Nh1oZJBD2V?^Rg#^?y0}aGV7&E}b z@dpwJf*r3n3JyPoWftRc{sj~53;cVtw;M)31t26Ctw_$B5WN%fC(D5P(v2A6^Of@r zi`IMK80_l$1@tvIFbf@eEBrb4JLqWpUFRASWDnYwDC@Tft9UR^$uDb8*K(KPU~flW zrvQV+4`Mj)9^|^w^9zz*`>eZ&VdxRU!Q)QLW}{@KR&O%NdQC8kB_#yGMN&-W3~=og z;@>cp%a}J)=D~cY=qk;n?)_FCkE~f}4sBiGauJV?7-VO%l%UnQ)Ue{3mqA zoyQp3UVN@f+tddqrbG)FI(r22->e%_1Q<)LZl&CLl3RL!MMgk4w^Y~-0H{y^e4mML z)@t9;odxZIp0hy`m9I~kXri{Vd|qW&0SC8$!Zi3?x$Z6f+tm}LR%+U> zFzX?(lvt_y^%Z$GgHu=&0y|1r99ftjhe)h(Rt-@<-zlGMQn*E`BRSA-|82$$HEMOc zAfu!b(Rq93;#!e8vU}AO8pl$uAqCXh8hWv6=RIByjmzCo1O=|Ln_yM^q>@_FL!K^{ z*yr@tj1R@={aFHuP-Zd5vpYmE*~0W3KxkkIc3h>UU>&*oh2lSlZ4e*d(f$>#dWqEk zIbM++l(Y)0Tews$#BK&SmuFkbS|cdE6nYrb&=0D1)sjMSoU-PsfIC=bIu!OWOFh!3 zEQ4D$r{zbYhnKj~lLVHNlR{yjT^y5q=Eli(-W%s#)Q2I#UultA?0Cqz2$vdysCJGj zVetft@@g_~E$DBKraxUD?73(Uv%5*`Jg0x6l5>c)KV7Fv8WiFfP-8+-Q8#Dhtwa6m zNH5Yps>Vg~q{Wsa3Eo`IC?dS9m>%D7J7kVml_{AiL#J$ydHHg%oNq8){ooMDMFBf; zOdVKNos1FH*xiTqOQ9BOuRYhu|I(i--C+N>fpZ+30cbeSmSVcit)2psXVOl+HP$4K zsuy+`Z32cWt_vSO{on7CPDv652vsc66gR5*n?=j!vLgq2gRW&qYi?4H+y`?AJ;%rO z=F_{*ho`&XJ@Rx$A}3Ea7v<@6IARZ&zk<95lEHN))1<#u=MVnQ>6bb-@`+l~%&6?% zGb$KTa+>^>N(3uwurnb#8HtW^{WyyyWC(ur8@38@^!!W<=?Y;fS8Cg=HS*kA-*i4i zThKC(s{PdiS`-JYRjIIPA+eL*1#%5g3)*J9zeIZJgE?Xmy~W6^=eBB)VVp|RpR@*4 zzvi&*=BoZ`vdCiw?vtN*O;KvhguuY?I%%^VF?$qdfi?#omC{LuG|#AO9GvOR<*qQE zE!l3e$luHcb$T7ib!voHuzZ!igS=sYZUl09}D=u+@5T;f0Fy+uLJL~5pZ zp{?HEsq#0|@-MN#avUyL80rDXU1Rnv>)nj!o5#nX+cYtz(ipi;$H#&F@UuN@*JzQ9 z!ks#ydsKW@qn)K&Z%@P5?2BjeCaUp#^GrfwE?6rGdWRYz(PH)At-sF#|} zNcD{(llJZ5@YL=PQ_bH4kTBKd*|1}`l-&Bl8Pk0qjjZ2MI)29!j9NR?acPQCYyKG<=3e~pgi!UxB8{3n!Qs_Z$ZD)ZA)o65iKufqAOl^ zc(WM??mr#1ZGGEksMB&Ifd2AS?^`M&RsrkHWKW^0C<;f;Kt>nG}DivGKJQE$PaMEeI$FU zA$#r0(uqfD`4bWTp6fx}nN!HskJ`G~-(9r8p$Drinyg_#HpQraLeh=Cxt7Q6fCNJ* zwadqAi6Eg`jz8~j2Af4W7kwYG#*(^|oUfGSt)#}~0MYX9hoOGG+6fXm%S2%Xll_`r z3!_(5qZ2S!68+QY+xQ{5>Hxgpd($;Sj-cIWe?`rcqYgga=={^JQ&#bOyu*vs_kc3L z-`vL|P^qnOCZ)GqI%)IsjA&d+AE!e~bC$S$MO9l1QF{^WLl}da?cCuM8mr_49EW%~ z8J|@58?SJ>iQ{}uH}Ch)A@HWi^~k}_tDY*(vD;(l;h7-H@&Z88>%)#2y0HWftsxj5FzvzTY`~H|KWr`t;7p2qah*dfF;a~`6!-awh^E@M&gVe5I&M(o#E$ywY;ucr zU#pRS&7g)X+E?3%p8cy@vWCIJJ^D9eOK*b0;&hNEP+G-$ZK}C8Uff!Xa>G0#_S7x$-J><0J_9jw zfAvmWzDS+RS5j?}yYIq0>s(*K8XUY)@6JaZzb~iM!1TmPm6qep6lp!C#%90P_aNY4 z>C;7Kl+1$q{ZQVhj_RP{wKQR?zONEzIu>_x+DsMsCbM6Z!6J3yn1D4<(1R>Y?~BO% zBA@@LJt2|X9zjdvEe>}1wcO&Zx{jRZDZP1HA-^US$;0qEIEtYHFYL%NQZy@E5WcMK$e&~-}hpixdYh7(^i3J_Bap*XIso4h-QDD;n}&-LVWxKX*s+q z&FNRh6k$#E%3!gn^}-9>;{qGs5|L8QfCjNT`+_TUfkFQdnGfC0J_P*@+D) zDOEl(udRCA7H!GNsKm(tf-IXcAx>o*lA0aOLl(Yvg)MRXH>35pg|R~5dI`(H7#T@HDA$2-Fp{kHjE`9k@-@$sxbN4?(KO@ zuY$hL$o}f&{K}fpM2jr363RHoE)k7OKr!*1=aH%7uCHzfqc0s7TB+qGTqz22mGU5PptWKb@pYeKtM3_ZA^2IT z@h>hbPVy_Sr`aus{w5VsK2-LIUvzcG$Usyw(Jo1y$BbsohOqwVT_RC0%sKd7L3#vFueQ5?_nM(GjsK zUb=EVRlbF8w<|e}^+Z;j`7lZ{%?}JeE3tf1iiv*vH)Pi@zNet89*2oDGsUODwl$IC2F;2&+*}NYVIHN0phI zQE`;*TZ-`uld|GGIM{8eJp9Fgu$}^aE-JuT@$GYz~)~@tc zQ=@zucK7p8QV?kEcQJ|hoi;uHg+^sq&H1jQ+T0^8bm57D=ZY^}d+w`qFhorczl*d| zR2L&<{2=Vx9hRIqgoFuPEzX_F?x)6G`VDDO^xg7s1a<0!l(TcjqHU#MdXnzk^ zcBLsqJ%XL>Jc)5ha5^^Spu7-jnO{@xZpLHEp;=tzNkpaYr?WHPx$4uN7y7WGM2YC6 z?9rj?ih1@(QebIS_?csB;%uJMhv<4v5kD49SQ_wRN@@g@&!54@TSsuld`iOeMvjXz zEzgCnHKTFe_MkRr_RzS@lzYpbS>S}|`Em)1bg5qh`W_Z7VSVTRNn2is*;VVz+17ve zX~Wd2P=$%Rfw<@zxnyBm0SomLgwd0EP*KZc{M>k2DDI`t`nf@x^!ynde2kg~&BVxo z17ED!Ar~Fbw6buoMhe9I<=bN2q*^j;oX=7=*Je>Xcy$3`^a_)??*D8@uKgG@sG0~j zuu+F=^^?sUv%c3e8C(Zq*v6lH)bYnT@#!}u_rvkKMf8em8CF)OdLa@T4S!W%)$z z%3+V|#x;BQxRw38&7zX#t1VfO%gglTRw6db-yY;z(%|011=TN-%WXRW#FniMHMSYP&eZ*nd_n9|b~3kZjL|y# zNiHy-qEQFqCbx&6n`TM^BN=t`;JlJeN*SPcnqz)fiK1M_rMZuIL2N`0{F!`n{rh|A z@mYDyeQ;bF>UMm_eK|IiYkavkWUO`Gx%x7#~a{Czdk(TL%uDX%fV$`~Oz~%2{v9F_t z2kv{!Z<|E@1fbFzx&Q(yE_VTEdA=*a=!n+x&oC>4hK)x-?d!*UnwUXL29(O$9<;|t z)B`?&>cm(&t_CfVpYtV5qA7=D7VRTaE(VCWp>RD+(*^F|tsF5XaSHYdmoF1;nY(u@ zPK+4Ap@6}H?1?4BH|YqxgKhhCsX@ynD~)=`hdUBgk^;Lp;HKN{jWVhMaB30n1v7A1 zR@T!t3waHQkRXTLM`t?Me`)b2j|A3FJT14Dc3H-5DKCwhmDdA$=w)mL5hj}}mh8b7 z3#@NXZBcFI_`?k18O~ep`w#Q zG*4Q%d0ZuIg)Y@*Xurqov@@tCx;6#!HxVwYTm>R2g>(*-D*kMLa_>}LpwF;1($_${KM`DPF&#*Mgw!}(vXD3L-yZ0ASidk^1%k6l6o?U-1wsGC z9!R8biRE}3VAwRr+uvJ)X?{|#)bk1KJ>3&B%=e1TZlE-jKo=;FKXpRaJhW^X*4GZX z?>9(?DNgygf^sOpi0eY9%!vsF@K5zUR%)!~+21SV&PtSgl_& zt{ya2b@JZOvHsGa22>jgTSmam!;2<2l-?&AK~ z36a7Ev(LV_6`x`xoZ0F8*8xu*nLon*_)~q;&mLS^T3|GF`oRmgNlQSliP+EV4s6xQ za8j?n_iTwKzonTY84XCA(8mKuOtrIZB|rwwL=42OOcW;%o}mVRYkYp(qwK`Xp5rFHlgGUISUJfFTt31#F728jY0C}Z*9Y{j%V=xkPvDCFv~Ib}fum;tk-EzL6AkJL%? zr0;(ucP`P_Qb(hpG8KT^z(|fXDS7sj$I z?|-g>vD_hUZSFe1Y*vp2#kD}C0r8x}SVD}gV?ihdd|gMERP^$D**7XyBR}x5Kij~v zKCNJfyBr+D;3^Ln?9hVro4)T)h7RN!QftX4ymQRkp#u(<$hjS$0aRLz)FHKSXz;GQ z-?8v0D_r^(!>!^53w5aUfZ>Si5+$E244k|Tn0T^08^pvx`I*HmZ9%w*-OeKXLSZ~K zMLxz0EVA;z`H)j3qgVxRRzjM!jWat{kzpIPU*%k;@&4J?{lXKdk&iyZ=4%!1fn3^^a%R?36t48B;tUR&OkwEIlmL4yg)qlgpjRvJdgMe*J-xv*YCzI z>q&Kn>CWeWOd@nXKUOeZOtgW0KsiBRmOueT9ibrthym+T0R!X10|13W`0(}#UR=QG znxLI*I}tbukRgH!^b~#r0}Auwn?y)K`V9dz!rSm}_JhJB0tO5e1`-wU;qjt_#JypL z@QMSA!^@;#=MV#w5bnq#BXFSa9{C5bvF6b^etChkShND-laoVsZSVT{Q6NLs2lfC` zL98L1g_W8>+XDWAZ=xkz`?-2-`HMYk!eYwDM?*sc7(yWq;Oh{xe+1m2Ya+sn8|FvA zmFv_)Gv>oQfbtfc27thE(}NK7Hm>JWBjU~%BMV?))^% z({t$sh)+!hLV~jg>D$U{LF==v5)AMM$_)Ud{|1gHfeP^`NEk9lm%5u3>Py_;w4Qzg zHp0d+uy2+uPv=>|4?njlrRyEy3!{nwYZvtS^ZhTJ|N2iEx8CtC$qPKJ{j*OQ`Dd@L zq~LcG8}FkWk8H2%_XabWKSZ_%N0^sxQ z<9rmTUe5`lC49Pjw_AkLq@*y{)THk{_^vx6EDXZBCs{`mVh0Ts7*Jkb-X|ahdFOkY z@!PCt`@mbn5x<5f`8_(o%*GuA?-31L^8V{+_tluL<*OtJ^Tim{62vGo7ht)&c^iue ztZvW)@-v(1hU4J7`K`O|qk8>&n??TC#`g8{_zn8z`vt#>_NVy{VuIa0hi-0$Ul#=E z+qwkdfriVR?@|}1>ibejK!6re&WEGrqdb;20crx%tQcBgo&Bp($a@+vehU;V_>4fd zM#mbLv%+Ijwq3^L$`4>`P=hr zb6>8kbqUq%0c)Q!$wDc6!)t@M?^d|CsgJHzF7cnO0uK3Klefgn%X0VXZ=VRxLgwvu zXsF8wmfcGm-Bqq_fHc!tA7`<7?bRt^Y*B{wXdW7oxsWRvHhm>M1PuXZkMF} zaDYo2F|sg>^JZU!QUrrX#0Twmr%@NeN*BO(ppbFn=}71E4N&Sdm~HZ;QdUuCaHS?j z_eNrkN^#&8a9}?NLW2e~$(V;{zHZbQ!zJEd9?yd(vaXP0KRqI+#Bc9z)*Fzc7gFor zvpS0lB}WviMf7aR!m#}%kc6CsIcn|Rxe+5Qwr-kd&p8*y#cu~9`#PVNme7-}`fI37 z)5YrXgJPuRf9LjHIxY+E#33(i1*pt1tfH%AdX(!3H`x2d)(_^?3pQGaBk!UmNOFmRFke>Hx426ldOiYR93YD(h#Hy}1J&75uZQ zQn9uhGNyiI`6fZ$WR0y><=6{E+8_Z^7JZFUqTYZ%ZW8VQN1tjT;4zs0mVWkUZfZfbQJvojg2I-3l?X2qwmfu-Pt zG0~fL&BJ;ssqJV>1IPNNDFAkF#MFN5dFWmKi%_j9&hwqU(AsjtRFqf}Kk+$Oqk9)* zT3V|8k;9*sjfZ$$3aRW7W;z|1RR;CdRimRzO1jc*LDp$KmF|5S{Is|6$%}}@YRFxb zX7ZC*E^dt@&nwLkL4eiav$p6KJN)1?DKKi*XLCM>UNkB8l@( z^g@~a6@qw<1!joC==>p9c?SI~&+P%)#OoS76-30op_>&@Mu7r07ut{d9yFGEGXA;V zm0zJ<65Rtf({PLDuElFue)jt=$ycw>M;Qj`>gxFxR0963T+_fUhZs_Lsf45bS~o?M z-U}A1No7$U?t{|y#Kh{^JyNH3sJ7guriAB5O;r{&2@aNCo%c9ba6`c0@8K{obW)X1 zQYx9`&xv`Byr#=g(n>(W$_DrIrZai3uI~&;c&YuvPvbQ>i$2|bc|)?+wY!QAh!ZkX zaz*5t$3AHheLPM0Z|8+cN-|Rjr&ENtHU^1PpfPrgj7{}6!_O^Y zg*=U^9x?=%W`@RMEvl)G^`B{^b&+0+lN!fq+p0*1pu{*|bmUey8m5}$oe{MqL5UAX z4$N8@NQ!X8*(`KImEgxlojs*taRfEku{x9BpQHu05_AHf$HQzFtJP36n{NJ%a-I-PPC3eM8seb4o@JrRz8B76aR4me$o!nSS+kI1yn5w3^*g1UZ|HjZ`FR3lIr zo8cR(ILQZ`F`mstBG6{Cwlg8@WmhW2Pe?^!Hoa7-gxn+hFdX^jaPzYHo;Ix2fWA3T+qlG4o&)_e~ z7DBJf`|k=YYkTKU^0Tke%kqL6V@RQdku6rCe!vqX74rhk*;t~^T)yvpU@@Igj%u(^ zTNswtcl&CMtPD4>L;PU$J(l|wqWvb*36H%&S+KU}Uo37w_lpynt`E(}Dgtvy7ZVt@ z-Ik6cn8SP56$8RU`6p6|lG$Zw9+hf)6i~O{;EVJJd4Ad*t3P6jy>2 zmy6H1M~~mh#uo%73Yu zCfu#chp6}lpChH>J$E3`b*#Ir81XAy>55GkhhKnO7Y4SK+KCx=AH?WE_x+{FsA7maR%E9ZAnj zI-{zAMcI(zOT(d&9`Wpt;46Ku2q{@@DZyDb19R3@Qyh{+gxZ>$P?>FcR3YEww3%OS z>J+{nhKISKK~f(z7I6`U%_v1xgq}RN?S8_3!wOf?poy zxO#O*SBxc^S2O~DwN@+1_;!zjQ;01Ky$c8Eq<CbzsJoL8b6c6aj_Dz!ez=Dlcl zb=ND55NnU0ILvcK4)=rNrQ{Jjq~yYCNqz)X9}3_$-rmc&P2VpT)m1QLo|TkBIog}y zY_gNVe5eNs%*sM&b9s8ywpBu>a~P!=rH{rk?MQvWYB_^1a>oP}`3Y8|m_XVoC#HUz zd^^m4)j3W9f4ZbqUjh9Zoi2ln*cNzvG5$pnf9b%Oxb}9|F~Qn}DZS(&(R7;C7$}+tSkKavo>w9_ z!_DA|>k03rjhu1PE+d zw37|XADrr66+Pn1vztv2)7vsnK0jRcd$)1BIhtr)IVppA$H=;@jWLe_4OeN=StL+M znf~Nn1aX49GtpokFX5qD2x}YKbRFhpqi%_)a;e-d6h;amcKi4*v@?m{R27^)2%)>$ z*Q<1HMPWeq$J3l^L5#A`3Q!If*AP&}&IqsL8!Y5MXDHO$hA4S3TS(dK25Cz1T5_|a z0FNlot-(+ib^DL6?p){vZ_UVVaP3-)t% zp0o;Sd)CnhU{L(LrU%7N*Vckd#rc)h5`7;ShoM436rm2tqdYWhsc)aajVwN^&Cf>x z8%FUVTbbdLmnMZZkN6zLj?o6qvR5j2Aa>-IFF_(}Hm;*nAtLArQ;~I(Bv!!N|C$m} zq_KpL)`&@_Nn^Fl`$U-8iAfq#e2`NXk3x)EX|q}#vOx#sLe^Rl0-4H;-_IqEnG}@n zB~zs+Ocbf_C7jIYrjT>>%kS;zQD9X=k{g-vi!(?Fm!6k`g6|Iz zBiW--8>3zLxT`Z%-If&Zujd=jqbpvdumGk2*o2l;lxlAruhlRyG_nESrMihCszn$k~ z>$W0yawjHM=MzXu5ncy?)aOujTp{(HevuA zP({kw5YVe2B`RH!CcPi@nI($aC3_-Y9k1eS-1?^U3a}1Ww6+vv?W@zK@?}`LOyVut zXhO>jT!2tp;YLqWP@yo{7t=W7nEbTIC;T#CjSkg`(r~0XO$?pAz6zOHTT=FB76!+X z!-q=DXh4#T7rX_$a(AOjN9E0`>oW4@QUX1$gA|2pZYqidq{5W2UN6(3sdqIy;~Ii$ zHS8p3(_Cw-?$$|s#Lp-?!?W!=A$L=I)7O4ECwg$~Hq!5C;4Kw|dJ(p=+(MzyfMD)d zdRWL1@;GhE3Eg-dnm)FMd*4wE z-47EhqPbmxvy*~#u+DW)oQ&NK9g4X%3bb zyPoUL4qMLT3PZc4JMNxJHY*5XA>ec2U0y1G@QMrfM0IJd@4VdS%z(tUYsolZ39HRlTMoZa`AZzI~AeQ}?t>l-pcZ8%y)XW0gw7z3GyIyQTaUSmUs8lIaqtjW9Q&%WE zu&`!tqbS$HfBsjmu%q836r13@T_eP+XXZX+pw$_y`D9m8e8|cdJJ!PyIF-5^F{Mqw z+nvxqN8w@h$IPmXT#_h+o&VLmwWb9(@K}#8E91*%FE#r_m3%Lh>h<)sQnq^Dhe?U3 z)=gQEc1vxUvv3N{ba&9AUh=O`x9CpD`=?M@)-$3)|_Xc0y6SW2Q|2u_&=>0w3Iffgl=Ove3ILo+3uZS`Nojgb(PmU+Cd1RQ*? zpvLg#qj~cqrn;>N!&v@t#Ajvl8cV@a%GvkPIVS25#Kd`bk<8Z5zp~cqV<`!@3wiv= z?~A);k<9L5UPuy_t%8iq$Gwk+UR+nT_ouI|q4M~mqYwAOY{Y*gAYII>{0FWzOOWAgj%@|6+CbM}*{&nWPF zKHgYHuV`QQ*pL`l8S61wj-=R9k<{Fp(A^abvVZ=#nG4K+<(%ya!ty0aZvy zg=w`vNK2t3@QKA3mcoAoXX;4?nyvEX1~5RBbIOCSrs^A*AB!dYd|(1t z7(`qL(oEE{{4q~F1b-N+TqCF5dbHt1!!0|YGbJAJKE!LbRUzBM;IyLY4vFANV6ZMq zPU9e;*KerEMI=4hLD;ceO91i~bZkLEa0p=3aRrmkHaR~qlB!Z*ntJ}TgSI{}F`&wg z!1h_|*y(v3mNAyIH1r*=m8tlZ$L0>V3hH>|!m`rFM6b@5N62fiWvta$ z4D0F6-e-vdev#Xpk-cwKlY`bPtvNj43_2vZ_&QTh5`KExO2za@~@;!-Ch`>lsR&Y7JUl&@t-UtS_Mu!ZEJOLFR`f&Jcd45 zOHG8sP#9iLfZOt@T@*w#!#Wq@hJ7sPCx6~LtS zp}nB$>%w=wfZ{nWR(6rJKR$!}oQ6B2E@Ul<^MgmpZC<-81#m15YR2Wx?Z4Cd1MdGd zvz%wDT>138UupN<P8|R(hJyyG~b$Mxa_k44&RlQ};EjVc0xbsE|23a5c@yk$kY2FmBm2+=m zoGRRlrqg-A-*^P2oyEsF@;*&}ZqYIl=HjC)!^}eZtnP?uehjA;b@gxX*4rOFNDL>@ zUD7(g;}<1pnSPb%vo(|he=aa+60TX6wG3QTw5T#fWWEwJv9rd+kw)HOD;cVwoc#_ zN!Ht4&*JAHKU)FQxU}TgqDEj9xYQAiE=g02-=vM2qXqC6@GxpdcwgXjbNVX(14q)+ z{tLeSpEy!kNPn`b5Kx^beEz zjQ8mS@bUM0@3v41>pFvgz#@I43G`xH_zUWVF^eM0A;AKdxbgyi{Nx>LSi3`5adCNl zqGc{<2sX5^a!mFi0Rb6Z0nBXB(S67B0GuCfgIQS#YHjM+0cX z7ZCEoMF6zB`|89O(&ZQJ&ZZQFWcCpWfzW81cE zTQ|0~d9fdAcWbxy$5eH9&CKuVsnh37fG|1)6MixN8@Pui-3d! z13>=G2Ypaw16r>(SS2ut7J!Nc@?(eWof-IU=@790b^;6d?6xsBhjepn3V0PX_>mVw zp~B6oE$<3>k`Msf82r^2#i0a=X}3SH1PxeU&4=nyZULs8I0dm$K>N}p=%2>hj}W~f zB%IY}yZdqqvT8tvD+F?L1RjWk+SGrg65i|sly<-0e7&|#Gqz$(Ue_e@fkD)Nt3r$p zB(ESsINk$QP=AdH3AKJ3{4yXF43qz@p%F#^1OWB#y2SPH182N(1pj9L`lHAO_R`j` z3;a~d+zX88#dro;P0&A#0w>t+drH`*__~hWfN+9;P{aKwT$B#PbbEa+& z=z}8=9k`{5$C8yKbXZ z@Grj~vx4W7V7{3MN=ydgPhUN{&kwGV924x;;Z?tadLDv}#EB6yX}awd+~Wo8yO#*c zG>ikY5L9TL>npFQ2Oks-6eb|Rbr)*1rYop@XyjRdeevHKR{;PPWg5>m6jso4ej!$wf4v@?juO6m4Um0 zyThgcuGRL9RjwdKo_YM&$eR+`e>=?r_zw&uh+*|@9Rpookm7kzDhNbS=8GNNUlXZ} z{wvGFpZflz_CO}EpBUu+{R(rv--(bn@-IDg3VeHFOny~iJxq&mH{2guvj%P$2pxX= zuzy<>R7m>l@Y%n?P!T^tKM~M|TmpJ|5x;8{{2_miL5n?pTxTiR1{J84&`Im>`W3G-xm>+B(Z?zFkoW{b&SxA4L-@R{!h@6CO{W9=G z%o;WnCMEIAHerxTjTv2s8;GJCa=$t&lSFX1A6iK`mm54rsk8Nm{d*Cd#*~eYMUlGG z7X3Au6z_q?Wn(3W>$kH-yD%WQvfLT(?URx&X#;+HRd3dVB4d`HYG4uhUl zF|eeihNP>?wG|V0m1h*M_n@=RCPEpzp=1D_kJn?hPb-BJ!E~pL1V3Ja#e96%R!j0? zIkn4O&S03b6z1CBFW_y{q4>C5MwCMJxY)e=Vr8-Y@XFN>QsqFTxYAkw!=Qj+a;;+Y zn6ek;wp}bEVWpET>2{1rw*G>dB4K^0qHu_NaNn%Wo`TYMl7Lo2Jo%QBj~vtds(1bx zpynb^p%auJ1d5KGtAV!#Vzx;=&81E99Z)>FW)o3n3~q6tq8iNFMgAw;c(6sk*2 z1Di0GofW5c&O*R5sP1K6CZ}nQGX+85;KQ60>c{BfZQCv^}KasDNB9Yi+2 z0*~On1mSZjcb)QwI#SQ`3`oG|m~9T-_k;xU+G-VkbGwldP@oF8WHS)OjHW{+5alkW zWRk(iqh*p`&{*S0S8GQ$`$v644cag3ICMh(luuTcy>V!H=}PGG8$!+j;AB`iBv_JK zmE@_=oVb+28070RmeHOAZK}{+#E39YD74~y=qFT@W&hq#2_W{TL6-jOUT+!AwFpKE`lk8zI(uO21)}*}jlf_SQ*8m+2&Jf09#@BfRcSC0<>fPeRYO7gf1b@F5ftuap1l z(q`Ds>`Yl#I<=AnF;wkE`8o;mJp)>z*h(4lOQ_H0V*GpR6&@M$Q5wZ8;YueI)A1YS3nX0pF7^zN!|B88-&iZcQ z-xfYLSF59ZT z9c!m)qLIli;f^8%pF29aVu{{g!nsHNLWVKs**#Pk8v3;JNlZZ}g^DMDCuN8FYHC_F zRuXM91+LSHr!gu4wc0?1`$k){qz&Jl=0Rj_D8Z014^)8UXbfBMz6Od!j8ae5iN*mY-*77y5GmZXq zayJknhLM$7wGeBp`xEMRPhMT6>14u!?R^T{OvEXwU$v&2W}{OCbvkQKoh12^0yr`Z zFD8hlp>T;Kqh%1gs~bgmVwy0(>tLtJGtAFEJzcaUD+6aZ#&lEg*I!)hjkD?d&Ark9 zxrW%!vyu#8Kd7$Fi#?ZR$u<8hhiar2nR~+3L_AU5dAuq9AylBN{q=w8I6vx`Fb z4SsjR2CJ0T#VK8|o?;f%5pv@2c>nTd!4Y zN-3tQCL$0HMW$51y=*b^R$JsP&0$lrEN7bIxTeZ>zXk7_>ks#SGXAt54S3zsgYRkn zk)3s)ABF$0%m#wND;5D_+pHK4&v!WTj_IN0Rw7MCH!-~MTzHEFtRsLVbPC0x9{eFA z4L5qE=vu$RkDJ?O>|~U8*lFVA?PhLzk)q1)Le^4f8c#=wW@>KL+S+bSoVNR2JZ+`X zMxqS`K9M3>@-re2nK*7zDF~Vig32q+a&@6w*_`gcY@vl`{61ml5EOf%hlj)WVn}Ol z!%#mfyB0Ogoak62wWIxO5BUp%4v_VaXK{xNj{p8jqKL5RDs|Lj(L3ed;5`DJk6ESh?+k<9v-{J<80ldazVwQBX|yCmj|&=AF6)TL~znS55m^ zEeDez{>=((dZDY*WeJbuwb;0BUBdSBGv>WTjAlEJ0P&EVTmMLT-O)BY&m-UO z_DsRffvp~br~Gz{`tgINEqbx}>KAEagW<=mYaUjHP#A}iPD9vLnH;P6f-_?)bKYck zCCb!<2U9}C*9DV6C=@^pB$&#s-i;wCJ=X4vys)f&MG|7(_1m}cdawAOV2_`A8SB60 zu@OQZ`ofmAQPUgiVd^IB*V3SVM%Q`6`R$L5l{L3P@9VBtBgCRw+`)}dIPdJmTr9^L zT)C09L?U#aU4EvDw@Sn-74y~tU*WS$m6r_26Qtu+tW-L`&&`S4m#%Uy1+i$pEef{) zq7pjpO~x0+cOl5k!$Qju81l4tNP%lCLz@6eN!pTx*#S6(!x?0E@Aljf;bhkPDP2=s zeaPW4%4wq)>&A31Ii~BjCVCOTu=I%49%MfB=b2vnFB!+`wycko!r(k!XC_6D1HKLT z7i{sl0Euh6nrR)V(ZB2mVCb|a0`6(!l_)zZT3xfYA-queYYskM^91q&Q6V9c;uv>( zOwcl|?*^V1;3G3N2oZ2FZnNWZf*(H&#`Mxs0h64J(303{BO|~5ICV0HO9hN zd-8Dmj;8m3dFi5F^HgTiEb1drd^x|jPxcTmCJREr`;-!X%4`S~zltY8#>JUd$)u*P z<7REvY1>IPA1HQcc<+t0WbyTSWQgPP+!rH~z}03FzRe8!39k0?-v$YSNRn$b8M7uS zK*DbRJ&E4{M)&|z8)fRntaik@-q>C#?iMlRFPmo*kQ&G3zkJDs z95lBD!!Iy->rdN#ZZcw$Lrt1%-JHVA*9$ZO_`|s-6My0{zPKz-5twE|qWW7dOW=%~ zH(i85yNOJWgLUlRJmWk!y6sWCoEzZ6@NdX$^| z5ySfxN+qXnX?^eXJawIiKS1{hbFMqb(Ha{?Y)QPuP9?iGmsXg5plXE9-j^8GMcrWT zzL`E8XZmW$4y*VZ^0l`=O%HL}6`yo%r-TNt8T7Bu^d6oC1%k&uOhx%CsNr%-tuwSH zKubG!ADvfAnn=*V@-oS8cxLU`;P;0O-L{7xaTTTAJ?nc`UI<z+v!g1 zr#Xc)n$;)+6xch z8Z5FFxa4MP{j}ELC8oSv2FM%9*|gYm*h^^E=W#uUZH>*iTR$ChURNWONKE#0sICY;44sVdt+!FT2ftISga`NBVXs`zWO@Ub}k2iAn zd-sqKRCd|p%K4aT91TvSW4g7rN?XqwEn@U%x4xC#$O#R)uW3Z|kmqKI@pI4-#h07= zxi&uqP&@KI2sZ;rNvZWUjT2Bkn{;Vl46B%rcOa18WFv>u9+RT`XQoY)4$E3ZeDl)YlNv@vD%+V&>>mRbgk_BiTHe97nreAfTbg#he>$#W#5UZQ2-r zO4~pw(*=Erg&P+;$g9aorJ|U-;%Ec6XvORmlE-b+goak>-(@ej*THKW+YnB|m6(I= zh2}J(h4`qg8SW<+UG?ed8otIYhya;LiE7O>+DV*dA}iCo);)TRc$G&EG6_q(ZN+_~ zvvtM|qx5ZYky^pk0GIR{hp1lWVc11F(qYkCJy(NF=1JID@*_HtqS0xRxI7#9M6a+MVXjjO- zp@A|^&u}$_U&Yta$QZc~3-eyQJmOC^F=!w-rtORU+c)GiEYLW2i}zCYM)?GeK1wUu z&?g0E*g0i63G2UJ!(X~PQI6r&8#F!@hB(h$$tt@BQEq4jFx&W$Nk!}RO&Zyi)<#uD zl-9MB-=8AnNQ>tXkupAws-1ex?j~0J!<`ns!{|bWc*Vshsb~NSX%mmtHyHoj6H%m#6S52D$OE->oTy3j;N)95#8GK9?U&h4;bsWFVMfPq`V^5JziMb&)ec zDA*=)IpURx{A0+4>ak8?gQXYWYV*o%Qliv2nbr!4FlnX@T!uzi3ePRest7MS4|bou zB22^_WEqURacnQ`@+wg$p4A^y#dOgnop|bTN*JkdUvUbWs5B}UqlK~(p2)`z!3E`= zRP+l$zq92B7daO3TW|GiC(4U-AtD}fh!#XAA zYd?-4t77x99pV-w1|fwB9Sd1~mqoNPa!-{d*%Mh2+b~lTBnP^eA;WnI=J>j_IAxBhG;KmTd!) z*~wpq-}vZ7$nQ7M&S!z_n4EOCyXP4)qIY+_(_gRp@~TeXj(Hvi1#mg8v<8#buH5!? z{QMeJZJWplxm?*(_(Uy1JOxc@{N3*=YmT78qHM~#eRTh+peZh& z&!}uaJX&x#{%SFB7>)84CmF5-;@f`U_aGVJ)sY-b_dX&&B9cK|J}_}^lwjLF_>u_Y z4t|z9J@A$?bxnAT9)0yP=7~9Sx0Mg3D9g!jT0CL*brJ};xoX8m)3*LRtyLKs zreLUI@Q7xgrM|T|X0gpf%Z_%a(W@A63CID|?(N-cASY{6mbhVUu^20*Km zmt`7ybs+F^O;d+uYlg&M#yw)oasn#neGkx$mAJJ~cQgEE2HQIL+ukG!#uuN@{l z8+q%N%H2g#|AdVJtJjoaY>>QZfGXK6pXG*JVD$C)h59iZAb7IjV>4 zpQcnEVcW*u(L_RfoaefoyB6H_YdJ(#j~da9s(iuPSkX;l{iK#>)Khac73+s1O|u0D zw;Q>m$@ME~Ax!}FT;7GNwmfl{Tj8@nYCQ;#J@>jD+FiOEg4*dvPQM&T%;GWQ>gGoo zYna3|_{xZ#ZV<*IFWLZWC+r)iz}^_3Ev(&jnpz z&g&YOll)w$@u77g;=$c<*L{STNx|4wV^V}>_1ZO~4oD1{Zhb>PLpqZGwQ5m2hP^%> z%OYUEE`Hiw$hBUx61vUca3%mus%v1K^du# zobArU3NN7MSN4Tcg{)?c8voRcd>r}5YQj2tTdtLK@{)_wlbzprd={dOhN~DCc{GRm zu@x)1b#iY%MBhT;kQdi=)U$!yHK2%0+>RlZ>bkal@!DfzqeS!vhI?#YgLy zPSHD9K%qi4iE@=Iw5VJ`eJsGNI?z{+W0U-gpG_{~__4mt;-KqB1q43YB24&PI^0B% zmMqCqA95GR-(#OB; zcX3Aq$xYEVvo`%b5Vt|TY{sJD85&KG7spMCJ$G4YO;;tn{M4q>5$`z)Bpnj=iLPF# zNV2$krV0=sr!n+)f%Ja7gG2=~Mmb)vnPM?tF`O+2s-JoSx^ll8%^aJ$cxj!78x~R7 z%J|#hM%(7jdyG?}KU{uWKtd9(TY7N6Ohjk~-s*_prvaDovZOe)I7smMj|inc0a|{V z$}#Ay0*rR4nU&d_bRweYsb|2dWQZjC27UD(v-l%v7=Nn zxi}E^mY)=M@fg?Uq52aan(8Sm)X^K|3W{x$b(`*-XN0ZtEn=z(*jf;jm877}_TaW7 zP)#_OgPTA9et=;`Z=V$taT;zvy##QN$)QoLhGsCwF&)JsRS{wN+lUKW|IH5YSqxd6 zS^dV#8E9cL{Jn5i(jkQzz;MH-W1W502vkw$mA4}mSel3>ol^P^2GISsVh3tYqsq8XEvTd8&=( z8Om_4OX_j&q>J3V*2-3c9@r3&3_t(dU@`?$!T9KKV|r0q+Ss@Cd3RJ(x0*e#%;b88 z)C4z)qDa!PkuyU3%7{eEo12p9K_e72o*3DN0(*;0=MRyF4fke(M^Qz7|9WqHE6Lx2 z0f?=X7aUM}mah<&P}44q=oA`O@5sg32fZP2O<}5*zr$VUg1Pqbm-@} zu|2S5LNH{+r7$GCxs8;`3kRR&3({W^l!i*$c_TPJobcS&bHp`7?t2WYSsV$0Z)Hta zKZxApa!g+9@~?l4)zxcm*ZMSmrqPqI=GxN`J3aER(^kuSf`PMLDQY~yPsjmBm*!<| ziv5)_+dA|Kux8$oL0`UkLiJ(x#j1gt7o|~`9>^pghTW|!M@A-FXe*iYRW>zF()U7Y z6=XJf5uWupidRwlA$uQt-ZvP-j7Ol(HnncM&QqhB^AZm>Hf^ZJ9ND3F&bL@wrLP0QH@op9LmvUTOcrOD+af0?y)pEsZ`0_TI)658`)fxV zTs_DVl~13+ry^`nbZ%dHr^}dN!x%pEWVS`$qB}qPf|mDxzbCwwdm#|*DY7a`V>vPq z*xlL3x@;=iolZkpxvaE)KYR#N}l0n?5P~xHGQ^YcURzCYO_8 z{t84r#{>2N$<6o21V|Jnd&I|=z;Jo%@=>ouw+ z>D;O%7FHEiQ;Y?}qxvkAaUY~qZdpCK&ZC|+_&+Bs(n->$f}LJZE``_7g(zJpVoL1u zVohUQ$Sg+;PN^b@@{;?YcO1$-KLu>$6?)S(8~j~4fYo6iyW)xH!d5TS<-c|b#T9)w zZ&EG9EpPNJ6=P{c=rkgeM5WJvS=w3=4O6lo!p0+BUM_|K!ON8E<(tr)9k*3|-vZYZ zF(ld6UcQQdXaEuhd4J~JrRs;CIWV_SV0lLkF#p`hq$jBQGxOAhp&Ll+#T~#^l$#ZL zY0f`kcz?3~+Z6oSI1N!5_A)f<;X2?TnrVSSi_3KyX<>!^L^~=f5aHQc;Mvm9>dsqE zvxxn`=lzHgDtif0iK~1kEeKk)nM55Dre{-xagq10odJ_6jJIP`An4Dbm6T6>lTmUW z-;XDE9s^qd;gg(sojX6$8VUu+zVjoT*1uD-)J1eX;}l*8eAfEdt6!OJ(7{tO4XmrH zZ-SSAauK8LqPjdwbzuT8-DyYtI$Q*pY0Q0KsFkWZ0HrUfaFe4LhGxJ^tB@P_$LAw@Zm7Wy}h>47{p2cWDI3yutu zC3WuJV23SR1=Qe`s>y(9uAG;cb=k~5OI(Uh3VSbzk<)iK7urDcjuT_zlNE*xE*2kS z(`jk}n|(ZkMPf+KmndI}(<7xnfDh10S3MEcccdu02uUVEWG5Rv2-bIUFQGtI_#;%{&Um7zLfa(5ljNerMDSEe_+Vc5iC28dL-G zj>d2=Z+V8A(HTRUs=%XYmPI>ao;VO0E(4*;iMl*ik|CSDXyh9MDInSu!l2ubCRSm}xFz&s>(i%}CJIOO*Cfc9MF@OgAn>3rw~sSb`!Nrje0N7mWx zxn5EA872W~g1T&(p|dlxNA9iL5n{rJb=?=qa*cIoEq&ZvQ5d%zs%oB#L?!DyJzuV4 z;NOXx1=~yP_zhL=J>+56*ONe^W8akX;kuy$4Jw*evji%uV$7}iCzDiI z3tKP6{}{irL0eUQD#YL8VD${`5_@=W!*S^Y3)!w_e#Md`NCR%a{>Lh~)vqm8wo#HFTRrU|!n&s@q1#+A}+O zWq7`+Iig^9UaDg(4PkSI=63_2wMv+SqI;|B0c) zbpyH4_x*D6D*HLcK}{xdP9ODt6^O2A@NCB#ZlR5Zpp{_5rVZG?$9ffxqC`mGVbK|? zM7x@>qY{}AuSzx?$;pXxG4a>6vM0CS!Pr$>Z@tNXNcQeL6|Ir(&P0W=D~hY45bnx( z=*_u0=D-_qOM>jH$3NUPF!>*3fZclT5lDjDhxG|?e4 zfPaoVYl<;c3+zhQOyRq?8W-`x7Akx{y+ z;aai|?n)G-NmDI}&`X&ct-Et^X4~d z^nK_0MmG0AHti(%hVa;F4rf!GY;0nP*-V7!@(Etw_(WT2R4*QPc~^tIb5gUT?Z0%# zMqrpLj^1=Il=wiWOWa#uCmx+2E~6a*;?AUIy2b1+PGLc*myA5JDA!Tx!Ty6y8L&c z!uaPMrcS0N3=U3~wx$eDh9;KAKXwskOIu4DL#O|I+5ZOq{U_%4zk`22DBu4T{A2x( zuEzfW|9%+7|8w@gfq%@bEdQUuzeaGCBx?-z-_Y|!RBVLVB5rOiE^w1TBJgnkfdCRo zhO-ju&TkQtK`G5s=1Ia3cusqqf3LiCvsv@qUv$;rt@!A_8x}4sA0z<}R1!rnVt`Q~ zU?`vxmgLpUfPw)9cA_B0&y?augT^gJA!{I0v*EGmZD@`0Oh1cnyl= z`so4FvR4A(|8Qh60;fP&=1{m+piaOR{)kVAC!sOygcSbxlMpaMFCXgA=#E}s{i;cs z8xs?eu!k4Z;6WQw^-e&Yf1$km=?C#Zo`d~?d#FHJg{I?r${Y=uKprmhuKd6s44R=B z5OG1=9XN$-3F)s8sPO3xpm1+y@&1_X z6o&-zGKH)y!Z9) zgr{quNALTs(X4<`dufktfS_&%icy*gDJu45KN$pjzQfN01wse}1`_h>4vfeFG`KOG z^X>-fL!dsIOE_cy3>pd~pq@t4`xF2{3SMd-x0Fg%f8RRaVV+@r(S&X4To zp_Yi59w7J4Em_(@yodv`{D;`);+yg~*V?`G3d}XYVqi0`r-uVb4xjMG&j^9W{NUwA z*3qxEQh;c5K>X890vrPe^X2*M76uAL(1CLl687aUgB~!jKf%mEOd!#=Um^VNu~PsM zz7i0^1qe_xBj@rp%@Ob&aOID3I>KMbFL7U(g$t3J9;1eN{pcno-^_7u? zqaXH(z>o;2WrX0q$E^9*e}G{5HMSBT>eKl96(C5sjfhiO1fCYH(JG?*@;>3loTQhv zk%-(7wed%@GRs%y=2xsWZ+lODu{Jy{FUZQq7^R zfWoaMualv+d5O3J{Co30sClPQmZsz>u5sRDg@==;3mCXJ(Z#^v`kW{bf@0d;dxu zN$Ug9#xG-C7kx3=CDpJ|f4Lhnq$0;Z{!gA)hz0z(lbg1DH0sUVD4uB)j~dDr zAR~B}asrt4sY>xl&fYGts%ml?6ngc~P;N42H449cECL_SBr7CPn`GNTPv z7(CxyPw9UQM!yH<1jyT6pIL0+R#|FgPe@XG@i2R;6|B;jmSk3`>Dhl75liXVtMN1z zz5mGi&!PuChKf%)RrSvjUOUVKG!Mud_{a%-#JfVR_s{aJLWSYd4`mb0pW~gwiYC$6-=nF*f2{?bY5AwPgk8A-ClQP&0R)BU1{>_V0>W6LmK>&(_Z zmU4^QF++}(!LQnN>TAhWA!}QI13v2b1TUg-yMNXXPbY9#aan(tmvz=nIUE1$gYQwo z9(tB_{;*&aGUh;*MhkEqjV*5bcbM8>^u4fjAbq9|pRSuUalc05EfrxEKwTJbA6zK) z7-FKvYS-9!Yt%Z3td_W+B(atkFW%%~1nf$c>M_Xk3v^ig1SS$cx$#;b7OttoS2{t} zL%&`TvjCE?i;e-&Y3VfV1ah5_#(ZY#KG!`Bf6o)k?ZLEja>IA%eH)eO)6j&~_|;WM zq&n#{_96}ad^4J-nam3Hj{)t?E=~m|gg*dGPIMY}4iV&SHgLgdIoQcBDE*-CZh%vp z`JrN*;x2k9OeT=a!OY$=KvY$(x_9vTS?D53c4!AT*b@^WZ~i&GFjM{yv?f)YdH%>l zTYT|WelB+9Z+c<;L0M@m=raud~XQ!Dv?8 z0pipv@?Rm*Bh%+bsVyCGxgCXsJs~wR?M@39*>OLy(Ca=pgeCs-NVDq4i^OZ9I+*Re z;Yr&8Gz>_Pxd?V4Wcpv(otb#||6th%y`=PbId5B154^nlmS3rl72BBj_~~5pp%3&O zY%yRa)PET%w;;&L*p1s<#$a~nz}gSyl{9Lx(oXswh2OgxyXz&pdGdJ1;hGt~zAw~a zjZb-QmyKOz17*Mtof)jXGOyZrYu~Ld_=-e;CMMmeUgw6=B%v$}X4`gcopd$unHn{iYl8=H+7eJKYa(fC^Jid5^6>?`^%yt!SmH`)Pb%^hc8J4MWCAwF6;U z&m}XR^Wb{xT}1p*ppZy)!mU6nG$stYeJ&kR2oknyUAgM&IbvdtaX+oat7`d@Zr023 z;SoiZS~p2wFsqvt4GHkOFpEs*0Zjv`>Pu%8W(?-5trfJU@B?+5%FtJ|}G3x<6&=CX$P=Y+>nywuk?H_(^g9C2mhk5s>Ty zz^G4Y!*+7QH-^BYXjhg3*zBC?T$LjR?kHCWsA|#8s~;36EhB>-gZxw4JeF~jm)s&F z)Cz-jUV+>1VRh?bCZJ7Gv6az4A9U@FhdGRWo%>dzgK)4q)i_#%cjpYm;HA$>Qi2w1 z#FtsVWQ(o2w5kv&nA0QL7#J1oOVqqgR8rIwoghU<(=NrMo%fzCF4|6DoRf5pPvRZ_ zn3B+uaWu(T#~rLl^X?6w>sVe*{EK>Cxtd%JXTPH;OzoY^cSoAZMj1^cvby7L+O5{1 z^tuTIqvFOoT_+gL>zMSy;FR$4cEf6Omr>uS0+0K<6FH821f8-fNyIAnbL z$5Tn4@drLkYxjw+wTit}+=)Xc(PR|J+21}6!0D(9)gz!G3Oi>qenQ|!3?h-pl>#TfYmEBpLC@Cn=*SFhmSUo@;$$1eId;;c?M>tb+ozRF>CE`b(#i6kq$ zh{aAE>c1U2hYk-S2RoR99vzAu3R(WJTGSOldD7)IH}w{7tUKXs@5ey*O{zkm=}vZr z-c7Y(Zwp1}xwj5Ye8f!KjHFaKWSFGt+PaZE#?VaBX$e$pb(M1nMYjjLI`FVJBRvy) zIa!aGrB{=3#mRQ^fSd5#pw^_>bn?K8*b+4v;B03a+a4|F;Vx%;GYnhhZ*J%~5(KP2 z*O++9V|@hR${S-2K6%FMoxq;a&zRH5CP)ZFNU*;Pq4>yRCQD9EQ4T87(`CFYtR?a~ zGPudW2_UF#8z#|h3F!Ax&CEXtnq#!-51(KVM?EU`o2Q?4X=@MA!S-EV#^lvJ;cX2D zSw3nNzpAgLZ7RNwqhAM0ab$u`%vJ;Rs$_!nL^@fTsj7++0A!~WQuFcG5{ywDBr!cl z^l@FQD-3mswhqEx^{5O+H!`<^P2v$bql-(O7+$Y)rRRk{nM_3zDFyd%IS-XAM3_B@@xPw@l*oM#~l-LxB;xpRLE|rNfERw&~_U&0~FU56vo$4;D&s zk&`?HO2w#K)4m~jA~yHTdXKw7qrRjvYJhyk{%Gf^ts_3Q>N2hf3k zHATD$(U+jYE;jlah6zUn=W!iSj3~Q@4{G7JP?0x3hi1q0VTF8G6cASF)OU_=vS=5d zXV-$7BC2zwvLF;Vs9B?u>ugS=V`PEalsc@C-T51gg6t)Y2XcG}RE2U8tr3>lDM-#R zEqQIpenj!@GCf-|_R2y>k_7}Q;~vS_cg_=9?0!~GF95|=zoW!?mF<%yW0pF0Q(W5A zK6n?L?SD zN}@mpE>k&d_gY_2kV^sg-209{Av?9!zb9=q5{?Otu9#*QK;(-ZeP#|zFkj(e(A!-y zA(16cF8*>!lJTvZR#Z|ioM5RE*MbNi{~(cHo0$e$plFu&x6kg=eecBPB$x_!+UKFC z>KI(ZCri*&x6gPya}POJHBYEY0tuY!!i*km@YRo%9EW5JOqkI-+FeyY;z;4k6B<{a zACEPj8YI(enLg3()@C;CbS&WlW)7s@d|_=7hhgaHDVkF6glK) z7+h{xR9*3v$|$hm2bARhK{{2+jJSHGvN{&MfXixv`8_2)~c$hvv&Su`DCG>$_HB2?HI*y#ERN(Uf^aJO8(T3^0NuUgl z-K2BnHd4CFPwN+zG_BF{N7MJ8Waq*NziL+n_I5yP5M)vWK5eg=yBBrD#_GZ z0u56R;AJh=klgxmXq9f~%%Bp5(c2y5eV%5kAc|A_#IBJ1jz^AsV>Vqaq>}2Ev}kY+ z3)+~xIyVra4~?3S)dOskwz_F-_j7%O;Q)ulMIKE!O6j$1AVePa;#_;y^hN^C5BwV`E^ zg-biL!AH~|23neU&Z3?aR!UHnOV6I>teaDe?8r&p;))GQvMCg2a>HSJQUl^OMEo&w ziAN1Rt*3L0QVv^7X!-|tqolpusfF1-gsGTyUE-QiSOc-C`wWvlV~)( z8sH*RtPfU%83oQy!sO=~>RwssA{$85zJ{f-o!MGh!5zL9Xwhkl5pr|*-lwcNFUmWy zQ*foRd_n`9v>lzge>MG;DITHHQck5EzaQm33yG#m^U&R1?~}c;B~g5|kx_-g{APO6 zxEW*}!LKSuFQhgeE0GARCAcgm(EQ^bCN|FMIW{CML=>;KXaumxV3l{evN8dH7v>2M6b!}BxcGq4VFvQhQ$Br;vMNXftI-Z!_x6~oDRh@*ev@FSL|ugfEB5G znza$pbBbRiibsQ#dU*ILphcyJD!pt@-5e>p(>};GNamm*Z&E^kY;K&fqf@Ja|RH`saG;t~m@Nc*iN3=7hl zZqSM*wtYC!`PZ@ySzN%{FnFe3v!{9_>I#7DGq~WVUHeewucp`g^JldkNYCHYvJGA-oLN(q1?o5e1CmaM-i+0)}aY<@##{X7vfT z7OUKTMerTnr%FAkZZaN!TZY{##!7pz%D5J=+W#nQbJCbv7S|gi6=ceMa?DPF;>8hMtet;m-*gS+McFt!)joDJ%7XjafrTPpo?(Ul*;Y0lL#TyVBveyZFn zO4azEVWOjD3{6|55GY89YMr%?|tT|C&MmRX4Ar1F%7|?d)fY;>*nyn!f0}1JoB%t$@Y@Gin~9Xf^y=W zw0c!!MUtE)kYb1nW6RytkJ8gyzBasBVaKBsl%?bA_0G6V5{ihv^Ryuu z$=`D@s8wUOM-h4vJv?n~GZV04bRFVRhV>4aPcrtekz>h~MCn1hf!g=tC#`#b=x`~o zkBp4t;uW7%^|?;~?cbu9p>j6C=QdVh^^sLQGP(!(R{z{N zM%rnrL`9;BQ4Y=Fl_wO}9MhSgLYovkr#2LGZ;Ku#N8TyU0qu%1NI5FZ`l$!kgy*^p zy(;KAnUBWa%%SxEHsD8wGqtMIS+(%FM3uUHd{W63bd-lHsV+ilkfy@=stM1qLeW^H z31ZfnwHMw=*WrDGx02#IAtf1cPxaV#PB^x!RCEQ($IQjIRM46nOLu9Yn~sL&e_Ns+ zB__%sFA!rs7GfX}<5j1|Bgg;Chy*E`Oh9-?($}|+Y8qkvG=WjralCa`MHc<~^R4{l zym0FZs3x9(IuBxo7@Qd=%>k`0_Ky$ATf@&x-@xu+NZ#M)%8~eK4OkZ*bi)ed57yLC z#L2PzDbcSX4v!j`6G-IvGkc;HN}N$I(SYgwR6>IelS=?R3C}X>g~2~5)vdP$l7Ebp z1371fF;UElQ?Hmg8Vz?mXmN!1F9ggOALRTw=tL;o#5OMxcFXqPhUZ=rNa86*7O*gb zFu@Fb0=?tYVQqX<>1dbi!vU6EbW-@37K@K#Ba2pC5L?h@oCdd6bPd&zwob>G&qtSA zksC)#q@`X1B>kJ*vu=&L_# zg2Xdl>Vb{@0m)7X(4jG6!Z79zs=6^;hVbf?c1^S9LEH?&orVnUvAYL4MHwdC%XYniEeIAqq3Pn~^?PhH^;GWo80@3cn?1%I6S9bTrQhW`)F-YH18 zKuy{#dzWq7ws*O9*|u%lw(VWEZQHhOPo00F`Ems>IL zM{W+f4coH%r_PN+7UH`U6{w`?)?qp?R?9}Jlk)ow$OT9nDB(Q=*zl~M`G^}(l(wWz zv`$ga3xokq;rgesG{ow0KHf!C8UtCQBYYelXAl_}J<&CZ>Kd8qDwZHP{76ppw1EBL zNzHTK*>qpHo!j%p+R~m=C_7oD7Q$c6nkAY%#Y9!2EFz%2O!9prI_wGgJk8_xOBs~S z8#3~=T^`Lp|D(&@DmpYBUD0}t4F~47<_8f3`#PFKyoS3*#A(ktglxTZbG}+nb0R`d z>GL0VH=~n#sD*!LFM>ZyO)A{EW$~iUOZ<2H&vBGjQx@!cP1}X~raT|E6=>kD?ES_c zw0sPpXpIv0bM(K#1O}jN$HNF9gpA8%2^q$JDuMX)w11IOYa*x-2BpR%TWp|N;tdRz z)5buwk&J>v@xF22N}NVm!4gfCUKCjZ-IF~^wBpu_+lKA>Zq$`ffDzRglS6`U$byl`V9HDoinesxK;>o?sFSr? z4nMnYwicGb0UX)jQ^|zM7gmN_zZR&nN(j|%NUTJ;kh03C)$U8&8lc85D6S6VfXrTy z*TKsB0a62B%{0w{_Jr4%YBn06UBQgDMR{@X_#`7 zgdQ8kOv>tHDU&XL=WxYrZ56bBdSH0b<%fgQ4Ipxw6h_v2l2PU;8oFi5R1+2|@0{&~ zBWS{#D=hc4r0yx{%1Lzk#ZJer{$4Smo@-3>_f}0B9%|dF~cxU3e`=HyDfgl zet#jrS>5B@YASoV^12Yg2+mgaL76wnn5U`9$Fl|y8?sdsbk(XM8IOR6l;Ya1r7}1& zBdEA@=MUsFf?W5jO9yq9IEH%x=;Ki-ichMtTU;ZU2eS?VmxeM>{b?|iaYcKHc;aCW z(qi5tZ)Wb%z9dOzK*;;$m-q#ftxs5Wshm~_TjB+svfo4_I`ZYeasc}I9_I>}itwK9 zc!hYl5;~~%d3wqCwr%{C;iT8HN3=T##u=JAgxnXlj_|j#sa4T}`#KgNZc3Qp?15Wh zu|b$U5Gz*?_4Dc8mJlpN$dkLd?^?|yDen9h#8T$C-{w&977AK~a18%~zZ9M#AFN$0 zz*C@2!~1@b@1zbq1J^fS$NU-c5BL(FaEeK>!o%QsMh>H#S==6P6b2FUi(qpOCkCc5 zbTA>U6vf%o_uBYPyx+ANmG(DEQQs9f)>Iba3_$6e15;$Y(Ha`q1BrcQAkT}_Y`1XA zfb>fdr4r68|0(}%l}-P#{v^`H6~}dSn;lOEAvh?x9uJ9TRt~3G=AOIr#9ZEJjF>Vr zZ#vRvm=`-*JRFkR7dJ2i_~~B#+k3|BWEC5UHQXX_l`5^7mrREsd^Xxlt|c zBAjjYRBt)v&l-8g$l~lNCp%KsP<+{Q3;2D;@GVo=^?cNn`!N~zryDdCB}VjSklCv9 z2;w37Xk>3bX{uEp15SK(+;2#;_D0Z85t~r(D4^|3#*LcK@Hz{qXZxz1pP3w;z2~n2 zLAJ;jw*+?SMJvqv6gX`x>WXWW-pR7a?J;vL2SGUEs29Q$-(JxFye$^ea!X%!< zys=g42w39UqGdY8{Cvq{*}t!(jFzcxjZ^1kd`}pIK-xncR)v|^Zvq!HYtLp{CUUot z+nl4$O{0fnh?4G*-Dq5Z$E5blVFGsIdUt^P^%ZtUgY_h&<#~m>egn z3t=J`;Ys=n1xBqQyx?DY$-r@an5@8R~HrVUTxL}Qm{@*shB37TKJuhgkjF9^1FaNWAsKz-Y^?e4aPlj;Y=V!z8k^y2|;N z0Yic_9!9OiMfZH0U;KIQ$A7 zYE!V}YFB>ijr}@BexqiYE-CN27)8r5KuoM~i5P!14mZ6y6vPPfT+3Xj>bfC)wQl4& zJUr|lLg0^crHV+#OtwrU5=~%EDN@g3*=EyW(yU^3uqgF;@#3zbF4+ItYHQFAq1d}O z;LV1;N3&sbOs`6(8bv?b4pls^`W`;9d%@? zAw7T9nCOyQPaWdzRS|ZOw6n~YTrx1&PP6fDKy$bql^30>gew&TR1hMx9}s(BJKT4L zNMai|RTmGe7dXvPb>e|{4*L{(Dq->ZvzX(n4lHc&IhkHf1IB4_Qge`S^63nwfC!K= zsBv9sR4#dS?-eFGD8#G3>p(dfljwCzKh2aOu$B=xT;CNREXJKu5Yus8yKJDzNtv1G zh;gy1d67g8G2LbNwH?_?G`+%0#?(XB`@welPq`8N5_cjP;P35pi|5?<_AEMB)99)T z2It^Y37VwZ&{`0ZnByPpzPnFv_}>9z%s(w_OicJJOlI!6~9I;Ve!ul|8r{X2Zc@-J`tpKDIcY%KqQTQTA@GXA5;?f?GmU*Rhz1~&Hp zHGHKA$*$;yCX-0=`$5F5T!YRC!x+o}qpqX^lAxGz9LIX7)8J@|K5R@motma}0?o>_ zkyKDI6gl5=HlI*VUihKQYkBU?Yx>@6ntM9SW9l{Qp0E4+HLJX7ZcutX-fxCq14N`T zj1z=W_wwM%8U!A~ofE{rDl$EtE3mEyqd77id}LP;Lh1`rcM`CS{HQ)JAAJha56}Y_ z8vhJX7?^Q3Rtg`W4aEPJ4Zn{7m{pN?03g#5zyk!VmC^hMd+hQ1_>j9uwW;>y)B?uG z#KPJuo&y}`j|HABqu&w!{n^WZ@v|KX+Ctz6Aq?7`D9iRJD#wZu0|yJclm}+-1RtG~ z1_lceEbG&Qa^R26I|4uo2|TsHZ=0SL9f`Sf1!q_CX$^t&tJt0&z@Y}86T+bu26qC0 z1HhSm$M_h+(lFqLdBqOX7ufg;4F3$iJ0)W@n;N87VN{V52hvbM4-n)XP%0D@2+In` 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 online -documentation +(US) via a flexible harmonic restraint bias. The colvars library is +hosted at http://colvars.github.io/

    -

    There are example scripts for using this package with LAMMPS in -examples/USER/colvars. +

    This documentation describes only the fix colvars command itself and +LAMMPS specific parts of the code. The full documentation of the +colvars library is available as this supplementary PDF document

    -

    The implementation of the portable collective variable library is also -documented in (Henin) +

    A detailed discussion of the implementation of the portable collective +variable library is in (Fiorin). Additional information can +be found in (Henin). +

    +

    There are some example scripts for using this package with LAMMPS in the +examples/USER/colvars directory.


    The only mandatory argument to the fix is the filename to the colvars -input file that contains all input that is independent from the MD +input file that contains the input that is independent from the MD program in which the colvars library has been integrated.

    The group-ID entry is ignored. The collective variable module will -always apply to the entire system, i.e. use the group all. +always apply to the entire system and there can only be one instance +of the colvars fix at a time. The colvars fix will only communicate +the minimum information necessary and the colvars library supports +multiple, completely independent collective variables, so there is +no restriction to functionaliry by limiting the number of colvars fixes.

    The input keyword allows to specify a state file that would contain -the information required in order to continue a calculation, e.g. -from a restart. Setting it to NULL will start a new colvars run. +the restart information required in order to continue a calculation from +a prerecorded state. Fix colvars records it state in binary restart +files, so when using the read_restart command, +this is usually not needed.

    The output keyword allows to specify the output prefix. All output files generated will use this prefix followed by the ".colvars." and @@ -90,8 +98,10 @@ temperature.

    Restart, fix_modify, output, run start/stop, minimize info:

    -

    No information about this fix is written to binary restart -files. +

    This fix writes the current status of the colvars module into +binary restart files. This is in addition to the text +mode status file that is written by the colvars module itself and the +kind of information in both files is identical.

    The fix_modify energy option is supported by this fix to add the energy change from the biasing force added by the fix @@ -109,10 +119,10 @@ calculated by this fix is "extensive". LAMMPS was built with that package. See the Making LAMMPS section for more info.

    -

    There can only be one colvars fix active at a time. Since the -colvars module itself can handle an arbitrary number of collective -variables and always applies to the entire system, this is not -really a deficit in practice. +

    There can only be one colvars fix active at a time. Since the interface +communicates only the minimum amount of information and colvars module +itself can handle an arbitrary number of collective variables, this is +not a limitation of functionality.

    Related commands:

    @@ -125,9 +135,13 @@ and tstat = NULL.


    + + +

    (Fiorin) Fiorin , Klein, Henin, Mol. Phys., DOI:10.1080/00268976.2013.813594 +

    -

    (Henin) Hénin, Fiorin, Chipot, Klein, J. Chem. Theory Comput., 6, +

    (Henin) Henin, Fiorin, Chipot, Klein, J. Chem. Theory Comput., 6, 35-47 (2010)

    diff --git a/doc/fix_colvars.txt b/doc/fix_colvars.txt index caf04c14a9..7e8f35922d 100644 --- a/doc/fix_colvars.txt +++ b/doc/fix_colvars.txt @@ -36,30 +36,38 @@ module library which allows to calculate potentials of mean force (PMFs) for any set of colvars, using different sampling methods: currently implemented are the Adaptive Biasing Force (ABF) method, metadynamics, Steered Molecular Dynamics (SMD) and Umbrella Sampling -(US) via a flexible harmonic restraint bias. This documentation -describes only the colvars fix itself and LAMMPS specific parts of the -code. The documentation of the colvars implementation itself is -available as part of the "NAMD online -documentation"_http://www.ks.uiuc.edu/Research/namd/2.8/ug/node53.html +(US) via a flexible harmonic restraint bias. The colvars library is +hosted at "http://colvars.github.io/"_http://colvars.github.io/ -There are example scripts for using this package with LAMMPS in -examples/USER/colvars. +This documentation describes only the fix colvars command itself and +LAMMPS specific parts of the code. The full documentation of the +colvars library is available as "this supplementary PDF document"_PDF/colvars-refman-lammps.pdf -The implementation of the portable collective variable library is also -documented in "(Henin)"_#Henin +A detailed discussion of the implementation of the portable collective +variable library is in "(Fiorin)"_#Fiorin. Additional information can +be found in "(Henin)"_#Henin. + +There are some example scripts for using this package with LAMMPS in the +examples/USER/colvars directory. :line The only mandatory argument to the fix is the filename to the colvars -input file that contains all input that is independent from the MD +input file that contains the input that is independent from the MD program in which the colvars library has been integrated. The {group-ID} entry is ignored. The collective variable module will -always apply to the entire system, i.e. use the group {all}. +always apply to the entire system and there can only be one instance +of the colvars fix at a time. The colvars fix will only communicate +the minimum information necessary and the colvars library supports +multiple, completely independent collective variables, so there is +no restriction to functionaliry by limiting the number of colvars fixes. The {input} keyword allows to specify a state file that would contain -the information required in order to continue a calculation, e.g. -from a restart. Setting it to NULL will start a new colvars run. +the restart information required in order to continue a calculation from +a prerecorded state. Fix colvars records it state in "binary restart"_restart.html +files, so when using the "read_restart"_read_restart.html command, +this is usually not needed. The {output} keyword allows to specify the output prefix. All output files generated will use this prefix followed by the ".colvars." and @@ -82,8 +90,10 @@ temperature. [Restart, fix_modify, output, run start/stop, minimize info:] -No information about this fix is written to "binary restart -files"_restart.html. +This fix writes the current status of the colvars module into +"binary restart files"_restart.html. This is in addition to the text +mode status file that is written by the colvars module itself and the +kind of information in both files is identical. The "fix_modify"_fix_modify.html {energy} option is supported by this fix to add the energy change from the biasing force added by the fix @@ -101,10 +111,10 @@ This fix is part of the USER-COLVARS package. It is only enabled if LAMMPS was built with that package. See the "Making LAMMPS"_Section_start.html#start_3 section for more info. -There can only be one colvars fix active at a time. Since the -colvars module itself can handle an arbitrary number of collective -variables and always applies to the entire system, this is not -really a deficit in practice. +There can only be one colvars fix active at a time. Since the interface +communicates only the minimum amount of information and colvars module +itself can handle an arbitrary number of collective variables, this is +not a limitation of functionality. [Related commands:] @@ -117,7 +127,10 @@ and tstat = NULL. :line +:link(Fiorin) +[(Fiorin)] Fiorin , Klein, Henin, Mol. Phys., DOI:10.1080/00268976.2013.813594 + :link(Henin) -[(Henin)] Hénin, Fiorin, Chipot, Klein, J. Chem. Theory Comput., 6, +[(Henin)] Henin, Fiorin, Chipot, Klein, J. Chem. Theory Comput., 6, 35-47 (2010) diff --git a/doc/group2ndx.html b/doc/group2ndx.html new file mode 100644 index 0000000000..3fb0670fcd --- /dev/null +++ b/doc/group2ndx.html @@ -0,0 +1,58 @@ + +
    LAMMPS WWW Site - LAMMPS Documentation - LAMMPS Commands +
    + + + + + + +
    + +

    group2ndx command +

    +

    Syntax: +

    +
    group2ndx file group-ID ... 
    +
    +
    • file = name of index file to write out + +
    • zero or more group IDs may be appended + + +
    +

    Examples: +

    +
    group2ndx allindex.ndx
    +group2ndx someindex.ndx upper lower mobile 
    +
    +

    Description: +

    +

    Write a Gromacs style index file in text format that associates atom IDs +with the corresponding group definitions. This index file can be used +with in combination with Gromacs analysis tools or to import group +definitions into the fix colvars input file. +

    +

    Without specifying any group IDs, all groups will be written to the index +file. When specifying group IDs, only those groups will be written to the +index file. In order to follow the Gromacs conventions, the group all +will be renamed to System in the index file. +

    +
    + +

    Restrictions: +

    +

    This command requires that atoms have atom IDs, since this is the +information that is written to the index file. +

    +

    This fix is part of the USER-COLVARS package. It is only enabled if +LAMMPS was built with that package. See the Making +LAMMPS section for more info. +

    +

    Related commands: +

    +

    group, dump, fix colvars +

    +

    Default: none +

    + diff --git a/doc/group2ndx.txt b/doc/group2ndx.txt new file mode 100644 index 0000000000..d40d4399f9 --- /dev/null +++ b/doc/group2ndx.txt @@ -0,0 +1,51 @@ +"LAMMPS WWW Site"_lws - "LAMMPS Documentation"_ld - "LAMMPS Commands"_lc :c + +:link(lws,http://lammps.sandia.gov) +:link(ld,Manual.html) +:link(lc,Section_commands.html#comm) + +:line + +group2ndx command :h3 + +[Syntax:] + +group2ndx file group-ID ... :pre + +file = name of index file to write out :ulb,l +zero or more group IDs may be appended :l +:ule + +[Examples:] + +group2ndx allindex.ndx +group2ndx someindex.ndx upper lower mobile :pre + +[Description:] + +Write a Gromacs style index file in text format that associates atom IDs +with the corresponding group definitions. This index file can be used +with in combination with Gromacs analysis tools or to import group +definitions into the "fix colvars"_fix_colvars.html input file. + +Without specifying any group IDs, all groups will be written to the index +file. When specifying group IDs, only those groups will be written to the +index file. In order to follow the Gromacs conventions, the group {all} +will be renamed to {System} in the index file. + +:line + +[Restrictions:] + +This command requires that atoms have atom IDs, since this is the +information that is written to the index file. + +This fix is part of the USER-COLVARS package. It is only enabled if +LAMMPS was built with that package. See the "Making +LAMMPS"_Section_start.html#start_3 section for more info. + +[Related commands:] + +"group"_group.html, "dump"_dump.html, "fix colvars"_fix_colvars.html + +[Default:] none From 4da20dce992f7366bb6b0e29b0055f61f2b96720 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:48:27 +0000 Subject: [PATCH 18/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10118 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- lib/colvars/Makefile.g++ | 14 +-- lib/colvars/Makefile.mingw32-cross | 70 ++++++------ lib/colvars/README | 17 ++- lib/colvars/colvar.cpp | 81 ++++++++------ lib/colvars/colvar.h | 7 +- lib/colvars/colvaratoms.cpp | 57 +++++++--- lib/colvars/colvaratoms.h | 25 +++-- lib/colvars/colvarbias.cpp | 136 ++++++++++++++++++++---- lib/colvars/colvarbias.h | 86 +++++++++------ lib/colvars/colvarbias_abf.cpp | 60 ++++++++--- lib/colvars/colvarbias_abf.h | 6 +- lib/colvars/colvarbias_meta.cpp | 72 +++++++------ lib/colvars/colvarbias_meta.h | 14 +-- lib/colvars/colvarcomp.cpp | 4 +- lib/colvars/colvarcomp.h | 153 ++++++++++++++------------- lib/colvars/colvarcomp_angles.cpp | 4 +- lib/colvars/colvarcomp_coordnums.cpp | 67 ++---------- lib/colvars/colvarcomp_distances.cpp | 68 ++++++------ lib/colvars/colvarcomp_protein.cpp | 16 +-- lib/colvars/colvarcomp_rotations.cpp | 4 +- lib/colvars/colvargrid.cpp | 4 +- lib/colvars/colvargrid.h | 54 +++++----- lib/colvars/colvarmodule.cpp | 153 ++++++++++++++++++++------- lib/colvars/colvarmodule.h | 48 ++++++--- lib/colvars/colvarparse.cpp | 34 +++--- lib/colvars/colvarparse.h | 10 +- lib/colvars/colvarproxy.h | 78 ++++++++------ lib/colvars/colvartypes.h | 74 ++++++------- lib/colvars/colvarvalue.cpp | 14 +-- lib/colvars/colvarvalue.h | 24 ++--- 30 files changed, 883 insertions(+), 571 deletions(-) diff --git a/lib/colvars/Makefile.g++ b/lib/colvars/Makefile.g++ index 825e24a862..272d93b801 100644 --- a/lib/colvars/Makefile.g++ +++ b/lib/colvars/Makefile.g++ @@ -1,4 +1,4 @@ -# library build makefile for colvars module +# library build -*- makefile -*- for colvars module # which file will be copied to Makefile.lammps @@ -14,9 +14,9 @@ SHELL = /bin/sh # ------ DEFINITIONS ------ -SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \ - colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \ - colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \ +SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \ + colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \ + colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \ colvargrid.cpp colvarmodule.cpp colvarparse.cpp colvarvalue.cpp LIB = libcolvars.a @@ -25,11 +25,13 @@ EXE = #colvars_standalone # ------ MAKE PROCEDURE ------ -default: $(LIB) $(EXE) +default: $(LIB) $(EXE) Makefile.lammps + +Makefile.lammps: + @cp $(EXTRAMAKE) Makefile.lammps $(LIB): $(OBJ) $(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ) - @cp $(EXTRAMAKE) Makefile.lammps colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB) $(CXX) -o $@ $(CXXFLAGS) $^ diff --git a/lib/colvars/Makefile.mingw32-cross b/lib/colvars/Makefile.mingw32-cross index 42435e9f4c..05bdbf32a5 100644 --- a/lib/colvars/Makefile.mingw32-cross +++ b/lib/colvars/Makefile.mingw32-cross @@ -1,4 +1,4 @@ -# library build makefile for colvars module +# library build -*- makefile -*- for colvars module # which file will be copied to Makefile.lammps @@ -6,35 +6,42 @@ EXTRAMAKE = Makefile.lammps.empty # ------ SETTINGS ------ -CXX = i686-pc-mingw32-g++ +CXX = i686-w64-mingw32-g++ CXXFLAGS = -O2 -march=i686 -mtune=generic -mfpmath=387 -mpc64 \ -fno-rtti -fno-exceptions -finline-functions \ -ffast-math -funroll-loops -fstrict-aliasing \ -Wall -W -Wno-uninitialized -ARCHIVE = i686-pc-mingw32-ar +ARCHIVE = i686-w64-mingw32-ar ARCHFLAG = -rscv SHELL = /bin/sh # ------ DEFINITIONS ------ -SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \ - colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \ - colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \ +SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \ + colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \ + colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \ colvargrid.cpp colvarmodule.cpp colvarparse.cpp colvarvalue.cpp -LIB = libcolvars.a -OBJ = $(SRC:.cpp=.o) +DIR = Obj_mingw32/ +LIB = $(DIR)libcolvars.a +OBJ = $(SRC:%.cpp=$(DIR)%.o) EXE = #colvars_standalone # ------ MAKE PROCEDURE ------ -default: $(LIB) $(EXE) +default: $(DIR) $(LIB) $(EXE) Makefile.lammps -$(LIB): $(OBJ) +$(DIR): + mkdir $(DIR) + +Makefile.lammps: + @cp $(EXTRAMAKE) Makefile.lammps + +$(LIB): $(DIR) $(OBJ) $(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ) @cp $(EXTRAMAKE) Makefile.lammps -colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB) +$(DIR)colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB) $(CXX) -o $@ $(CXXFLAGS) $^ # ------ MAKE FLAGS ------ @@ -46,58 +53,59 @@ colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB) # ------ COMPILE RULES ------ -.cpp.o: - $(CXX) $(CXXFLAGS) -c $< +$(DIR)%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ # ------ DEPENDENCIES ------ # -colvars_main.o: colvars_main.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvars_main.o: colvars_main.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarproxy_standalone.h colvaratoms.h colvarparse.h colvarvalue.h -colvarproxy_standalone.o: colvarproxy_standalone.cpp colvarmodule.h \ +$(DIR)colvarproxy_standalone.o: colvarproxy_standalone.cpp colvarmodule.h \ colvartypes.h colvarproxy.h colvaratoms.h colvarparse.h colvarvalue.h \ colvarproxy_standalone.h -colvaratoms.o: colvaratoms.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvaratoms.o: colvaratoms.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarparse.h colvarvalue.h colvaratoms.h -colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h colvartypes.h \ +$(DIR)colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h colvartypes.h \ colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarbias_abf.h \ colvarbias.h colvargrid.h -colvarbias.o: colvarbias.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvarbias.o: colvarbias.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarvalue.h colvarbias.h colvar.h colvarparse.h -colvarbias_meta.o: colvarbias_meta.cpp colvar.h colvarmodule.h \ +$(DIR)colvarbias_meta.o: colvarbias_meta.cpp colvar.h colvarmodule.h \ colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h \ colvarbias_meta.h colvarbias.h colvargrid.h -colvar.o: colvar.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvar.o: colvar.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h -colvarcomp_angles.o: colvarcomp_angles.cpp colvarmodule.h colvartypes.h \ +$(DIR)colvarcomp_angles.o: colvarcomp_angles.cpp colvarmodule.h colvartypes.h \ colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarcomp.h \ colvaratoms.h -colvarcomp.o: colvarcomp.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvarcomp.o: colvarcomp.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarvalue.h colvar.h colvarparse.h colvarcomp.h colvaratoms.h -colvarcomp_coordnums.o: colvarcomp_coordnums.cpp colvarmodule.h \ +$(DIR)colvarcomp_coordnums.o: colvarcomp_coordnums.cpp colvarmodule.h \ colvartypes.h colvarproxy.h colvarparse.h colvarvalue.h colvaratoms.h \ colvar.h colvarcomp.h -colvarcomp_distances.o: colvarcomp_distances.cpp colvarmodule.h \ +$(DIR)colvarcomp_distances.o: colvarcomp_distances.cpp colvarmodule.h \ colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \ colvarcomp.h colvaratoms.h -colvarcomp_protein.o: colvarcomp_protein.cpp colvarmodule.h colvartypes.h \ +$(DIR)colvarcomp_protein.o: colvarcomp_protein.cpp colvarmodule.h colvartypes.h \ colvarproxy.h colvarvalue.h colvarparse.h colvar.h colvarcomp.h \ colvaratoms.h -colvarcomp_rotations.o: colvarcomp_rotations.cpp colvarmodule.h \ +$(DIR)colvarcomp_rotations.o: colvarcomp_rotations.cpp colvarmodule.h \ colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \ colvarcomp.h colvaratoms.h -colvargrid.o: colvargrid.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvargrid.o: colvargrid.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h \ colvargrid.h -colvarmodule.o: colvarmodule.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvarmodule.o: colvarmodule.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarparse.h colvarvalue.h colvar.h colvarbias.h colvarbias_meta.h \ colvargrid.h colvarbias_abf.h -colvarparse.o: colvarparse.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvarparse.o: colvarparse.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarvalue.h colvarparse.h -colvarvalue.o: colvarvalue.cpp colvarmodule.h colvartypes.h colvarproxy.h \ +$(DIR)colvarvalue.o: colvarvalue.cpp colvarmodule.h colvartypes.h colvarproxy.h \ colvarvalue.h # ------ CLEAN ------ clean: - -rm *.o *~ $(LIB) + -rm $(DIR)*.o *~ $(LIB) + -rmdir $(DIR) diff --git a/lib/colvars/README b/lib/colvars/README index 9c6b1f6c09..d6efc333a5 100644 --- a/lib/colvars/README +++ b/lib/colvars/README @@ -4,9 +4,24 @@ that allows enhanced sampling in molecular dynamics simulations. The module is written to maximize performance, portability, flexibility of usage for the user, and extensibility for the developer. -The following publication describes the principles of +The development of the colvars library is now hosted on github at: +http://colvars.github.io/ +You can use this site to get access to the latest development sources +and the up-to-date documentation. + +Copy of the specific documentation is also in + doc/PDF/colvars-refman-lammps.pdf + +Please report bugs and request new features at: +https://github.com/colvars/colvars/issues + +The following publications describe the principles of the implementation of this library: + Using collective variables to drive molecular dynamics simulations, + Giacomo Fiorin , Michael L. Klein & Jérôme Hénin (2013): + Molecular Physics DOI:10.1080/00268976.2013.813594 + Exploring Multidimensional Free Energy Landscapes Using Time-Dependent Biases on Collective Variables, J. Hénin, G. Fiorin, C. Chipot, and M. L. Klein, diff --git a/lib/colvars/colvar.cpp b/lib/colvars/colvar.cpp index 1c3d9c8215..f6652852eb 100644 --- a/lib/colvars/colvar.cpp +++ b/lib/colvars/colvar.cpp @@ -14,7 +14,7 @@ colvar::colvar (std::string const &conf) { cvm::log ("Initializing a new collective variable.\n"); - + get_keyval (conf, "name", this->name, (std::string ("colvar")+cvm::to_str (cvm::colvars.size()+1))); @@ -26,7 +26,7 @@ colvar::colvar (std::string const &conf) "\", as another colvar.\n"); } - // all tasks disabled by default + // all tasks disabled by default for (size_t i = 0; i < task_ntot; i++) { tasks[i] = false; } @@ -152,7 +152,7 @@ colvar::colvar (std::string const &conf) // Decide whether the colvar is periodic // Used to wrap extended DOF if extendedLagrangian is on - if (cvcs.size() == 1 && (cvcs[0])->b_periodic && (cvcs[0])->sup_np == 1 + if (cvcs.size() == 1 && (cvcs[0])->b_periodic && (cvcs[0])->sup_np == 1 && (cvcs[0])->sup_coeff == 1.0 ) { this->b_periodic = true; this->period = (cvcs[0])->period; @@ -167,6 +167,9 @@ colvar::colvar (std::string const &conf) // check the available features of each cvc for (size_t i = 0; i < cvcs.size(); i++) { + if ((cvcs[i])->b_debug_gradients) + enable (task_gradients); + if ((cvcs[i])->sup_np != 1) { if (cvm::debug() && b_linear) cvm::log ("Warning: You are using a non-linear polynomial " @@ -307,7 +310,7 @@ colvar::colvar (std::string const &conf) cvm::log ("Enabling the extended Lagrangian term for colvar \""+ this->name+"\".\n"); - + enable (task_extended_lagrangian); xr.type (this->type()); @@ -525,10 +528,10 @@ void colvar::enable (colvar::task const &t) if ( !tasks[task_extended_lagrangian] ) { enable (task_gradients); - if (!b_Jacobian_force) + if (!b_Jacobian_force) cvm::fatal_error ("Error: colvar \""+this->name+ "\" does not have Jacobian forces implemented.\n"); - if (!b_linear) + if (!b_linear) cvm::fatal_error ("Error: colvar \""+this->name+ "\" must be defined as a linear combination " "to calculate the Jacobian force.\n"); @@ -664,12 +667,24 @@ void colvar::disable (colvar::task const &t) } +void colvar::setup() { + // loop over all components to reset masses of all groups + for (size_t i = 0; i < cvcs.size(); i++) { + for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { + cvm::atom_group &atoms = *(cvcs[i]->atom_groups[ig]); + atoms.read_positions(); + atoms.reset_mass(name,i,ig); + } + } +} + + colvar::~colvar() { for (size_t i = 0; i < cvcs.size(); i++) { delete cvcs[i]; } -} +} @@ -682,6 +697,8 @@ void colvar::calc() cvm::log ("Calculating colvar \""+this->name+"\".\n"); // prepare atom groups for calculation + if (cvm::debug()) + cvm::log ("Collecting data from atom groups.\n"); for (size_t i = 0; i < cvcs.size(); i++) { for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { cvm::atom_group &atoms = *(cvcs[i]->atom_groups[ig]); @@ -697,19 +714,21 @@ void colvar::calc() for (size_t i = 0; i < cvcs.size(); i++) { for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { cvcs[i]->atom_groups[ig]->read_velocities(); - } + } } } if (tasks[task_system_force]) { for (size_t i = 0; i < cvcs.size(); i++) { for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { cvcs[i]->atom_groups[ig]->read_system_forces(); - } + } } } // calculate the value of the colvar + if (cvm::debug()) + cvm::log ("Calculating colvar components.\n"); x.reset(); if (x.type() == colvarvalue::type_scalar) { // polynomial combination allowed @@ -727,7 +746,7 @@ void colvar::calc() ( ((cvcs[i])->sup_np != 1) ? std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np) : (cvcs[i])->value().real_value ); - } + } } else { // only linear combination allowed @@ -741,7 +760,7 @@ void colvar::calc() cvm::to_str ((cvcs[i])->value(), cvm::cv_width, cvm::cv_prec)+".\n"); x += (cvcs[i])->sup_coeff * (cvcs[i])->value(); - } + } } if (cvm::debug()) @@ -762,9 +781,9 @@ void colvar::calc() // if requested, propagate (via chain rule) the gradients above // to the atoms used to define the roto-translation for (size_t ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) { - if (cvcs[i]->atom_groups[ig]->b_fit_gradients) + if (cvcs[i]->atom_groups[ig]->b_fit_gradients) cvcs[i]->atom_groups[ig]->calc_fit_gradients(); - } + } cvm::decrease_depth(); } @@ -801,7 +820,7 @@ void colvar::calc() for (size_t k = 0; k < cvcs[i]->atom_groups[j]->size(); k++) { int a = std::lower_bound (atom_ids.begin(), atom_ids.end(), cvcs[i]->atom_groups[j]->at(k).id) - atom_ids.begin(); - atomic_gradients[a] += coeff * cvcs[i]->atom_groups[j]->at(k).grad; + atomic_gradients[a] += coeff * cvcs[i]->atom_groups[j]->at(k).grad; } } } @@ -925,7 +944,7 @@ cvm::real colvar::update() } if (tasks[task_Jacobian_force]) { - + cvm::increase_depth(); for (size_t i = 0; i < cvcs.size(); i++) { (cvcs[i])->calc_Jacobian_derivative(); @@ -941,8 +960,8 @@ cvm::real colvar::update() fj *= cvm::boltzmann() * cvm::temperature(); // the instantaneous Jacobian force was not included in the reported system force; - // instead, it is subtracted from the applied force (silent Jacobian correction) - if (! tasks[task_report_Jacobian_force]) + // instead, it is subtracted from the applied force (silent Jacobian correction) + if (! tasks[task_report_Jacobian_force]) f -= fj; } @@ -966,9 +985,9 @@ cvm::real colvar::update() // leap to v_(i+1/2) if (tasks[task_langevin]) { vr -= dt * ext_gamma * vr.real_value; - vr += dt * ext_sigma * cvm::rand_gaussian() / ext_mass; - } - vr += (0.5 * dt) * fr / ext_mass; + vr += dt * ext_sigma * cvm::rand_gaussian() / ext_mass; + } + vr += (0.5 * dt) * fr / ext_mass; xr += dt * vr; xr.apply_constraints(); if (this->b_periodic) this->wrap (xr); @@ -989,13 +1008,13 @@ cvm::real colvar::update() void colvar::communicate_forces() { if (cvm::debug()) - cvm::log ("Communicating forces from colvar \""+this->name+"\".\n"); + cvm::log ("Communicating forces from colvar \""+this->name+"\".\n"); if (x.type() == colvarvalue::type_scalar) { for (size_t i = 0; i < cvcs.size(); i++) { cvm::increase_depth(); - (cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff * + (cvcs[i])->apply_force (f * (cvcs[i])->sup_coeff * cvm::real ((cvcs[i])->sup_np) * (std::pow ((cvcs[i])->value().real_value, (cvcs[i])->sup_np-1)) ); @@ -1012,7 +1031,7 @@ void colvar::communicate_forces() } if (cvm::debug()) - cvm::log ("Done communicating forces from colvar \""+this->name+"\".\n"); + cvm::log ("Done communicating forces from colvar \""+this->name+"\".\n"); } @@ -1200,7 +1219,7 @@ std::istream & colvar::read_traj (std::istream &is) is >> ft; - if (tasks[task_extended_lagrangian]) { + if (tasks[task_extended_lagrangian]) { is >> fr; ft_reported = fr; } else { @@ -1248,7 +1267,7 @@ std::ostream & colvar::write_restart (std::ostream &os) { os << "}\n\n"; return os; -} +} std::ostream & colvar::write_traj_label (std::ostream & os) @@ -1418,7 +1437,7 @@ inline void history_add_value (size_t const &history_length, inline void history_incr (std::list< std::list > &history, std::list< std::list >::iterator &history_p) { - if ((++history_p) == history.end()) + if ((++history_p) == history.end()) history_p = history.begin(); } @@ -1435,7 +1454,7 @@ void colvar::calc_acf() // first-step operations - colvar *cfcv = (acf_colvar_name.size() ? + colvar *cfcv = (acf_colvar_name.size() ? cvm::colvar_p (acf_colvar_name) : this); if (cfcv->type() != this->type()) @@ -1474,7 +1493,7 @@ void colvar::calc_acf() } else { - colvar *cfcv = (acf_colvar_name.size() ? + colvar *cfcv = (acf_colvar_name.size() ? cvm::colvar_p (acf_colvar_name) : this); @@ -1539,7 +1558,7 @@ void colvar::calc_vel_acf (std::list &v_list, // inner products of previous velocities with current (acf_i and // vs_i are updated) - colvarvalue::inner_opt (v, vs_i, v_list.end(), acf_i); + colvarvalue::inner_opt (v, vs_i, v_list.end(), acf_i); acf_nframes++; } @@ -1559,7 +1578,7 @@ void colvar::calc_coor_acf (std::list &x_list, *(acf_i++) += x.norm2(); - colvarvalue::inner_opt (x, xs_i, x_list.end(), acf_i); + colvarvalue::inner_opt (x, xs_i, x_list.end(), acf_i); acf_nframes++; } @@ -1581,7 +1600,7 @@ void colvar::calc_p2coor_acf (std::list &x_list, // value of P2(0) = 1 *(acf_i++) += 1.0; - colvarvalue::p2leg_opt (x, xs_i, x_list.end(), acf_i); + colvarvalue::p2leg_opt (x, xs_i, x_list.end(), acf_i); acf_nframes++; } diff --git a/lib/colvars/colvar.h b/lib/colvars/colvar.h index c16355dd16..6f6692e904 100644 --- a/lib/colvars/colvar.h +++ b/lib/colvars/colvar.h @@ -215,7 +215,7 @@ protected: cvm::real ext_gamma; /// Amplitude of Gaussian white noise for Langevin extended dynamics cvm::real ext_sigma; - + /// \brief Harmonic restraint force colvarvalue fr; @@ -288,6 +288,9 @@ public: /// Disable the specified task void disable (colvar::task const &t); + /// Get ready for a run and possibly re-initialize internal data + void setup(); + /// Destructor ~colvar(); @@ -380,7 +383,7 @@ protected: colvarvalue x_old; /// Time series of values and velocities used in correlation - /// functions + /// functions std::list< std::list > acf_x_history, acf_v_history; /// Time series of values and velocities used in correlation /// functions (pointers)x diff --git a/lib/colvars/colvaratoms.cpp b/lib/colvars/colvaratoms.cpp index 10898f0fa9..c211168ff2 100644 --- a/lib/colvars/colvaratoms.cpp +++ b/lib/colvars/colvaratoms.cpp @@ -26,8 +26,8 @@ cvm::atom_group::atom_group (std::string const &conf, cvm::atom_group::atom_group (std::vector const &atoms) - : b_dummy (false), b_center (false), b_rotate (false), - ref_pos_group (NULL), b_fit_gradients (false), + : b_dummy (false), b_center (false), b_rotate (false), + ref_pos_group (NULL), b_fit_gradients (false), noforce (false) { this->reserve (atoms.size()); @@ -43,8 +43,8 @@ cvm::atom_group::atom_group (std::vector const &atoms) cvm::atom_group::atom_group() - : b_dummy (false), b_center (false), b_rotate (false), - ref_pos_group (NULL), b_fit_gradients (false), + : b_dummy (false), b_center (false), b_rotate (false), + ref_pos_group (NULL), b_fit_gradients (false), noforce (false) { total_mass = 0.0; @@ -71,6 +71,18 @@ void cvm::atom_group::add_atom (cvm::atom const &a) } +void cvm::atom_group::reset_mass(std::string &name, int i, int j) +{ + total_mass = 0.0; + for (cvm::atom_iter ai = this->begin(); + ai != this->end(); ai++) { + total_mass += ai->mass; + } + cvm::log ("Re-initialized atom group "+name+":"+cvm::to_str (i)+"/"+ + cvm::to_str (j)+". "+ cvm::to_str (this->size())+ + " atoms: total mass = "+cvm::to_str (this->total_mass)+".\n"); +} + void cvm::atom_group::parse (std::string const &conf, char const *key) { @@ -129,6 +141,25 @@ void cvm::atom_group::parse (std::string const &conf, atom_indexes.clear(); } + + std::string index_group_name; + if (get_keyval (group_conf, "indexGroup", index_group_name)) { + // use an index group from the index file read globally + std::list::iterator names_i = cvm::index_group_names.begin(); + std::list >::iterator index_groups_i = cvm::index_groups.begin(); + for ( ; names_i != cvm::index_group_names.end() ; names_i++, index_groups_i++) { + if (*names_i == index_group_name) + break; + } + if (names_i == cvm::index_group_names.end()) { + cvm::fatal_error ("Error: could not find index group "+ + index_group_name+" among those provided by the index file.\n"); + } + this->reserve (index_groups_i->size()); + for (size_t i = 0; i < index_groups_i->size(); i++) { + this->push_back (cvm::atom ((*index_groups_i)[i])); + } + } } { @@ -234,7 +265,7 @@ void cvm::atom_group::parse (std::string const &conf, } } - for (std::vector::iterator a1 = this->begin(); + for (std::vector::iterator a1 = this->begin(); a1 != this->end(); a1++) { std::vector::iterator a2 = a1; ++a2; @@ -253,10 +284,10 @@ void cvm::atom_group::parse (std::string const &conf, if (get_keyval (group_conf, "dummyAtom", dummy_atom_pos, cvm::atom_pos(), mode)) { b_dummy = true; this->total_mass = 1.0; - } else + } else b_dummy = false; - if (b_dummy && (this->size())) + if (b_dummy && (this->size())) cvm::fatal_error ("Error: cannot set up group \""+ std::string (key)+"\" as a dummy atom " "and provide it with atom definitions.\n"); @@ -328,7 +359,7 @@ void cvm::atom_group::parse (std::string const &conf, std::string ref_pos_col; double ref_pos_col_value; - + if (get_keyval (group_conf, "refPositionsCol", ref_pos_col, std::string (""), mode)) { // if provided, use PDB column to select coordinates bool found = get_keyval (group_conf, "refPositionsColValue", ref_pos_col_value, 0.0, mode); @@ -486,7 +517,7 @@ void cvm::atom_group::calc_apply_roto_translation() } } -void cvm::atom_group::apply_translation (cvm::rvector const &t) +void cvm::atom_group::apply_translation (cvm::rvector const &t) { if (b_dummy) return; @@ -496,7 +527,7 @@ void cvm::atom_group::apply_translation (cvm::rvector const &t) } } -void cvm::atom_group::apply_rotation (cvm::rotation const &rot) +void cvm::atom_group::apply_rotation (cvm::rotation const &rot) { if (b_dummy) return; @@ -617,11 +648,11 @@ void cvm::atom_group::calc_fit_gradients() } if (b_rotate) { - + // add the rotation matrix contribution to the gradients cvm::rotation const rot_inv = rot.inverse(); cvm::atom_pos const cog = this->center_of_geometry(); - + for (size_t i = 0; i < this->size(); i++) { cvm::atom_pos const pos_orig = rot_inv.rotate ((b_center ? ((*this)[i].pos - cog) : ((*this)[i].pos))); @@ -785,7 +816,7 @@ void cvm::atom_group::apply_force (cvm::rvector const &force) for (cvm::atom_iter ai = this->begin(); ai != this->end(); ai++) { ai->apply_force ((ai->mass/this->total_mass) * force); - } + } } } diff --git a/lib/colvars/colvaratoms.h b/lib/colvars/colvaratoms.h index d2be1dfe6e..e4d95537f8 100644 --- a/lib/colvars/colvaratoms.h +++ b/lib/colvars/colvaratoms.h @@ -8,7 +8,7 @@ /// \brief Stores numeric id, mass and all mutable data for an atom, /// mostly used by a \link cvc \endlink -/// +/// /// This class may be used (although not necessarily) to keep atomic /// data (id, mass, position and collective variable derivatives) /// altogether. There may be multiple instances with identical @@ -23,7 +23,7 @@ protected: /// \brief Index in the list of atoms involved by the colvars (\b /// NOT in the global topology!) - size_t index; + int index; public: @@ -47,7 +47,7 @@ public: /// \brief Gradient of a scalar collective variable with respect /// to this atom - /// + /// /// This can only handle a scalar collective variable (i.e. when /// the \link colvarvalue::real_value \endlink member is used /// from the \link colvarvalue \endlink class), which is also the @@ -57,8 +57,8 @@ public: /// implementation cvm::rvector grad; - /// \brief Default constructor, setting id to a non-valid value - inline atom() {} + /// \brief Default constructor, setting id and index to invalid numbers + atom() : id (-1), index (-1) { reset_data(); } /// \brief Initialize an atom for collective variable calculation /// and get its internal identifier \param atom_number Atom index in @@ -121,7 +121,7 @@ class colvarmodule::atom_group public colvarparse { public: - // Note: all members here are kept public, to make possible to any + // Note: all members here are kept public, to allow any // object accessing and manipulating them @@ -138,7 +138,7 @@ public: /// Allocates and populates the sorted list of atom ids void create_sorted_ids (void); - + /// \brief When updating atomic coordinates, translate them to align with the /// center of mass of the reference coordinates @@ -175,7 +175,7 @@ public: /// \brief If b_center or b_rotate is true, use this group to /// define the transformation (default: this group itself) atom_group *ref_pos_group; - + /// Total mass of the atom group cvm::real total_mass; @@ -199,9 +199,14 @@ public: /// \brief Initialize the group after a temporary vector of atoms atom_group (std::vector const &atoms); - /// \brief Add an atom to this group + /// \brief Add an atom to this group void add_atom (cvm::atom const &a); + /// \brief Re-initialize the total mass of a group. + /// This is needed in case the hosting MD code has an option to + /// change atom masses after their initialization. + void reset_mass (std::string &name, int i, int j); + /// \brief Default constructor atom_group(); @@ -216,7 +221,7 @@ public: void calc_apply_roto_translation(); /// \brief Save center of geometry fo ref positions, - /// then subtract it + /// then subtract it void center_ref_pos(); /// \brief Move all positions diff --git a/lib/colvars/colvarbias.cpp b/lib/colvars/colvarbias.cpp index 561bb4a688..58ec049b3c 100644 --- a/lib/colvars/colvarbias.cpp +++ b/lib/colvars/colvarbias.cpp @@ -41,10 +41,11 @@ colvarbias::colvarbias (std::string const &conf, char const *key) add_colvar (colvars_str[i]); } } - if (!colvars.size()) { cvm::fatal_error ("Error: no collective variables specified.\n"); } + + get_keyval (conf, "outputEnergy", b_output_energy, false); } @@ -59,7 +60,7 @@ void colvarbias::add_colvar (std::string const &cv_name) cvp->enable (colvar::task_gradients); if (cvm::debug()) cvm::log ("Applying this bias to collective variable \""+ - cvp->name+"\".\n"); + cvp->name+"\".\n"); colvars.push_back (cvp); colvar_forces.push_back (colvarvalue (cvp->type())); } else { @@ -79,13 +80,47 @@ void colvarbias::communicate_forces() } colvars[i]->add_bias_force (colvar_forces[i]); } -} +} + + +void colvarbias::change_configuration(std::string const &conf) +{ + cvm::fatal_error ("Error: change_configuration() not implemented.\n"); +} + + +cvm::real colvarbias::energy_difference(std::string const &conf) +{ + cvm::fatal_error ("Error: energy_difference() not implemented.\n"); + return 0.; +} + + +std::ostream & colvarbias::write_traj_label (std::ostream &os) +{ + os << " "; + if (b_output_energy) + os << " E_" + << cvm::wrap_string (this->name, cvm::en_width-2); + return os; +} + + +std::ostream & colvarbias::write_traj (std::ostream &os) +{ + os << " "; + if (b_output_energy) + os << " " + << bias_energy; + return os; +} + colvarbias_harmonic::colvarbias_harmonic (std::string const &conf, char const *key) - : colvarbias (conf, key), + : colvarbias (conf, key), target_nsteps (0), target_nstages (0) { @@ -171,25 +206,22 @@ colvarbias_harmonic::colvarbias_harmonic (std::string const &conf, } } + get_keyval (conf, "outputCenters", b_output_centers, false); + get_keyval (conf, "outputAccumulatedWork", b_output_acc_work, false); + acc_work = 0.0; + if (cvm::debug()) cvm::log ("Done initializing a new harmonic restraint bias.\n"); } - -void colvarbias::change_configuration(std::string const &conf) +colvarbias_harmonic::~colvarbias_harmonic () { - cvm::fatal_error ("Error: change_configuration() not implemented.\n"); + if (cvm::n_harm_biases > 0) + cvm::n_harm_biases -= 1; } -cvm::real colvarbias::energy_difference(std::string const &conf) -{ - cvm::fatal_error ("Error: energy_difference() not implemented.\n"); - return 0.; -} - - -void colvarbias_harmonic::change_configuration(std::string const &conf) +void colvarbias_harmonic::change_configuration (std::string const &conf) { get_keyval (conf, "forceConstant", force_k, force_k); if (get_keyval (conf, "centers", colvar_centers, colvar_centers)) { @@ -317,7 +349,7 @@ cvm::real colvarbias_harmonic::update() if (target_equil_steps == 0 || cvm::step_absolute() % target_nsteps >= target_equil_steps) { // Start averaging after equilibration period, if requested - + // Square distance normalized by square colvar width cvm::real dist_sq = 0.0; for (size_t i = 0; i < colvars.size(); i++) { @@ -335,7 +367,7 @@ cvm::real colvarbias_harmonic::update() cvm::log ("Lambda= " + cvm::to_str (lambda) + " dA/dLambda= " + cvm::to_str (restraint_FE / cvm::real(target_nsteps - target_equil_steps))); - + // ...and move on to the next one if (stage < target_nstages) { @@ -363,7 +395,7 @@ cvm::real colvarbias_harmonic::update() if (cvm::debug()) cvm::log ("Done updating the harmonic bias \""+this->name+"\".\n"); - + // Force and energy calculation for (size_t i = 0; i < colvars.size(); i++) { colvar_forces[i] = @@ -379,6 +411,15 @@ cvm::real colvarbias_harmonic::update() colvar_centers[i]))+"\n"); } + if (b_output_acc_work) { + if ((cvm::step_relative() > 0) || (cvm::step_absolute() == 0)) { + for (size_t i = 0; i < colvars.size(); i++) { + // project forces on the calculated increments at this step + acc_work += colvar_forces[i] * centers_incr[i]; + } + } + } + if (cvm::debug()) cvm::log ("Current forces for the harmonic bias \""+ this->name+"\": "+cvm::to_str (colvar_forces)+".\n"); @@ -408,7 +449,7 @@ std::istream & colvarbias_harmonic::read_restart (std::istream &is) return is; } -// int id = -1; +// int id = -1; std::string name = ""; // if ( ( (colvarparse::get_keyval (conf, "id", id, -1, colvarparse::parse_silent)) && // (id != this->id) ) || @@ -442,6 +483,11 @@ std::istream & colvarbias_harmonic::read_restart (std::istream &is) cvm::fatal_error ("Error: current stage is missing from the restart.\n"); } + if (b_output_acc_work) { + if (!get_keyval (conf, "accumulatedWork", acc_work)) + cvm::fatal_error ("Error: accumulatedWork is missing from the restart.\n"); + } + is >> brace; if (brace != "}") { cvm::fatal_error ("Error: corrupt restart information for harmonic bias \""+ @@ -484,9 +530,61 @@ std::ostream & colvarbias_harmonic::write_restart (std::ostream &os) << stage << "\n"; } + if (b_output_acc_work) { + os << " accumulatedWork " << acc_work << "\n"; + } + os << " }\n" << "}\n\n"; return os; } + +std::ostream & colvarbias_harmonic::write_traj_label (std::ostream &os) +{ + os << " "; + + if (b_output_energy) + os << " E_" + << cvm::wrap_string (this->name, cvm::en_width-2); + + if (b_output_centers) + for (size_t i = 0; i < colvars.size(); i++) { + size_t const this_cv_width = (colvars[i]->value()).output_width (cvm::cv_width); + os << " x0_" + << cvm::wrap_string (colvars[i]->name, this_cv_width-3); + } + + if (b_output_acc_work) + os << " W_" + << cvm::wrap_string (this->name, cvm::en_width-2); + + return os; +} + + +std::ostream & colvarbias_harmonic::write_traj (std::ostream &os) +{ + os << " "; + + if (b_output_energy) + os << " " + << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) + << bias_energy; + + if (b_output_centers) + for (size_t i = 0; i < colvars.size(); i++) { + os << " " + << std::setprecision (cvm::cv_prec) << std::setw (cvm::cv_width) + << colvar_centers[i]; + } + + if (b_output_acc_work) + os << " " + << std::setprecision (cvm::en_prec) << std::setw (cvm::en_width) + << acc_work; + + return os; +} + diff --git a/lib/colvars/colvarbias.h b/lib/colvars/colvarbias.h index 2130c4ba1c..5a2c2d8c5f 100644 --- a/lib/colvars/colvarbias.h +++ b/lib/colvars/colvarbias.h @@ -14,7 +14,7 @@ public: /// Name of this bias std::string name; - + /// Add a new collective variable to this bias void add_colvar (std::string const &cv_name); @@ -35,7 +35,7 @@ public: void communicate_forces(); /// \brief Constructor - /// + /// /// The constructor of the colvarbias base class is protected, so /// that it can only be called from inherited classes colvarbias (std::string const &conf, char const *key); @@ -52,6 +52,13 @@ public: /// Write the bias configuration to a restart file virtual std::ostream & write_restart (std::ostream &os) = 0; + /// Write a label to the trajectory file (comment line) + virtual std::ostream & write_traj_label (std::ostream &os); + + /// Output quantities such as the bias energy to the trajectory file + virtual std::ostream & write_traj (std::ostream &os); + + protected: /// \brief Pointers to collective variables to which the bias is @@ -62,10 +69,12 @@ protected: /// \brief Current forces from this bias to the colvars std::vector colvar_forces; - /// \brief Current energy of this bias (colvar_forces should be - /// obtained by deriving this) + /// \brief Current energy of this bias (colvar_forces should be obtained by deriving this) cvm::real bias_energy; + /// Whether to write the current bias energy from this bias to the trajectory file + bool b_output_energy; + /// \brief Whether this bias has already accumulated information /// (when relevant) bool has_data; @@ -94,11 +103,17 @@ public: /// Write the bias configuration to a restart file virtual std::ostream & write_restart (std::ostream &os); + /// Write a label to the trajectory file (comment line) + virtual std::ostream & write_traj_label (std::ostream &os); + + /// Output quantities such as the bias energy to the trajectory file + virtual std::ostream & write_traj (std::ostream &os); + /// \brief Constructor colvarbias_harmonic (std::string const &conf, char const *key); /// Destructor - virtual inline ~colvarbias_harmonic() {} + virtual ~colvarbias_harmonic(); protected: @@ -109,27 +124,9 @@ protected: /// \brief Restraint centers without wrapping or constraints applied std::vector colvar_centers_raw; - /// \brief Restraint force constant - cvm::real force_k; - /// \brief Moving target? bool b_chg_centers; - /// \brief Changing force constant? - bool b_chg_force_k; - - /// \brief Restraint force constant (target value) - cvm::real target_force_k; - - /// \brief Equilibration steps for restraint FE calculation through TI - cvm::real target_equil_steps; - - /// \brief Restraint force constant (starting value) - cvm::real starting_force_k; - - /// \brief Lambda-schedule for custom varying force constant - std::vector lambda_schedule; - /// \brief New restraint centers std::vector target_centers; @@ -137,12 +134,41 @@ protected: /// (or stage) towards the new values (calculated from target_nsteps) std::vector centers_incr; + /// Whether to write the current restraint centers to the trajectory file + bool b_output_centers; + + /// Whether to write the current accumulated work to the trajectory file + bool b_output_acc_work; + + /// \brief Accumulated work + cvm::real acc_work; + + + /// \brief Restraint force constant + cvm::real force_k; + + /// \brief Changing force constant? + bool b_chg_force_k; + + /// \brief Restraint force constant (target value) + cvm::real target_force_k; + + /// \brief Restraint force constant (starting value) + cvm::real starting_force_k; + + /// \brief Lambda-schedule for custom varying force constant + std::vector lambda_schedule; + /// \brief Exponent for varying the force constant cvm::real force_k_exp; - /// \brief Number of steps required to reach the target force constant - /// or restraint centers - size_t target_nsteps; + /// \brief Intermediate quantity to compute the restraint free energy + /// (in TI, would be the accumulating FE derivative) + cvm::real restraint_FE; + + + /// \brief Equilibration steps for restraint FE calculation through TI + cvm::real target_equil_steps; /// \brief Number of stages over which to perform the change /// If zero, perform a continuous change @@ -150,10 +176,10 @@ protected: /// \brief Number of current stage of the perturbation int stage; - - /// \brief Intermediate quantity to compute the restraint free energy - /// (in TI, would be the accumulating FE derivative) - cvm::real restraint_FE; + + /// \brief Number of steps required to reach the target force constant + /// or restraint centers + size_t target_nsteps; }; diff --git a/lib/colvars/colvarbias_abf.cpp b/lib/colvars/colvarbias_abf.cpp index 677bedffbd..dcbfba6acd 100644 --- a/lib/colvars/colvarbias_abf.cpp +++ b/lib/colvars/colvarbias_abf.cpp @@ -15,7 +15,7 @@ colvarbias_abf::colvarbias_abf (std::string const &conf, char const *key) { if (cvm::temperature() == 0.0) cvm::log ("WARNING: ABF should not be run without a thermostat or at 0 Kelvin!\n"); - + // ************* parsing general ABF options *********************** get_keyval (conf, "applyBias", apply_bias, true); @@ -32,7 +32,7 @@ colvarbias_abf::colvarbias_abf (std::string const &conf, char const *key) } get_keyval (conf, "fullSamples", full_samples, 200); - if ( full_samples <= 1 ) full_samples = 1; + if ( full_samples <= 1 ) full_samples = 1; min_samples = full_samples / 2; // full_samples - min_samples >= 1 is guaranteed @@ -79,7 +79,21 @@ colvarbias_abf::colvarbias_abf (std::string const &conf, char const *key) } // Here we could check for orthogonality of the Cartesian coordinates - // and make it just a warning if some parameter is set? + // and make it just a warning if some parameter is set? + } + + if (get_keyval (conf, "maxForce", max_force)) { + if (max_force.size() != colvars.size()) { + cvm::fatal_error ("Error: Number of parameters to maxForce does not match number of colvars."); + } + for (size_t i=0; i 0 ) { read_gradients_samples (); } - + cvm::log ("Finished ABF setup.\n"); } @@ -114,6 +128,9 @@ colvarbias_abf::~colvarbias_abf() } delete [] force; + + if (cvm::n_abf_biases > 0) + cvm::n_abf_biases -= 1; } @@ -125,7 +142,7 @@ cvm::real colvarbias_abf::update() if (cvm::debug()) cvm::log ("Updating ABF bias " + this->name); if (cvm::step_relative() == 0) { - + // At first timestep, do only: // initialization stuff (file operations relying on n_abf_biases // compute current value of colvars @@ -158,7 +175,7 @@ cvm::real colvarbias_abf::update() } // save bin for next timestep - force_bin = bin; + force_bin = bin; // Reset biasing forces from previous timestep for (size_t i=0; ivalue (bin)); if ( fact != 0.0 ) { - if ( (colvars.size() == 1) && colvars[0]->periodic_boundaries() ) { // Enforce a zero-mean bias on periodic, 1D coordinates - colvar_forces[0].real_value += fact * (grad[0] / cvm::real (count) - gradients->average ()); + // in other words: boundary condition is that the biasing potential is periodic + colvar_forces[0].real_value = fact * (grad[0] / cvm::real (count) - gradients->average ()); } else { for (size_t i=0; i max_force[i] * max_force[i] ) { + colvar_forces[i].real_value = (colvar_forces[i].real_value > 0 ? max_force[i] : -1.0 * max_force[i]); + } } } } @@ -203,7 +226,7 @@ cvm::real colvarbias_abf::update() // otherwise, backup and replace write_gradients_samples (output_prefix + ".hist", (cvm::step_absolute() > 0)); } - return 0.0; // TODO compute bias energy whenever possible (i.e. 1D with updateBias off) + return 0.0; } @@ -250,7 +273,7 @@ void colvarbias_abf::read_gradients_samples () samples_in_name = input_prefix[i] + ".count"; gradients_in_name = input_prefix[i] + ".grad"; // For user-provided files, the per-bias naming scheme may not apply - + std::ifstream is; cvm::log ("Reading sample count from " + samples_in_name + " and gradients from " + gradients_in_name); @@ -272,7 +295,7 @@ void colvarbias_abf::read_gradients_samples () std::ostream & colvarbias_abf::write_restart (std::ostream& os) { - std::ios::fmtflags flags (os.flags ()); + std::ios::fmtflags flags (os.flags ()); os.setf(std::ios::fmtflags (0), std::ios::floatfield); // default floating-point format os << "abf {\n" @@ -325,7 +348,7 @@ std::istream & colvarbias_abf::read_restart (std::istream& is) if ( name == "" ) { cvm::fatal_error ("Error: \"abf\" block in the restart file has no name.\n"); } - + if ( !(is >> key) || !(key == "samples")) { cvm::log ("Error: in reading restart configuration for ABF bias \""+ this->name+"\" at position "+ @@ -381,7 +404,7 @@ colvarbias_histogram::colvarbias_histogram (std::string const &conf, char const bin.assign (colvars.size(), 0); out_name = cvm::output_prefix + "." + this->name + ".dat"; - cvm::log ("Histogram will be written to file " + out_name); + cvm::log ("Histogram will be written to file " + out_name); cvm::log ("Finished histogram setup.\n"); } @@ -395,6 +418,9 @@ colvarbias_histogram::~colvarbias_histogram() delete grid; grid = NULL; } + + if (cvm::n_histo_biases > 0) + cvm::n_histo_biases -= 1; } /// Update the grid @@ -452,7 +478,7 @@ std::istream & colvarbias_histogram::read_restart (std::istream& is) cvm::fatal_error ("Error: \"histogram\" block in the restart file " "has no name.\n"); } - + if ( !(is >> key) || !(key == "grid")) { cvm::log ("Error: in reading restart configuration for histogram \""+ this->name+"\" at position "+ diff --git a/lib/colvars/colvarbias_abf.h b/lib/colvars/colvarbias_abf.h index f107b78248..e82d2f08ac 100644 --- a/lib/colvars/colvarbias_abf.h +++ b/lib/colvars/colvarbias_abf.h @@ -46,6 +46,10 @@ private: bool b_history_files; size_t history_freq; + /// Cap applied biasing force? + bool cap_force; + std::vector max_force; + // Internal data and methods std::vector bin, force_bin; @@ -84,7 +88,7 @@ private: std::vector bin; std::string out_name; - int output_freq; + int output_freq; void write_grid (); std::ofstream grid_os; /// Stream for writing grid to disk diff --git a/lib/colvars/colvarbias_meta.cpp b/lib/colvars/colvarbias_meta.cpp index 2d03b5c8d8..3ddc6af6c5 100644 --- a/lib/colvars/colvarbias_meta.cpp +++ b/lib/colvars/colvarbias_meta.cpp @@ -54,7 +54,7 @@ colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) get_keyval (conf, "multipleReplicas", b_replicas, false); if (b_replicas) comm = multiple_replicas; - else + else comm = single_replica; } @@ -76,7 +76,8 @@ colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) } get_keyval (conf, "keepHills", keep_hills, false); - get_keyval (conf, "dumpFreeEnergyFile", dump_fes, true); + if (! get_keyval (conf, "writeFreeEnergyFile", dump_fes, true)) + get_keyval (conf, "dumpFreeEnergyFile", dump_fes, true, colvarparse::parse_silent); get_keyval (conf, "saveFreeEnergyFile", dump_fes_save, false); for (size_t i = 0; i < colvars.size(); i++) { @@ -113,7 +114,7 @@ colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) "when using more than one replica.\n"); get_keyval (conf, "replicasRegistry", - replicas_registry_file, + replicas_registry_file, (this->name+".replicas.registry.txt")); get_keyval (conf, "replicaUpdateFrequency", @@ -171,7 +172,7 @@ colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) // if this replica was not included yet, we should generate a // new record for it: but first, we write this replica's files, // for the others to read - + // open the "hills" buffer file replica_hills_os.open (replica_hills_file.c_str()); if (!replica_hills_os.good()) @@ -188,7 +189,7 @@ colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) // if we're running without grids, use a growing list of "hills" files // otherwise, just one state file and one "hills" file as buffer - std::ofstream list_os (replica_list_file.c_str(), + std::ofstream list_os (replica_list_file.c_str(), (use_grids ? std::ios::trunc : std::ios::app)); if (! list_os.good()) cvm::fatal_error ("Error: in opening file \""+ @@ -232,7 +233,7 @@ colvarbias_meta::colvarbias_meta (std::string const &conf, char const *key) cvm::log("Well-tempered metadynamics is used.\n"); cvm::log("The bias temperature is "+cvm::to_str(bias_temperature)+".\n"); } - + if (cvm::debug()) cvm::log ("Done initializing the metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+".\n"); @@ -258,6 +259,9 @@ colvarbias_meta::~colvarbias_meta() if (hills_traj_os.good()) hills_traj_os.close(); + + if (cvm::n_meta_biases > 0) + cvm::n_meta_biases -= 1; } @@ -266,7 +270,7 @@ colvarbias_meta::~colvarbias_meta() // Hill management member functions // ********************************************************************** -std::list::const_iterator +std::list::const_iterator colvarbias_meta::create_hill (colvarbias_meta::hill const &h) { hill_iter const hills_end = hills.end(); @@ -324,7 +328,7 @@ colvarbias_meta::delete_hill (hill_iter &h) } if (hills_traj_os.good()) { - // output to the trajectory + // output to the trajectory hills_traj_os << "# DELETED this hill: " << (hills.back()).output_traj() << "\n"; @@ -459,9 +463,9 @@ cvm::real colvarbias_meta::update() cvm::real const hills_energy_sum_here = hills_energy->value(curr_bin); cvm::real const exp_weight = std::exp(-hills_energy_sum_here/(bias_temperature*cvm::boltzmann())); create_hill (hill ((hill_weight*exp_weight), colvars, hill_width)); - } else { + } else { create_hill (hill (hill_weight, colvars, hill_width)); - } + } break; case multiple_replicas: @@ -470,9 +474,9 @@ cvm::real colvarbias_meta::update() cvm::real const hills_energy_sum_here = hills_energy->value(curr_bin); cvm::real const exp_weight = std::exp(-hills_energy_sum_here/(bias_temperature*cvm::boltzmann())); create_hill (hill ((hill_weight*exp_weight), colvars, hill_width, replica_id)); - } else { + } else { create_hill (hill (hill_weight, colvars, hill_width, replica_id)); - } + } if (replica_hills_os.good()) { replica_hills_os << hills.back(); } else { @@ -591,11 +595,11 @@ cvm::real colvarbias_meta::update() calc_hills_force (ic, new_hills_begin, hills.end(), colvar_forces); } - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Hills energy = "+cvm::to_str (bias_energy)+ ", hills forces = "+cvm::to_str (colvar_forces)+".\n"); - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ ": adding the forces from the other replicas.\n"); @@ -611,7 +615,7 @@ cvm::real colvarbias_meta::update() replicas[ir]->hills.end(), colvar_forces); } - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Hills energy = "+cvm::to_str (bias_energy)+ ", hills forces = "+cvm::to_str (colvar_forces)+".\n"); } @@ -641,7 +645,7 @@ void colvarbias_meta::calc_hills (colvarbias_meta::hill_iter h_first, } for (hill_iter h = h_first; h != h_last; h++) { - + // compute the gaussian exponent cvm::real cv_sqdev = 0.0; for (size_t i = 0; i < colvars.size(); i++) { @@ -684,8 +688,8 @@ void colvarbias_meta::calc_hills_force (size_t const &i, if (h->value() == 0.0) continue; colvarvalue const ¢er = h->centers[i]; cvm::real const half_width = 0.5 * h->widths[i]; - forces[i].real_value += - ( h->weight() * h->value() * (0.5 / (half_width*half_width)) * + forces[i].real_value += + ( h->weight() * h->value() * (0.5 / (half_width*half_width)) * (colvars[i]->dist2_lgrad (x, center)).real_value ); } break; @@ -993,7 +997,7 @@ void colvarbias_meta::read_replica_files() } // (re)read the state file if necessary - if ( (! (replicas[ir])->has_data) || + if ( (! (replicas[ir])->has_data) || (! (replicas[ir])->replica_state_file_in_sync) ) { cvm::log ("Metadynamics bias \""+this->name+"\""+ @@ -1013,11 +1017,11 @@ void colvarbias_meta::read_replica_files() } is.close(); } - + // now read the hills added after writing the state file if ((replicas[ir])->replica_hills_file.size()) { - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Metadynamics bias \""+this->name+"\""+ ": checking for new hills from replica \""+ (replicas[ir])->replica_id+"\" in the file \""+ @@ -1064,7 +1068,7 @@ void colvarbias_meta::read_replica_files() "\" at position "+ cvm::to_str ((replicas[ir])->replica_hills_file_pos)+".\n"); - // test whether this is the end of the file + // test whether this is the end of the file is.seekg (0, std::ios::end); if (is.tellg() > (replicas[ir])->replica_hills_file_pos+1) { (replicas[ir])->update_status++; @@ -1108,7 +1112,7 @@ std::istream & colvarbias_meta::read_restart (std::istream& is) size_t const start_pos = is.tellg(); if (comm == single_replica) { - // if using a multiple replicas scheme, output messages + // if using a multiple replicas scheme, output messages // are printed before and after calling this function cvm::log ("Restarting metadynamics bias \""+this->name+"\""+ ".\n"); @@ -1118,7 +1122,7 @@ std::istream & colvarbias_meta::read_restart (std::istream& is) !(is >> brace) || !(brace == "{") || !(is >> colvarparse::read_block ("configuration", conf)) ) { - if (comm == single_replica) + if (comm == single_replica) cvm::log ("Error: in reading restart configuration for metadynamics bias \""+ this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ @@ -1239,7 +1243,7 @@ std::istream & colvarbias_meta::read_restart (std::istream& is) is.clear(); is.seekg (hills_energy_gradients_pos, std::ios::beg); grids_from_restart_file = false; - if (!rebin_grids) { + if (!rebin_grids) { if (hills_energy_backup == NULL) cvm::fatal_error ("Error: couldn't read the free energy gradients grid for metadynamics bias \""+ this->name+"\""+ @@ -1283,7 +1287,7 @@ std::istream & colvarbias_meta::read_restart (std::istream& is) // read the hills explicitly written (if there are any) while (read_hill (is)) { - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Read a previously saved hill under the " "metadynamics bias \""+ this->name+"\", created at step "+ @@ -1364,7 +1368,7 @@ std::istream & colvarbias_meta::read_restart (std::istream& is) hills.erase (hills.begin(), old_hills_end); hills_off_grid.erase (hills_off_grid.begin(), old_hills_off_grid_end); } - + has_data = true; if (comm != single_replica) { @@ -1372,7 +1376,7 @@ std::istream & colvarbias_meta::read_restart (std::istream& is) } return is; -} +} std::istream & colvarbias_meta::read_hill (std::istream &is) @@ -1404,7 +1408,7 @@ std::istream & colvarbias_meta::read_hill (std::istream &is) std::vector h_centers (colvars.size()); for (size_t i = 0; i < colvars.size(); i++) { - h_centers[i].type ((colvars[i]->value()).type()); + h_centers[i].type ((colvars[i]->value()).type()); } { // it is safer to read colvarvalue objects one at a time; @@ -1421,7 +1425,7 @@ std::istream & colvarbias_meta::read_hill (std::istream &is) get_keyval (data, "widths", h_widths, std::vector (colvars.size(), (std::sqrt (2.0 * PI) / 2.0)), parse_silent); - + std::string h_replica = ""; if (comm != single_replica) { get_keyval (data, "replicaID", h_replica, replica_id, parse_silent); @@ -1517,7 +1521,7 @@ std::ostream & colvarbias_meta::write_restart (std::ostream& os) } return os; -} +} void colvarbias_meta::write_pmf() @@ -1527,7 +1531,7 @@ void colvarbias_meta::write_pmf() pmf->create(); std::string fes_file_name_prefix (cvm::output_prefix); - + if ((cvm::n_meta_biases > 1) || (cvm::n_abf_biases > 0)) { // if this is not the only free energy integrator, append // this bias's name, to distinguish it from the output of the other @@ -1598,7 +1602,7 @@ void colvarbias_meta::write_replica_state_file() cvm::fatal_error ("Error: in opening file \""+ replica_state_file+"\" for writing.\n"); - rep_state_os.setf (std::ios::scientific, std::ios::floatfield); + rep_state_os.setf (std::ios::scientific, std::ios::floatfield); rep_state_os << "\n" << "metadynamics {\n" << " configuration {\n" @@ -1672,7 +1676,7 @@ std::string colvarbias_meta::hill::output_traj() << std::setw (cvm::en_width) << W << "\n"; return os.str(); -} +} std::ostream & operator << (std::ostream &os, colvarbias_meta::hill const &h) diff --git a/lib/colvars/colvarbias_meta.h b/lib/colvars/colvarbias_meta.h index 1f51e5469c..8c3eb91003 100644 --- a/lib/colvars/colvarbias_meta.h +++ b/lib/colvars/colvarbias_meta.h @@ -33,7 +33,7 @@ public: /// Destructor virtual ~colvarbias_meta(); - + virtual cvm::real update(); virtual std::istream & read_restart (std::istream &is); @@ -84,7 +84,7 @@ protected: std::istream & read_hill (std::istream &is); /// \brief step present in a state file - /// + /// /// When using grids and reading state files containing them /// (multiple replicas), this is used to check whether a hill is /// newer or older than the grids @@ -147,10 +147,10 @@ protected: /// time steps, appending the step number to each file bool dump_fes_save; - /// \brief Whether to use well-tempered metadynamics - bool well_tempered; + /// \brief Whether to use well-tempered metadynamics + bool well_tempered; - /// \brief Biasing temperature in well-tempered metadynamics + /// \brief Biasing temperature in well-tempered metadynamics cvm::real bias_temperature; /// \brief Try to read the restart information by allocating new @@ -282,7 +282,7 @@ public: centers[i] = cv[i]->value(); widths[i] = cv[i]->width * hill_width; } - if (cvm::debug()) + if (cvm::debug()) cvm::log ("New hill, applied to "+cvm::to_str (cv.size())+ " collective variables, with centers "+ cvm::to_str (centers)+", widths "+ @@ -322,7 +322,7 @@ public: /// Destructor inline ~hill() {} - + /// Get the energy inline cvm::real energy() { diff --git a/lib/colvars/colvarcomp.cpp b/lib/colvars/colvarcomp.cpp index 25da537da8..316454e8ef 100644 --- a/lib/colvars/colvarcomp.cpp +++ b/lib/colvars/colvarcomp.cpp @@ -39,7 +39,7 @@ colvar::cvc::cvc (std::string const &conf) } -void colvar::cvc::parse_group (std::string const &conf, +void colvar::cvc::parse_group (std::string const &conf, char const *group_key, cvm::atom_group &group, bool optional) @@ -120,7 +120,7 @@ void colvar::cvc::debug_gradients (cvm::atom_group &group) for (size_t id = 0; id < 3; id++) { // (re)read original positions group.read_positions(); - // change one coordinate + // change one coordinate group[ia].pos[id] += cvm::debug_gradients_step_size; // (re)do the fit (if defined) if (group.b_center || group.b_rotate) { diff --git a/lib/colvars/colvarcomp.h b/lib/colvars/colvarcomp.h index a8a06814f4..964a733964 100644 --- a/lib/colvars/colvarcomp.h +++ b/lib/colvars/colvarcomp.h @@ -1,6 +1,16 @@ #ifndef COLVARCOMP_H #define COLVARCOMP_H +// Declaration of colvar::cvc base class and derived ones. +// +// Future cvc's could be declared on additional header files. +// After the declaration of a new derived class, its metric +// functions must be reimplemented as well. +// If the new cvc has no symmetry or periodicity, +// this can be done straightforwardly by using the macro: +// simple_scalar_dist_functions (derived_class) + + #include #include @@ -40,7 +50,7 @@ /// 2. add a call to the parser in colvar.C, within the function colvar::colvar() \par /// 3. declare the class in colvarcomp.h \par /// 4. implement the class in one of the files colvarcomp_*.C -/// +/// ///
    /// The cvm::atom and cvm::atom_group classes are available to /// transparently communicate with the simulation program. However, @@ -58,7 +68,7 @@ public: std::string name; /// \brief Description of the type of collective variable - /// + /// /// Normally this string is set by the parent \link colvar \endlink /// object within its constructor, when all \link cvc \endlink /// objects are initialized; therefore the main "config string" @@ -94,7 +104,7 @@ public: /// \brief Within the constructor, make a group parse its own /// options from the provided configuration string - void parse_group (std::string const &conf, + void parse_group (std::string const &conf, char const *group_key, cvm::atom_group &group, bool optional = false); @@ -164,7 +174,7 @@ public: /// \brief Square distance between x1 and x2 (can be redefined to /// transparently implement constraints, symmetries and /// periodicities) - /// + /// /// colvar::cvc::dist2() and the related functions are /// declared as "const" functions, but not "static", because /// additional parameters defining the metrics (e.g. the @@ -182,7 +192,7 @@ public: /// to provide a gradient which is compatible with the constraint, /// i.e. already deprived of its component normal to the constraint /// hypersurface. - /// + /// /// Finally, another useful application, if you are performing very /// many operations with these functions, could be to override the /// \link colvarvalue \endlink member functions and access directly @@ -591,7 +601,7 @@ protected: std::vector ref_pos; /// Geometric center of the reference coordinates - cvm::rvector ref_pos_center; + cvm::atom_pos ref_pos_center; /// Eigenvector (of a normal or essential mode): will always have zero center std::vector eigenvec; @@ -691,7 +701,7 @@ protected: /// \brief Compute system force on first site only to avoid unwanted /// coupling to other colvars (see e.g. Ciccotti et al., 2005) bool b_1site_force; - + public: /// Initialize by parsing the configuration @@ -759,7 +769,7 @@ public: static cvm::real switching_function (cvm::real const &r0, int const &exp_num, int const &exp_den, cvm::atom &A1, cvm::atom &A2); - + template /// \brief Calculate a coordination number through the function /// (1-x**n)/(1-x**m), x = |(A1-A2)*(r0_vec)^-|1 \param r0_vec @@ -808,7 +818,7 @@ public: static cvm::real switching_function (cvm::real const &r0, int const &exp_num, int const &exp_den, cvm::atom &A1, cvm::atom &A2); - + virtual cvm::real dist2 (colvarvalue const &x1, colvarvalue const &x2) const; virtual colvarvalue dist2_lgrad (colvarvalue const &x1, @@ -822,7 +832,7 @@ public: /// \brief Colvar component: hydrogen bond, defined as the product of /// a colvar::coordnum and 1/2*(1-cos((180-ang)/ang_tol)) /// (colvarvalue::type_scalar type, range [0:1]) -class colvar::h_bond +class colvar::h_bond : public colvar::cvc { protected: @@ -887,7 +897,7 @@ public: // alpha_dihedrals(); // virtual inline ~alpha_dihedrals() {} // virtual void calc_value(); -// virtual void calc_gradients(); +// virtual void calc_gradients(); // virtual void apply_force (colvarvalue const &force); // virtual cvm::real dist2 (colvarvalue const &x1, // colvarvalue const &x2) const; @@ -921,7 +931,7 @@ protected: /// List of hydrogen bonds std::vector hb; - /// Contribution of the hb terms + /// Contribution of the hb terms cvm::real hb_coeff; public: @@ -1140,6 +1150,62 @@ public: }; + +// metrics functions for cvc implementations + +// simple definitions of the distance functions; these are useful only +// for optimization (the type check performed in the default +// colvarcomp functions is skipped) + +// definitions assuming the scalar type + +#define simple_scalar_dist_functions(TYPE) \ + \ + inline cvm::real colvar::TYPE::dist2 (colvarvalue const &x1, \ + colvarvalue const &x2) const \ + { \ + return (x1.real_value - x2.real_value)*(x1.real_value - x2.real_value); \ + } \ + \ + inline colvarvalue colvar::TYPE::dist2_lgrad (colvarvalue const &x1, \ + colvarvalue const &x2) const \ + { \ + return 2.0 * (x1.real_value - x2.real_value); \ + } \ + \ + inline colvarvalue colvar::TYPE::dist2_rgrad (colvarvalue const &x1, \ + colvarvalue const &x2) const \ + { \ + return this->dist2_lgrad (x2, x1); \ + } \ + \ + inline cvm::real colvar::TYPE::compare (colvarvalue const &x1, \ + colvarvalue const &x2) const \ + { \ + return this->dist2_lgrad (x1, x2); \ + } \ + \ + + simple_scalar_dist_functions (distance) + // NOTE: distance_z has explicit functions, see below + simple_scalar_dist_functions (distance_xy) + simple_scalar_dist_functions (distance_inv) + simple_scalar_dist_functions (angle) + simple_scalar_dist_functions (coordnum) + simple_scalar_dist_functions (selfcoordnum) + simple_scalar_dist_functions (h_bond) + simple_scalar_dist_functions (gyration) + simple_scalar_dist_functions (inertia) + simple_scalar_dist_functions (inertia_z) + simple_scalar_dist_functions (rmsd) + simple_scalar_dist_functions (orientation_angle) + simple_scalar_dist_functions (tilt) + simple_scalar_dist_functions (eigenvector) + // simple_scalar_dist_functions (alpha_dihedrals) + simple_scalar_dist_functions (alpha_angles) + simple_scalar_dist_functions (dihedPC) + + // metrics functions for cvc implementations with a periodicity inline cvm::real colvar::dihedral::dist2 (colvarvalue const &x1, @@ -1177,12 +1243,12 @@ inline void colvar::dihedral::wrap (colvarvalue &x) const if ((x.real_value - wrap_center) >= 180.0) { x.real_value -= 360.0; return; - } + } if ((x.real_value - wrap_center) < -180.0) { x.real_value += 360.0; return; - } + } return; } @@ -1222,68 +1288,16 @@ inline void colvar::spin_angle::wrap (colvarvalue &x) const if ((x.real_value - wrap_center) >= 180.0) { x.real_value -= 360.0; return; - } + } if ((x.real_value - wrap_center) < -180.0) { x.real_value += 360.0; return; - } + } return; } -// simple definitions of the distance functions; these are useful only -// for optimization (the type check performed in the default -// colvarcomp functions is skipped) - -// definitions assuming the scalar type - -#define simple_scalar_dist_functions(TYPE) \ - \ - inline cvm::real colvar::TYPE::dist2 (colvarvalue const &x1, \ - colvarvalue const &x2) const \ - { \ - return (x1.real_value - x2.real_value)*(x1.real_value - x2.real_value); \ - } \ - \ - inline colvarvalue colvar::TYPE::dist2_lgrad (colvarvalue const &x1, \ - colvarvalue const &x2) const \ - { \ - return 2.0 * (x1.real_value - x2.real_value); \ - } \ - \ - inline colvarvalue colvar::TYPE::dist2_rgrad (colvarvalue const &x1, \ - colvarvalue const &x2) const \ - { \ - return this->dist2_lgrad (x2, x1); \ - } \ - \ - inline cvm::real colvar::TYPE::compare (colvarvalue const &x1, \ - colvarvalue const &x2) const \ - { \ - return this->dist2_lgrad (x1, x2); \ - } \ - \ - - simple_scalar_dist_functions (distance) - // NOTE: distance_z has explicit functions, see below - simple_scalar_dist_functions (distance_xy) - simple_scalar_dist_functions (distance_inv) - simple_scalar_dist_functions (angle) - simple_scalar_dist_functions (coordnum) - simple_scalar_dist_functions (selfcoordnum) - simple_scalar_dist_functions (h_bond) - simple_scalar_dist_functions (gyration) - simple_scalar_dist_functions (inertia) - simple_scalar_dist_functions (inertia_z) - simple_scalar_dist_functions (rmsd) - simple_scalar_dist_functions (orientation_angle) - simple_scalar_dist_functions (tilt) - simple_scalar_dist_functions (eigenvector) - // simple_scalar_dist_functions (alpha_dihedrals) - simple_scalar_dist_functions (alpha_angles) - simple_scalar_dist_functions (dihedPC) - // Projected distance // Differences should always be wrapped around 0 (ignoring wrap_center) @@ -1422,9 +1436,6 @@ inline cvm::real colvar::orientation::compare (colvarvalue const &x1, } - - - #endif diff --git a/lib/colvars/colvarcomp_angles.cpp b/lib/colvars/colvarcomp_angles.cpp index 414c01df7e..bc2aee0598 100644 --- a/lib/colvars/colvarcomp_angles.cpp +++ b/lib/colvars/colvarcomp_angles.cpp @@ -77,7 +77,7 @@ void colvar::angle::calc_gradients() { cvm::real const cos_theta = (r21*r23)/(r21l*r23l); cvm::real const dxdcos = -1.0 / std::sqrt (1.0 - cos_theta*cos_theta); - + dxdr1 = (180.0/PI) * dxdcos * (1.0/r21l) * ( r23/r23l + (-1.0) * cos_theta * r21/r21l ); @@ -246,7 +246,7 @@ void colvar::dihedral::calc_gradients() cvm::real rA = A.norm(); cvm::rvector B = cvm::rvector::outer (r23, r34); cvm::real rB = B.norm(); - cvm::rvector C = cvm::rvector::outer (r23, A); + cvm::rvector C = cvm::rvector::outer (r23, A); cvm::real rC = C.norm(); cvm::real const cos_phi = (A*B)/(rA*rB); diff --git a/lib/colvars/colvarcomp_coordnums.cpp b/lib/colvars/colvarcomp_coordnums.cpp index a33f95e072..d08a5e505c 100644 --- a/lib/colvars/colvarcomp_coordnums.cpp +++ b/lib/colvars/colvarcomp_coordnums.cpp @@ -72,11 +72,14 @@ cvm::real colvar::coordnum::switching_function (cvm::rvector const &r0_vec, colvar::coordnum::coordnum (std::string const &conf) : distance (conf), b_anisotropic (false), b_group2_center_only (false) -{ +{ function_type = "coordnum"; x.type (colvarvalue::type_scalar); // group1 and group2 are already initialized by distance() + if (group1.b_dummy) + cvm::fatal_error ("Error: only group2 is allowed to be a dummy atom\n"); + // need to specify this explicitly because the distance() constructor // has set it to true @@ -105,7 +108,7 @@ colvar::coordnum::coordnum (std::string const &conf) cvm::fatal_error ("Error: odd exponents provided, can only use even ones.\n"); } - get_keyval (conf, "group2CenterOnly", b_group2_center_only, false); + get_keyval (conf, "group2CenterOnly", b_group2_center_only, group2.b_dummy); } @@ -121,18 +124,10 @@ void colvar::coordnum::calc_value() { x.real_value = 0.0; - // these are necessary: for each atom, gradients are summed together - // by multiple calls to switching_function() - group1.reset_atoms_data(); - group2.reset_atoms_data(); - - group1.read_positions(); - group2.read_positions(); - if (b_group2_center_only) { // create a fake atom to hold the group2 com coordinates - cvm::atom group2_com_atom (group2[0]); + cvm::atom group2_com_atom; group2_com_atom.pos = group2.center_of_mass(); if (b_anisotropic) { @@ -165,9 +160,10 @@ void colvar::coordnum::calc_gradients() if (b_group2_center_only) { // create a fake atom to hold the group2 com coordinates - cvm::atom group2_com_atom (group2[0]); + cvm::atom group2_com_atom; group2_com_atom.pos = group2.center_of_mass(); + if (b_anisotropic) { for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) switching_function (r0_vec, en, ed, *ai1, group2_com_atom); @@ -288,14 +284,6 @@ colvar::h_bond::~h_bond() void colvar::h_bond::calc_value() { - // this is necessary, because switching_function() will sum the new - // gradient to the current one - acceptor.reset_data(); - donor.reset_data(); - - acceptor.read_position(); - donor.read_position(); - x.real_value = colvar::coordnum::switching_function (r0, en, ed, acceptor, donor); } @@ -315,40 +303,11 @@ void colvar::h_bond::apply_force (colvarvalue const &force) } -// Self-coordination number for a group - -template -cvm::real colvar::selfcoordnum::switching_function (cvm::real const &r0, - int const &en, - int const &ed, - cvm::atom &A1, - cvm::atom &A2) -{ - cvm::rvector const diff = cvm::position_distance (A1.pos, A2.pos); - cvm::real const l2 = diff.norm2()/(r0*r0); - - // Assume en and ed are even integers, and avoid sqrt in the following - int const en2 = en/2; - int const ed2 = ed/2; - - cvm::real const xn = std::pow (l2, en2); - cvm::real const xd = std::pow (l2, ed2); - cvm::real const func = (1.0-xn)/(1.0-xd); - - if (calculate_gradients) { - cvm::real const dFdl2 = (1.0/(1.0-xd))*(en2*(xn/l2) - func*ed2*(xd/l2))*(-1.0); - cvm::rvector const dl2dx = (2.0/(r0*r0))*diff; - A1.grad += (-1.0)*dFdl2*dl2dx; - A2.grad += dFdl2*dl2dx; - } - - return func; -} colvar::selfcoordnum::selfcoordnum (std::string const &conf) : distance (conf, false) -{ +{ function_type = "selfcoordnum"; x.type (colvarvalue::type_scalar); @@ -379,13 +338,9 @@ void colvar::selfcoordnum::calc_value() { x.real_value = 0.0; - // for each atom, gradients are summed by multiple calls to switching_function() - group1.reset_atoms_data(); - group1.read_positions(); - for (size_t i = 0; i < group1.size() - 1; i++) for (size_t j = i + 1; j < group1.size(); j++) - x.real_value += switching_function (r0, en, ed, group1[i], group1[j]); + x.real_value += colvar::coordnum::switching_function (r0, en, ed, group1[i], group1[j]); } @@ -393,7 +348,7 @@ void colvar::selfcoordnum::calc_gradients() { for (size_t i = 0; i < group1.size() - 1; i++) for (size_t j = i + 1; j < group1.size(); j++) - switching_function (r0, en, ed, group1[i], group1[j]); + colvar::coordnum::switching_function (r0, en, ed, group1[i], group1[j]); } void colvar::selfcoordnum::apply_force (colvarvalue const &force) diff --git a/lib/colvars/colvarcomp_distances.cpp b/lib/colvars/colvarcomp_distances.cpp index a371cff605..339ba95f7d 100644 --- a/lib/colvars/colvarcomp_distances.cpp +++ b/lib/colvars/colvarcomp_distances.cpp @@ -113,7 +113,7 @@ void colvar::distance_vec::calc_value() } void colvar::distance_vec::calc_gradients() -{ +{ // gradients are not stored: a 3x3 matrix for each atom would be // needed to store just the identity matrix } @@ -140,7 +140,7 @@ colvar::distance_z::distance_z (std::string const &conf) // TODO detect PBC from MD engine (in simple cases) // and then update period in real time if (period != 0.0) - b_periodic = true; + b_periodic = true; if ((wrap_center != 0.0) && (period == 0.0)) { cvm::fatal_error ("Error: wrapAround was defined in a distanceZ component," @@ -153,7 +153,7 @@ colvar::distance_z::distance_z (std::string const &conf) atom_groups.push_back (&ref1); // this group is optional parse_group (conf, "ref2", ref2, true); - + if (ref2.size()) { atom_groups.push_back (&ref2); cvm::log ("Using axis joining the centers of mass of groups \"ref\" and \"ref2\""); @@ -522,7 +522,7 @@ colvar::gyration::gyration (std::string const &conf) cvm::log ("WARNING: explicit fitting parameters were provided for atom group \"atoms\"."); } else { atoms.b_center = true; - atoms.ref_pos.assign (1, cvm::rvector (0.0, 0.0, 0.0)); + atoms.ref_pos.assign (1, cvm::atom_pos (0.0, 0.0, 0.0)); } x.type (colvarvalue::type_scalar); @@ -700,7 +700,7 @@ colvar::rmsd::rmsd (std::string const &conf) parse_group (conf, "atoms", atoms); atom_groups.push_back (&atoms); - if (atoms.b_dummy) + if (atoms.b_dummy) cvm::fatal_error ("Error: \"atoms\" cannot be a dummy atom."); if (atoms.ref_pos_group != NULL) { @@ -732,7 +732,7 @@ colvar::rmsd::rmsd (std::string const &conf) std::string ref_pos_col; double ref_pos_col_value; - + if (get_keyval (conf, "refPositionsCol", ref_pos_col, std::string (""))) { // if provided, use PDB column to select coordinates bool found = get_keyval (conf, "refPositionsColValue", ref_pos_col_value, 0.0); @@ -771,7 +771,7 @@ colvar::rmsd::rmsd (std::string const &conf) } } - + void colvar::rmsd::calc_value() { // rotational-translational fit is handled by the atom group @@ -813,9 +813,9 @@ void colvar::rmsd::calc_force_invgrads() { atoms.read_system_forces(); ft.real_value = 0.0; - + // Note: gradient square norm is 1/N_atoms - + for (size_t ia = 0; ia < atoms.size(); ia++) { ft.real_value += atoms[ia].grad * atoms[ia].system_force; } @@ -832,7 +832,7 @@ void colvar::rmsd::calc_Jacobian_derivative() // gradient of the rotation matrix cvm::matrix2d grad_rot_mat; - // gradients of products of 2 quaternion components + // gradients of products of 2 quaternion components cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23; for (size_t ia = 0; ia < atoms.size(); ia++) { @@ -850,15 +850,15 @@ void colvar::rmsd::calc_Jacobian_derivative() g23 = (atoms.rot.q)[2]*dq[3] + (atoms.rot.q)[3]*dq[2]; // Gradient of the rotation matrix wrt current Cartesian position - grad_rot_mat[0][0] = -2.0 * (g22 + g33); - grad_rot_mat[1][0] = 2.0 * (g12 + g03); - grad_rot_mat[2][0] = 2.0 * (g13 - g02); - grad_rot_mat[0][1] = 2.0 * (g12 - g03); - grad_rot_mat[1][1] = -2.0 * (g11 + g33); - grad_rot_mat[2][1] = 2.0 * (g01 + g23); - grad_rot_mat[0][2] = 2.0 * (g02 + g13); - grad_rot_mat[1][2] = 2.0 * (g23 - g01); - grad_rot_mat[2][2] = -2.0 * (g11 + g22); + grad_rot_mat[0][0] = -2.0 * (g22 + g33); + grad_rot_mat[1][0] = 2.0 * (g12 + g03); + grad_rot_mat[2][0] = 2.0 * (g13 - g02); + grad_rot_mat[0][1] = 2.0 * (g12 - g03); + grad_rot_mat[1][1] = -2.0 * (g11 + g33); + grad_rot_mat[2][1] = 2.0 * (g01 + g23); + grad_rot_mat[0][2] = 2.0 * (g02 + g13); + grad_rot_mat[1][2] = 2.0 * (g23 - g01); + grad_rot_mat[2][2] = -2.0 * (g11 + g22); cvm::atom_pos &y = ref_pos[ia]; @@ -991,7 +991,7 @@ colvar::eigenvector::eigenvector (std::string const &conf) "and eigenvector must be defined.\n"); } - cvm::rvector eig_center (0.0, 0.0, 0.0); + cvm::atom_pos eig_center (0.0, 0.0, 0.0); for (size_t i = 0; i < atoms.size(); i++) { eig_center += eigenvec[i]; } @@ -1053,7 +1053,7 @@ colvar::eigenvector::eigenvector (std::string const &conf) } } - + void colvar::eigenvector::calc_value() { x.real_value = 0.0; @@ -1087,7 +1087,7 @@ void colvar::eigenvector::calc_force_invgrads() { atoms.read_system_forces(); ft.real_value = 0.0; - + for (size_t ia = 0; ia < atoms.size(); ia++) { ft.real_value += eigenvec_invnorm2 * atoms[ia].grad * atoms[ia].system_force; @@ -1101,10 +1101,10 @@ void colvar::eigenvector::calc_Jacobian_derivative() cvm::matrix2d grad_rot_mat; cvm::quaternion &quat0 = atoms.rot.q; - // gradients of products of 2 quaternion components + // gradients of products of 2 quaternion components cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23; - cvm::atom_pos x_relative; + cvm::atom_pos x_relative; cvm::real sum = 0.0; for (size_t ia = 0; ia < atoms.size(); ia++) { @@ -1126,15 +1126,15 @@ void colvar::eigenvector::calc_Jacobian_derivative() // Gradient of the inverse rotation matrix wrt current Cartesian position // (transpose of the gradient of the direct rotation) - grad_rot_mat[0][0] = -2.0 * (g22 + g33); - grad_rot_mat[0][1] = 2.0 * (g12 + g03); - grad_rot_mat[0][2] = 2.0 * (g13 - g02); - grad_rot_mat[1][0] = 2.0 * (g12 - g03); - grad_rot_mat[1][1] = -2.0 * (g11 + g33); - grad_rot_mat[1][2] = 2.0 * (g01 + g23); - grad_rot_mat[2][0] = 2.0 * (g02 + g13); - grad_rot_mat[2][1] = 2.0 * (g23 - g01); - grad_rot_mat[2][2] = -2.0 * (g11 + g22); + grad_rot_mat[0][0] = -2.0 * (g22 + g33); + grad_rot_mat[0][1] = 2.0 * (g12 + g03); + grad_rot_mat[0][2] = 2.0 * (g13 - g02); + grad_rot_mat[1][0] = 2.0 * (g12 - g03); + grad_rot_mat[1][1] = -2.0 * (g11 + g33); + grad_rot_mat[1][2] = 2.0 * (g01 + g23); + grad_rot_mat[2][0] = 2.0 * (g02 + g13); + grad_rot_mat[2][1] = 2.0 * (g23 - g01); + grad_rot_mat[2][2] = -2.0 * (g11 + g22); for (size_t i = 0; i < 3; i++) { for (size_t j = 0; j < 3; j++) { @@ -1143,7 +1143,7 @@ void colvar::eigenvector::calc_Jacobian_derivative() } } - jd.real_value = sum * std::sqrt (eigenvec_invnorm2); + jd.real_value = sum * std::sqrt (eigenvec_invnorm2); } diff --git a/lib/colvars/colvarcomp_protein.cpp b/lib/colvars/colvarcomp_protein.cpp index b30d50e1c1..1750782b7a 100644 --- a/lib/colvars/colvarcomp_protein.cpp +++ b/lib/colvars/colvarcomp_protein.cpp @@ -122,7 +122,7 @@ void colvar::alpha_angles::calc_value() if (theta.size()) { - cvm::real const theta_norm = + cvm::real const theta_norm = (1.0-hb_coeff) / cvm::real (theta.size()); for (size_t i = 0; i < theta.size(); i++) { @@ -162,7 +162,7 @@ void colvar::alpha_angles::calc_value() void colvar::alpha_angles::calc_gradients() { - for (size_t i = 0; i < theta.size(); i++) + for (size_t i = 0; i < theta.size(); i++) (theta[i])->calc_gradients(); for (size_t i = 0; i < hb.size(); i++) @@ -175,9 +175,9 @@ void colvar::alpha_angles::apply_force (colvarvalue const &force) if (theta.size()) { - cvm::real const theta_norm = + cvm::real const theta_norm = (1.0-hb_coeff) / cvm::real (theta.size()); - + for (size_t i = 0; i < theta.size(); i++) { cvm::real const t = ((theta[i])->value().real_value-theta_ref)/theta_tol; @@ -185,10 +185,10 @@ void colvar::alpha_angles::apply_force (colvarvalue const &force) (1.0 - std::pow (t, (int) 4)) ); cvm::real const dfdt = - 1.0/(1.0 - std::pow (t, (int) 4)) * + 1.0/(1.0 - std::pow (t, (int) 4)) * ( (-2.0 * t) + (-1.0*f)*(-4.0 * std::pow (t, (int) 3)) ); - (theta[i])->apply_force (theta_norm * + (theta[i])->apply_force (theta_norm * dfdt * (1.0/theta_tol) * force.real_value ); } @@ -216,7 +216,7 @@ void colvar::alpha_angles::apply_force (colvarvalue const &force) // of this cvc (alpha) // This is true of all cvcs with sub-cvcs, and those // that do not calculate explicit gradients - // SO: we need a flag giving the availability of + // SO: we need a flag giving the availability of // atomic gradients colvar::dihedPC::dihedPC (std::string const &conf) : cvc (conf) @@ -260,7 +260,7 @@ colvar::dihedPC::dihedPC (std::string const &conf) std::string vecFileName; int vecNumber; if (get_keyval (conf, "vectorFile", vecFileName, vecFileName)) { - get_keyval (conf, "vectorNumber", vecNumber, 0); + get_keyval (conf, "vectorNumber", vecNumber, 0); if (vecNumber < 1) cvm::fatal_error ("A positive value of vectorNumber is required."); diff --git a/lib/colvars/colvarcomp_rotations.cpp b/lib/colvars/colvarcomp_rotations.cpp index 50c9e0da2e..b77c563878 100644 --- a/lib/colvars/colvarcomp_rotations.cpp +++ b/lib/colvars/colvarcomp_rotations.cpp @@ -74,7 +74,7 @@ colvar::orientation::orientation (std::string const &conf) } - + colvar::orientation::orientation() : cvc () { @@ -160,7 +160,7 @@ void colvar::orientation_angle::calc_value() void colvar::orientation_angle::calc_gradients() { cvm::real const dxdq0 = - ( ((rot.q).q0 * (rot.q).q0 < 1.0) ? + ( ((rot.q).q0 * (rot.q).q0 < 1.0) ? ((180.0 / PI) * (-2.0) / std::sqrt (1.0 - ((rot.q).q0 * (rot.q).q0))) : 0.0 ); diff --git a/lib/colvars/colvargrid.cpp b/lib/colvars/colvargrid.cpp index c6099bcb8f..a300b91340 100644 --- a/lib/colvars/colvargrid.cpp +++ b/lib/colvars/colvargrid.cpp @@ -141,7 +141,7 @@ void colvar_grid_gradient::write_1D_integral (std::ostream &os) { cvm::real bin, min, integral; std::vector int_vals; - + os << "# xi A(xi)\n"; if ( cv.size() != 1 ) { @@ -162,7 +162,7 @@ void colvar_grid_gradient::write_1D_integral (std::ostream &os) } for (std::vector ix = new_index(); index_ok (ix); incr (ix), bin += 1.0 ) { - + if (samples) { size_t const samples_here = samples->value (ix); if (samples_here) diff --git a/lib/colvars/colvargrid.h b/lib/colvars/colvargrid.h index 80a7b70b16..ccaf046039 100644 --- a/lib/colvars/colvargrid.h +++ b/lib/colvars/colvargrid.h @@ -266,11 +266,11 @@ public: } - { + { cvm::real nbins = ( upper_boundaries[i].real_value - lower_boundaries[i].real_value ) / widths[i]; int nbins_round = (int)(nbins+0.5); - + if (std::fabs (nbins - cvm::real (nbins_round)) > 1.0E-10) { cvm::log ("Warning: grid interval ("+ cvm::to_str (lower_boundaries[i], cvm::cv_width, cvm::cv_prec)+" - "+ @@ -284,7 +284,7 @@ public: if (cvm::debug()) cvm::log ("Number of points is "+cvm::to_str ((int) nbins_round)+ " for the colvar no. "+cvm::to_str (i+1)+".\n"); - + nx_i.push_back (nbins_round); } @@ -336,7 +336,7 @@ public: { return lower_boundaries[i].real_value + widths[i] * (0.5 + i_bin); } - + /// \brief Same as the standard version, but uses different parameters inline colvarvalue bin_to_value_scalar (int const &i_bin, colvarvalue const &new_offset, @@ -349,7 +349,7 @@ public: inline void set_value (std::vector const &ix, T const &t, size_t const &imult = 0) - { + { data[this->address (ix)+imult] = t; has_data = true; } @@ -367,7 +367,7 @@ public: /// \brief Add a constant to all elements (fast loop) inline void add_constant (T const &t) { - for (size_t i = 0; i < nt; i++) + for (size_t i = 0; i < nt; i++) data[i] += t; has_data = true; } @@ -375,11 +375,11 @@ public: /// \brief Multiply all elements by a scalar constant (fast loop) inline void multiply_constant (cvm::real const &a) { - for (size_t i = 0; i < nt; i++) + for (size_t i = 0; i < nt; i++) data[i] *= a; } - + /// \brief Get the bin indices corresponding to the provided values of /// the colvars inline std::vector const get_colvars_index (std::vector const &values) const @@ -432,7 +432,7 @@ public: /// \brief Add data from another grid of the same type - /// + /// /// Note: this function maps other_grid inside this one regardless /// of whether it fits or not. void map_grid (colvar_grid const &other_grid) @@ -482,11 +482,11 @@ public: if (other_grid.multiplicity() != this->multiplicity()) cvm::fatal_error ("Error: trying to sum togetehr two grids with values of " "different multiplicity.\n"); - if (scale_factor != 1.0) + if (scale_factor != 1.0) for (size_t i = 0; i < data.size(); i++) { data[i] += scale_factor * other_grid.data[i]; } - else + else // skip multiplication if possible for (size_t i = 0; i < data.size(); i++) { data[i] += other_grid.data[i]; @@ -571,30 +571,30 @@ public: std::ostream & write_params (std::ostream &os) { os << "grid_parameters {\n n_colvars " << nd << "\n"; - + os << " lower_boundaries "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << lower_boundaries[i]; os << "\n"; os << " upper_boundaries "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << upper_boundaries[i]; os << "\n"; os << " widths "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << widths[i]; os << "\n"; os << " sizes "; - for (size_t i = 0; i < nd; i++) + for (size_t i = 0; i < nd; i++) os << " " << nx[i]; os << "\n"; os << "}\n"; return os; - } + } bool parse_params (std::string const &conf) @@ -648,9 +648,9 @@ public: { for (size_t i = 0; i < nd; i++) { if ( (std::sqrt (cv[i]->dist2 (cv[i]->lower_boundary, - lower_boundaries[i])) > 1.0E-10) || + lower_boundaries[i])) > 1.0E-10) || (std::sqrt (cv[i]->dist2 (cv[i]->upper_boundary, - upper_boundaries[i])) > 1.0E-10) || + upper_boundaries[i])) > 1.0E-10) || (std::sqrt (cv[i]->dist2 (cv[i]->width, widths[i])) > 1.0E-10) ) { cvm::fatal_error ("Error: restart information for a grid is " @@ -669,11 +669,11 @@ public: // matter: boundaries should be EXACTLY the same (otherwise, // map_grid() should be used) if ( (std::fabs (other_grid.lower_boundaries[i] - - lower_boundaries[i]) > 1.0E-10) || + lower_boundaries[i]) > 1.0E-10) || (std::fabs (other_grid.upper_boundaries[i] - - upper_boundaries[i]) > 1.0E-10) || + upper_boundaries[i]) > 1.0E-10) || (std::fabs (other_grid.widths[i] - - widths[i]) > 1.0E-10) || + widths[i]) > 1.0E-10) || (data.size() != other_grid.data.size()) ) { cvm::fatal_error ("Error: inconsistency between " "two grids that are supposed to be equal, " @@ -681,7 +681,7 @@ public: } } } - + /// \brief Write the grid data without labels, as they are /// represented in memory @@ -714,7 +714,7 @@ public: std::istream & read_raw (std::istream &is) { size_t const start_pos = is.tellg(); - + for (std::vector ix = new_index(); index_ok (ix); incr (ix)) { for (size_t imult = 0; imult < mult; imult++) { T new_value; @@ -845,7 +845,7 @@ std::istream & read_multicol (std::istream &is, bool add = false) if ( !(is >> x) ) end_of_file = true; bin[i] = value_to_bin_scalar (x, i); } - if (end_of_file) break; + if (end_of_file) break; for (size_t imult = 0; imult < mult; imult++) { is >> new_value[imult]; @@ -904,7 +904,7 @@ public: ++(data[this->address (ix)]); } - /// \brief Get the binned count indexed by ix from the newly read data + /// \brief Get the binned count indexed by ix from the newly read data inline size_t const & new_count (std::vector const &ix, size_t const &imult = 0) { @@ -993,7 +993,7 @@ public: A0 += data[address (ix)]; grad[n] = 0.5 * (A1 - A0) / widths[n]; } - return grad; + return grad; } /// \brief Return the value of the function at ix divided by its diff --git a/lib/colvars/colvarmodule.cpp b/lib/colvars/colvarmodule.cpp index 98e05ca00d..03fe18da94 100644 --- a/lib/colvars/colvarmodule.cpp +++ b/lib/colvars/colvarmodule.cpp @@ -9,14 +9,14 @@ colvarmodule::colvarmodule (char const *config_filename, colvarproxy *proxy_in) -{ +{ // pointer to the proxy object if (proxy == NULL) { proxy = proxy_in; parse = new colvarparse(); } else { - cvm::fatal_error ("Error: trying to allocate twice the collective " - "variable module.\n"); + cvm::fatal_error ("Error: trying to allocate the collective " + "variable module twice.\n"); } cvm::log (cvm::line_marker); @@ -44,6 +44,11 @@ colvarmodule::colvarmodule (char const *config_filename, config_s.close(); } + std::string index_file_name; + if (parse->get_keyval (conf, "indexFile", index_file_name)) { + read_index_file (index_file_name.c_str()); + } + parse->get_keyval (conf, "analysis", b_analysis, false); parse->get_keyval (conf, "debugGradientsStepSize", debug_gradients_step_size, 1.0e-07, @@ -80,12 +85,10 @@ colvarmodule::colvarmodule (char const *config_filename, std::string (output_prefix+".colvars.state") : std::string ("colvars.state"))+"\".\n"); - cv_traj_name = + cv_traj_name = (output_prefix.size() ? std::string (output_prefix+".colvars.traj") : std::string ("colvars.traj")); - cvm::log ("The trajectory file will be \""+ - cv_traj_name+"\".\n"); if (cv_traj_freq) { // open trajectory file @@ -94,6 +97,8 @@ colvarmodule::colvarmodule (char const *config_filename, "\".\n"); cv_traj_os.open (cv_traj_name.c_str(), std::ios::app); } else { + cvm::log ("Writing to colvar trajectory file \""+cv_traj_name+ + "\".\n"); proxy->backup_file (cv_traj_name.c_str()); cv_traj_os.open (cv_traj_name.c_str(), std::ios::out); } @@ -135,7 +140,7 @@ colvarmodule::colvarmodule (char const *config_filename, } -std::istream & colvarmodule::read_restart (std::istream &is) +std::istream & colvarmodule::read_restart (std::istream &is) { { // read global restart information @@ -192,7 +197,7 @@ void colvarmodule::init_colvars (std::string const &conf) (colvars.back())->check_keywords (colvar_conf, "colvar"); cvm::decrease_depth(); } else { - cvm::log ("Warning: \"colvar\" keyword found without any configuration.\n"); + cvm::log ("Warning: \"colvar\" keyword found without any configuration.\n"); } colvar_conf = ""; } @@ -347,13 +352,13 @@ void colvarmodule::calc() { } // calculate collective variables and their gradients - if (cvm::debug()) + if (cvm::debug()) cvm::log ("Calculating collective variables.\n"); cvm::increase_depth(); for (std::vector::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->calc(); + (*cvi)->calc(); } cvm::decrease_depth(); @@ -365,7 +370,7 @@ void colvarmodule::calc() { for (std::vector::iterator bi = biases.begin(); bi != biases.end(); bi++) { - total_bias_energy += (*bi)->update(); + total_bias_energy += (*bi)->update(); } cvm::decrease_depth(); @@ -376,12 +381,12 @@ void colvarmodule::calc() { for (std::vector::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->reset_bias_force(); + (*cvi)->reset_bias_force(); } for (std::vector::iterator bi = biases.begin(); bi != biases.end(); bi++) { - (*bi)->communicate_forces(); + (*bi)->communicate_forces(); } cvm::decrease_depth(); @@ -393,12 +398,12 @@ void colvarmodule::calc() { for (std::vector::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { - (*cvi)->analyse(); + (*cvi)->analyse(); } for (std::vector::iterator bi = biases.begin(); bi != biases.end(); bi++) { - (*bi)->analyse(); + (*bi)->analyse(); } cvm::decrease_depth(); } @@ -426,7 +431,7 @@ void colvarmodule::calc() { cvi != colvars.end(); cvi++) { if ((*cvi)->tasks[colvar::task_gradients]) - (*cvi)->communicate_forces(); + (*cvi)->communicate_forces(); } cvm::decrease_depth(); @@ -442,7 +447,7 @@ void colvarmodule::calc() { if (!write_restart (restart_out_os)) cvm::fatal_error ("Error: in writing restart file.\n"); restart_out_os.close(); - } + } } // write trajectory file, if needed @@ -471,15 +476,20 @@ void colvarmodule::calc() { if ((cvm::step_absolute() % (cv_traj_freq * 1000)) == 0 || cvm::step_relative() == 0) { cv_traj_os << "# " << cvm::wrap_string ("step", cvm::it_width-2) << " "; - if (cvm::debug()) + if (cvm::debug()) cv_traj_os.flush(); for (std::vector::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { (*cvi)->write_traj_label (cv_traj_os); } + for (std::vector::iterator bi = biases.begin(); + bi != biases.end(); + bi++) { + (*bi)->write_traj_label (cv_traj_os); + } cv_traj_os << "\n"; - if (cvm::debug()) + if (cvm::debug()) cv_traj_os.flush(); } cvm::decrease_depth(); @@ -494,13 +504,18 @@ void colvarmodule::calc() { cvi++) { (*cvi)->write_traj (cv_traj_os); } + for (std::vector::iterator bi = biases.begin(); + bi != biases.end(); + bi++) { + (*bi)->write_traj (cv_traj_os); + } cv_traj_os << "\n"; if (cvm::debug()) cv_traj_os.flush(); } cvm::decrease_depth(); - if (restart_out_freq) { + if (restart_out_freq) { // flush the trajectory file if we are at the restart frequency if ( (cvm::step_relative() > 0) && ((cvm::step_absolute() % restart_out_freq) == 0) ) { @@ -527,7 +542,7 @@ void colvarmodule::analyze() cvi != colvars.end(); cvi++) { cvm::increase_depth(); - (*cvi)->analyse(); + (*cvi)->analyse(); cvm::decrease_depth(); } @@ -536,12 +551,20 @@ void colvarmodule::analyze() bi != biases.end(); bi++) { cvm::increase_depth(); - (*bi)->analyse(); + (*bi)->analyse(); cvm::decrease_depth(); } } +void colvarmodule::setup() +{ + // loop over all components of all colvars to reset masses of all groups + for (std::vector::iterator cvi = colvars.begin(); + cvi != colvars.end(); cvi++) { + (*cvi)->setup(); + } +} colvarmodule::~colvarmodule() { @@ -565,7 +588,7 @@ colvarmodule::~colvarmodule() delete parse; proxy = NULL; -} +} void colvarmodule::write_output_files() @@ -590,7 +613,7 @@ void colvarmodule::write_output_files() (*cvi)->write_output_files(); } cvm::decrease_depth(); - + // do not close to avoid problems with multiple NAMD runs cv_traj_os.flush(); } @@ -631,7 +654,7 @@ bool colvarmodule::read_traj (char const *traj_filename, continue; - } else { + } else { if ((it % 1000) == 0) std::cerr << "Reading from trajectory, step = " << it @@ -721,6 +744,55 @@ void cvm::exit (std::string const &message) } +void cvm::read_index_file (char const *filename) +{ + std::ifstream is (filename); + if (!is.good()) + fatal_error ("Error: in opening index file \""+ + std::string (filename)+"\".\n"); + // std::list::iterator names_i = cvm::index_group_names.begin(); + // std::list >::iterator lists_i = cvm::index_groups.begin(); + while (is.good()) { + char open, close; + std::string group_name; + if ( (is >> open) && (open == '[') && + (is >> group_name) && + (is >> close) && (close == ']') ) { + cvm::index_group_names.push_back (group_name); + cvm::index_groups.push_back (std::vector ()); + } else { + cvm::fatal_error ("Error: in parsing index file \""+ + std::string (filename)+"\".\n"); + } + + int atom_number = 1; + size_t pos = is.tellg(); + while ( (is >> atom_number) && (atom_number > 0) ) { + (cvm::index_groups.back()).push_back (atom_number); + pos = is.tellg(); + } + is.clear(); + is.seekg (pos, std::ios::beg); + std::string delim; + if ( (is >> delim) && (delim == "[") ) { + // new group + is.clear(); + is.seekg (pos, std::ios::beg); + } else { + break; + } + } + + cvm::log ("The following index groups were read from the index file \""+ + std::string (filename)+"\":\n"); + std::list::iterator names_i = cvm::index_group_names.begin(); + std::list >::iterator lists_i = cvm::index_groups.begin(); + for ( ; names_i != cvm::index_group_names.end() ; names_i++, lists_i++) { + cvm::log (" "+(*names_i)+" ("+cvm::to_str (lists_i->size())+" atoms).\n"); + } + +} + // static pointers std::vector colvarmodule::colvars; @@ -741,6 +813,8 @@ size_t colvarmodule::cv_traj_freq = 0; size_t colvarmodule::depth = 0; bool colvarmodule::b_analysis = false; cvm::real colvarmodule::rotation::crossing_threshold = 1.0E-04; +std::list colvarmodule::index_group_names; +std::list > colvarmodule::index_groups; // file name prefixes @@ -827,7 +901,7 @@ std::istream & operator >> (std::istream &is, colvarmodule::quaternion &q) std::string ("euler")) ) { // parse the Euler angles - + char sep; cvm::real phi, theta, psi; if ( !(is >> sep) || !(sep == '(') || @@ -846,7 +920,7 @@ std::istream & operator >> (std::istream &is, colvarmodule::quaternion &q) // parse the quaternion components - is.seekg (start_pos, std::ios::beg); + is.seekg (start_pos, std::ios::beg); char sep; if ( !(is >> sep) || !(sep == '(') || !(is >> q.q0) || !(is >> sep) || !(sep == ',') || @@ -932,15 +1006,13 @@ cvm::quaternion::position_derivative_inner (cvm::rvector const &pos, + + // Calculate the optimal rotation between two groups, and implement it // as a quaternion. The method is the one documented in: Coutsias EA, // Seok C, Dill KA. Using quaternions to calculate RMSD. J Comput // Chem. 25(15):1849-57 (2004) DOI: 10.1002/jcc.20110 PubMed: 15376254 - - - - void colvarmodule::rotation::build_matrix (std::vector const &pos1, std::vector const &pos2, matrix2d &S) @@ -959,7 +1031,7 @@ void colvarmodule::rotation::build_matrix (std::vector const &pos C.zx() += pos1[i].z * pos2[i].x; C.zy() += pos1[i].z * pos2[i].y; C.zz() += pos1[i].z * pos2[i].z; - } + } // build the "overlap" matrix, whose eigenvectors are stationary // points of the RMSD in the space of rotations @@ -1068,11 +1140,12 @@ void colvarmodule::rotation::calc_optimal_rotation if (q_old.norm2() > 0.0) { q.match (q_old); if (q_old.inner (q) < (1.0 - crossing_threshold)) { - cvm::log ("Warning: discontinuous rotation!\n"); + cvm::log ("Warning: one molecular orientation has changed by more than "+ + cvm::to_str (crossing_threshold)+": discontinuous rotation ?\n"); } } q_old = q; - + if (cvm::debug()) { if (b_debug_gradients) { cvm::log ("L0 = "+cvm::to_str (L0, cvm::cv_width, cvm::cv_prec)+ @@ -1143,8 +1216,8 @@ void colvarmodule::rotation::calc_optimal_rotation for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { dq0_1[p] += - (Q1[i] * ds_1[i][j] * Q0[j]) / (L0-L1) * Q1[p] + - (Q2[i] * ds_1[i][j] * Q0[j]) / (L0-L2) * Q2[p] + + (Q1[i] * ds_1[i][j] * Q0[j]) / (L0-L1) * Q1[p] + + (Q2[i] * ds_1[i][j] * Q0[j]) / (L0-L2) * Q2[p] + (Q3[i] * ds_1[i][j] * Q0[j]) / (L0-L3) * Q3[p]; } } @@ -1193,15 +1266,15 @@ void colvarmodule::rotation::calc_optimal_rotation for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { dq0_2[p] += - (Q1[i] * ds_2[i][j] * Q0[j]) / (L0-L1) * Q1[p] + - (Q2[i] * ds_2[i][j] * Q0[j]) / (L0-L2) * Q2[p] + + (Q1[i] * ds_2[i][j] * Q0[j]) / (L0-L1) * Q1[p] + + (Q2[i] * ds_2[i][j] * Q0[j]) / (L0-L2) * Q2[p] + (Q3[i] * ds_2[i][j] * Q0[j]) / (L0-L3) * Q3[p]; } } } if (cvm::debug()) { - + if (b_debug_gradients) { matrix2d S_new; @@ -1216,7 +1289,7 @@ void colvarmodule::rotation::calc_optimal_rotation // diagonalize the new overlap matrix for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { - S_new[i][j] += + S_new[i][j] += colvarmodule::debug_gradients_step_size * ds_2[i][j][comp]; } } diff --git a/lib/colvars/colvarmodule.h b/lib/colvars/colvarmodule.h index 7449ae23f7..2587f95d88 100644 --- a/lib/colvars/colvarmodule.h +++ b/lib/colvars/colvarmodule.h @@ -2,16 +2,16 @@ #define COLVARMODULE_H #ifndef COLVARS_VERSION -#define COLVARS_VERSION "2013-04-17" +#define COLVARS_VERSION "2013-06-19" #endif #ifndef COLVARS_DEBUG #define COLVARS_DEBUG false #endif -/// \file colvarmodule.h +/// \file colvarmodule.h /// \brief Collective variables main module -/// +/// /// This file declares the main class for defining and manipulating /// collective variables: there should be only one instance of this /// class, because several variables are made static (i.e. they are @@ -40,7 +40,7 @@ class colvarproxy; /// Class to control the collective variables calculation. An object /// (usually one) of this class is spawned from the MD program, /// containing all i/o routines and general interface. -/// +/// /// At initialization, the colvarmodule object creates a proxy object /// to provide a transparent interface between the MD program and the /// child objects @@ -54,7 +54,7 @@ private: public: friend class colvarproxy; - + /// Defining an abstract real number allows to switch precision typedef double real; /// Residue identifier @@ -147,7 +147,7 @@ public: /// \brief Number of histograms initialized (no limit on the /// number) static size_t n_histo_biases; - + /// \brief Whether debug output should be enabled (compile-time option) static inline bool debug() { @@ -157,8 +157,9 @@ public: /// \brief Constructor \param config_name Configuration file name /// \param restart_name (optional) Restart file name - colvarmodule (char const *config_name, + colvarmodule (char const *config_name, colvarproxy *proxy_in); + /// Destructor ~colvarmodule(); @@ -168,6 +169,11 @@ public: /// Initialize collective variable biases void init_biases (std::string const &conf); + /// Re-initialize data at the beginning of a run. For use with + /// MD codes that can change system parameters like atom masses + /// between run commands. + void setup(); + /// Load new configuration for the given bias - /// currently works for harmonic (force constant and/or centers) void change_configuration (std::string const &bias_name, std::string const &conf); @@ -194,16 +200,16 @@ public: bool read_traj (char const *traj_filename, size_t traj_read_begin, size_t traj_read_end); - + /// Get the pointer of a colvar from its name (returns NULL if not found) static colvar * colvar_p (std::string const &name); /// Quick conversion of an object to a string - template static std::string to_str (T const &x, + template static std::string to_str (T const &x, size_t const &width = 0, size_t const &prec = 0); /// Quick conversion of a vector of objects to a string - template static std::string to_str (std::vector const &x, + template static std::string to_str (std::vector const &x, size_t const &width = 0, size_t const &prec = 0); @@ -247,10 +253,10 @@ public: /// \brief Time step of MD integrator (fs) static real dt(); - + /// Request calculation of system force from MD engine static void request_system_force(); - + /// Print a message to the main log static void log (std::string const &message); @@ -278,7 +284,7 @@ public: /// \brief Get the closest periodic image to a reference position /// \param pos The position to look for the closest periodic image - /// \param ref_pos (optional) The reference position + /// \param ref_pos (optional) The reference position static void select_closest_image (atom_pos &pos, atom_pos const &ref_pos); @@ -290,6 +296,16 @@ public: atom_pos const &ref_pos); + /// \brief Names of groups from a Gromacs .ndx file to be read at startup + static std::list index_group_names; + + /// \brief Groups from a Gromacs .ndx file read at startup + static std::list > index_groups; + + /// \brief Read a Gromacs .ndx file + static void read_index_file (char const *filename); + + /// \brief Create atoms from a file \param filename name of the file /// (usually a PDB) \param atoms array of the atoms to be allocated /// \param pdb_field (optiona) if "filename" is a PDB file, use this @@ -371,7 +387,7 @@ std::ostream & operator << (std::ostream &os, cvm::rvector const &v); std::istream & operator >> (std::istream &is, cvm::rvector &v); -template std::string cvm::to_str (T const &x, +template std::string cvm::to_str (T const &x, size_t const &width, size_t const &prec) { std::ostringstream os; @@ -384,7 +400,7 @@ template std::string cvm::to_str (T const &x, return os.str(); } -template std::string cvm::to_str (std::vector const &x, +template std::string cvm::to_str (std::vector const &x, size_t const &width, size_t const &prec) { if (!x.size()) return std::string (""); @@ -434,7 +450,7 @@ inline void cvm::request_system_force() { proxy->request_system_force (true); } - + inline void cvm::select_closest_image (atom_pos &pos, atom_pos const &ref_pos) { diff --git a/lib/colvars/colvarparse.cpp b/lib/colvars/colvarparse.cpp index e08746c547..2e62072f34 100644 --- a/lib/colvars/colvarparse.cpp +++ b/lib/colvars/colvarparse.cpp @@ -241,11 +241,11 @@ _get_keyval_vector_ (colvarvalue); -bool colvarparse::get_keyval (std::string const &conf, - char const *key, - bool &value, - bool const &def_value, - Parse_Mode const parse_mode) +bool colvarparse::get_keyval (std::string const &conf, + char const *key, + bool &value, + bool const &def_value, + Parse_Mode const parse_mode) { std::string data; bool b_found = false, b_found_any = false; @@ -277,7 +277,7 @@ bool colvarparse::get_keyval (std::string const &conf, (data == std::string ("false")) ) { value = false; } else - cvm::fatal_error ("Error: boolean values only are allowed " + cvm::fatal_error ("Error: boolean values only are allowed " "for \""+std::string (key)+"\".\n"); if (parse_mode != parse_silent) { cvm::log ("# "+std::string (key)+" = "+ @@ -371,8 +371,8 @@ void colvarparse::check_keywords (std::string &conf, char const *key) std::string uk; std::istringstream line_is (line); line_is >> uk; - if (cvm::debug()) - cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line); + // if (cvm::debug()) + // cvm::log ("Checking the validity of \""+uk+"\" from line:\n" + line); uk = to_lower_cppstr (uk); bool found_keyword = false; @@ -426,8 +426,8 @@ bool colvarparse::key_lookup (std::string const &conf, colvarparse::dummy_pos = 0; // start from the first occurrence of key - size_t pos = conf_lower.find (key, save_pos); - + size_t pos = conf_lower.find (key, save_pos); + // iterate over all instances until it finds the isolated keyword while (true) { @@ -442,7 +442,7 @@ bool colvarparse::key_lookup (std::string const &conf, if ( std::string ("\n"+white_space+ "}").find (conf[pos-1]) == std::string::npos ) { - // none of the valid delimiting characters is on the left of key + // none of the valid delimiting characters is on the left of key b_isolated_left = false; } } @@ -451,7 +451,7 @@ bool colvarparse::key_lookup (std::string const &conf, if ( std::string ("\n"+white_space+ "{").find (conf[pos+key.size()]) == std::string::npos ) { - // none of the valid delimiting characters is on the right of key + // none of the valid delimiting characters is on the right of key b_isolated_right = false; } } @@ -461,7 +461,7 @@ bool colvarparse::key_lookup (std::string const &conf, bool const b_isolated = (b_isolated_left && b_isolated_right && b_not_within_block); - + if (b_isolated) { // found it break; @@ -490,7 +490,7 @@ bool colvarparse::key_lookup (std::string const &conf, size_t line_begin = (pl == std::string::npos) ? 0 : pos; size_t nl = conf.find ("\n", pos); size_t line_end = (nl == std::string::npos) ? conf.size() : nl; - std::string line (conf, line_begin, (line_end-line_begin)); + std::string line (conf, line_begin, (line_end-line_begin)); size_t data_begin = (to_lower_cppstr (line)).find (key) + key.size(); data_begin = line.find_first_not_of (white_space, data_begin+1); @@ -527,9 +527,9 @@ bool colvarparse::key_lookup (std::string const &conf, line_begin = line_end; nl = conf.find ('\n', line_begin+1); - if (nl == std::string::npos) + if (nl == std::string::npos) line_end = conf.size(); - else + else line_end = nl; line.append (conf, line_begin, (line_end-line_begin)); @@ -565,7 +565,7 @@ bool colvarparse::key_lookup (std::string const &conf, // << "\n"; } } - + save_pos = line_end; return true; diff --git a/lib/colvars/colvarparse.h b/lib/colvars/colvarparse.h index e7e1d96275..d3b3968a79 100644 --- a/lib/colvars/colvarparse.h +++ b/lib/colvars/colvarparse.h @@ -53,15 +53,15 @@ public: {} /// How a keyword is parsed in a string - enum Parse_Mode { + enum Parse_Mode { /// \brief (default) Read the first instance of a keyword (if /// any), report its value, and print a warning when there is more /// than one - parse_normal, + parse_normal, /// \brief Like parse_normal, but don't send any message to the log /// (useful e.g. in restart files when such messages are very /// numerous and redundant) - parse_silent + parse_silent }; /// \fn get_keyval bool const get_keyval (std::string const &conf, @@ -112,7 +112,7 @@ public: std::vector<_type_> const &def_values = \ std::vector<_type_> (0, static_cast<_type_>(_def_value_)), \ Parse_Mode const parse_mode = parse_normal) - + _get_keyval_vector_proto_ (int, 0); _get_keyval_vector_proto_ (size_t, 0); _get_keyval_vector_proto_ (std::string, std::string ("")); @@ -169,7 +169,7 @@ public: /// data (optional) holds the string provided after "key", if any /// \param save_pos (optional) stores the position of the keyword /// within "conf", useful when doing multiple calls \param - /// save_delimiters (optional) + /// save_delimiters (optional) bool key_lookup (std::string const &conf, char const *key, std::string &data = dummy_string, diff --git a/lib/colvars/colvarproxy.h b/lib/colvars/colvarproxy.h index 0af4826c96..c1416f13b7 100644 --- a/lib/colvars/colvarproxy.h +++ b/lib/colvars/colvarproxy.h @@ -16,6 +16,12 @@ public: /// Pointer to the instance of colvarmodule colvarmodule *colvars; + /// Default destructor + virtual inline ~colvarproxy() {} + + + // **************** SYSTEM-WIDE PHYSICAL QUANTITIES **************** + /// \brief Value of the unit for atomic coordinates with respect to /// angstroms (used by some variables for hard-coded default values) virtual cvm::real unit_angstrom() = 0; @@ -29,21 +35,11 @@ public: /// \brief Time step of the simulation (fs) virtual cvm::real dt() = 0; - /// Pass restraint energy value for current timestep to MD engine - virtual void add_energy (cvm::real energy) = 0; + /// \brief Pseudo-random number with Gaussian distribution + virtual cvm::real rand_gaussian (void) = 0; - /// Tell the proxy whether system forces are needed - virtual void request_system_force (bool yesno) = 0; - - /// Print a message to the main log - virtual void log (std::string const &message) = 0; - - /// Print a message to the main log and exit with error code - virtual void fatal_error (std::string const &message) = 0; - - /// Print a message to the main log and exit normally - virtual void exit (std::string const &message) = 0; + // **************** SIMULATION PARAMETERS **************** /// \brief Prefix to be used for input files (restarts, not /// configuration) @@ -59,25 +55,32 @@ public: /// \brief Restarts will be fritten each time this number of steps has passed virtual size_t restart_frequency() = 0; + + + // **************** ACCESS ATOMIC DATA **************** + + /// Pass restraint energy value for current timestep to MD engine + virtual void add_energy (cvm::real energy) = 0; + + /// Tell the proxy whether system forces are needed (may not always be available) + virtual void request_system_force (bool yesno) = 0; + + + // **************** PERIODIC BOUNDARY CONDITIONS **************** - /// \brief Get the simple distance vector between two positions - /// (with periodic boundary conditions handled transparently) + /// \brief Get the PBC-aware distance vector between two positions virtual cvm::rvector position_distance (cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) = 0; - /// \brief Get the square distance between two positions (with - /// periodic boundary conditions handled transparently) - /// - /// Note: in the case of periodic boundary conditions, this provides - /// an analytical square distance (while taking the square of - /// position_distance() would produce leads to a cusp) + /// \brief Get the PBC-aware square distance between two positions; + /// may be implemented independently from position_distance() for optimization purposes virtual cvm::real position_dist2 (cvm::atom_pos const &pos1, - cvm::atom_pos const &pos2) = 0; + cvm::atom_pos const &pos2); /// \brief Get the closest periodic image to a reference position /// \param pos The position to look for the closest periodic image - /// \param ref_pos The reference position + /// \param ref_pos The reference position virtual void select_closest_image (cvm::atom_pos &pos, cvm::atom_pos const &ref_pos) = 0; @@ -89,6 +92,18 @@ public: cvm::atom_pos const &ref_pos); + + // **************** INPUT/OUTPUT **************** + + /// Print a message to the main log + virtual void log (std::string const &message) = 0; + + /// Print a message to the main log and exit with error code + virtual void fatal_error (std::string const &message) = 0; + + /// Print a message to the main log and exit normally + virtual void exit (std::string const &message) = 0; + /// \brief Read atom identifiers from a file \param filename name of /// the file (usually a PDB) \param atoms array to which atoms read /// from "filename" will be appended \param pdb_field (optiona) if @@ -97,7 +112,7 @@ public: virtual void load_atoms (char const *filename, std::vector &atoms, std::string const pdb_field, - double const pdb_field_value = 0.0) = 0; + double const pdb_field_value = 0.0) {} /// \brief Load the coordinates for a group of atoms from a file /// (usually a PDB); if "pos" is already allocated, the number of its @@ -108,14 +123,9 @@ public: std::string const pdb_field, double const pdb_field_value = 0.0) = 0; - /// \brief Rename the given file, under the convention provided by - /// the MD program - virtual void backup_file (char const *filename) = 0; + /// \brief Rename the given file, before overwriting it + virtual void backup_file (char const *filename) {} - /// \brief Pseudo-random number with Gaussian distribution - virtual cvm::real rand_gaussian (void) = 0; - - virtual inline ~colvarproxy() {} }; @@ -128,6 +138,12 @@ inline void colvarproxy::select_closest_images (std::vector &pos, } } +inline cvm::real colvarproxy::position_dist2 (cvm::atom_pos const &pos1, + cvm::atom_pos const &pos2) +{ + return (position_distance (pos1, pos2)).norm2(); +} + #endif diff --git a/lib/colvars/colvartypes.h b/lib/colvars/colvartypes.h index fa18310ac4..4c285631a8 100644 --- a/lib/colvars/colvartypes.h +++ b/lib/colvars/colvartypes.h @@ -19,7 +19,7 @@ class colvarmodule::rvector { public: cvm::real x, y, z; - + inline rvector() : x (0.0), y (0.0), z (0.0) {} @@ -55,7 +55,7 @@ public: } - inline cvm::rvector & operator = (cvm::real const &v) + inline cvm::rvector & operator = (cvm::real const &v) { x = v; y = v; @@ -63,28 +63,28 @@ public: return *this; } - inline void operator += (cvm::rvector const &v) + inline void operator += (cvm::rvector const &v) { x += v.x; y += v.y; z += v.z; } - inline void operator -= (cvm::rvector const &v) + inline void operator -= (cvm::rvector const &v) { x -= v.x; y -= v.y; z -= v.z; } - inline void operator *= (cvm::real const &v) + inline void operator *= (cvm::real const &v) { x *= v; y *= v; z *= v; } - inline void operator /= (cvm::real const& v) + inline void operator /= (cvm::real const& v) { x /= v; y /= v; @@ -113,57 +113,57 @@ public: } - static inline cvm::rvector outer (cvm::rvector const &v1, cvm::rvector const &v2) + static inline cvm::rvector outer (cvm::rvector const &v1, cvm::rvector const &v2) { return cvm::rvector ( v1.y*v2.z - v2.y*v1.z, -v1.x*v2.z + v2.x*v1.z, v1.x*v2.y - v2.x*v1.y); } - friend inline cvm::rvector operator - (cvm::rvector const &v) + friend inline cvm::rvector operator - (cvm::rvector const &v) { return cvm::rvector (-v.x, -v.y, -v.z); } - friend inline int operator == (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline int operator == (cvm::rvector const &v1, cvm::rvector const &v2) { return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); } - friend inline int operator != (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline int operator != (cvm::rvector const &v1, cvm::rvector const &v2) { return (v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z); } - friend inline cvm::rvector operator + (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline cvm::rvector operator + (cvm::rvector const &v1, cvm::rvector const &v2) { return cvm::rvector (v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); } - friend inline cvm::rvector operator - (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline cvm::rvector operator - (cvm::rvector const &v1, cvm::rvector const &v2) { return cvm::rvector (v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); } - friend inline cvm::real operator * (cvm::rvector const &v1, cvm::rvector const &v2) + friend inline cvm::real operator * (cvm::rvector const &v1, cvm::rvector const &v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } - friend inline cvm::rvector operator * (cvm::real const &a, cvm::rvector const &v) + friend inline cvm::rvector operator * (cvm::real const &a, cvm::rvector const &v) { return cvm::rvector (a*v.x, a*v.y, a*v.z); } - friend inline cvm::rvector operator * (cvm::rvector const &v, cvm::real const &a) + friend inline cvm::rvector operator * (cvm::rvector const &v, cvm::real const &a) { return cvm::rvector (a*v.x, a*v.y, a*v.z); } - friend inline cvm::rvector operator / (cvm::rvector const &v, cvm::real const &a) + friend inline cvm::rvector operator / (cvm::rvector const &v, cvm::real const &a) { return cvm::rvector (v.x/a, v.y/a, v.z/a); } - + }; @@ -186,7 +186,7 @@ public: { return length; } - + /// Default constructor inline vector1d (T const &t = T()) { @@ -251,7 +251,7 @@ public: return prod; } - /// Formatted output + /// Formatted output friend std::ostream & operator << (std::ostream &os, vector1d const &v) { @@ -310,7 +310,7 @@ public: this->alloc(); reset(); } - + /// Constructor from a 2-d C array inline matrix2d (T const **m) { @@ -380,7 +380,7 @@ public: // } // } - /// Formatted output + /// Formatted output friend std::ostream & operator << (std::ostream &os, matrix2d const &m) { @@ -454,16 +454,16 @@ public: inline cvm::real zz() const { return array[2][2]; } /// Constructor from a 2-d C array - inline rmatrix (cvm::real const **m) - : cvm::matrix2d (m) + inline rmatrix (cvm::real const **m) + : cvm::matrix2d (m) {} /// Default constructor - inline rmatrix() + inline rmatrix() : cvm::matrix2d() {} - /// Constructor component by component + /// Constructor component by component inline rmatrix (cvm::real const &xxi, cvm::real const &xyi, cvm::real const &xzi, @@ -472,7 +472,7 @@ public: cvm::real const &yzi, cvm::real const &zxi, cvm::real const &zyi, - cvm::real const &zzi) + cvm::real const &zzi) : cvm::matrix2d() { this->xx() = xxi; @@ -488,7 +488,7 @@ public: /// Destructor inline ~rmatrix() - {} + {} /// Return the determinant inline cvm::real determinant() const @@ -528,7 +528,7 @@ public: }; - + inline cvm::rvector operator * (cvm::rmatrix const &m, cvm::rvector const &r) { @@ -680,7 +680,7 @@ public: return std::sqrt (this->norm2()); } - /// Return the conjugate quaternion + /// Return the conjugate quaternion inline cvm::quaternion conjugate() const { return cvm::quaternion (q0, -q1, -q2, -q3); @@ -720,7 +720,7 @@ public: return cvm::quaternion (0.0, v.x, v.y, v.z); } /// Return the vector component - inline cvm::rvector get_vector() const + inline cvm::rvector get_vector() const { return cvm::rvector (q1, q2, q3); } @@ -1002,11 +1002,11 @@ public: if (q.q0 != 0.0) { // cvm::real const x = iprod/q.q0; - + cvm::real const dspindx = (180.0/PI) * 2.0 * (1.0 / (1.0 + (iprod*iprod)/(q.q0*q.q0))); - return - cvm::quaternion ( dspindx * (iprod * (-1.0) / (q.q0*q.q0)), + return + cvm::quaternion ( dspindx * (iprod * (-1.0) / (q.q0*q.q0)), dspindx * ((1.0/q.q0) * axis.x), dspindx * ((1.0/q.q0) * axis.y), dspindx * ((1.0/q.q0) * axis.z)); @@ -1023,11 +1023,11 @@ public: inline cvm::real cos_theta (cvm::rvector const &axis) const { cvm::rvector const q_vec = q.get_vector(); - cvm::real const alpha = + cvm::real const alpha = (180.0/PI) * 2.0 * std::atan2 (axis * q_vec, q.q0); cvm::real const cos_spin_2 = std::cos (alpha * (PI/180.0) * 0.5); - cvm::real const cos_theta_2 = ( (cos_spin_2 != 0.0) ? + cvm::real const cos_theta_2 = ( (cos_spin_2 != 0.0) ? (q.q0 / cos_spin_2) : (0.0) ); // cos(2t) = 2*cos(t)^2 - 1 @@ -1044,7 +1044,7 @@ public: if (q.q0 != 0.0) { - cvm::real const d_cos_theta_dq0 = + cvm::real const d_cos_theta_dq0 = (4.0 * q.q0 / (cos_spin_2*cos_spin_2)) * (1.0 - (iprod*iprod)/(q.q0*q.q0) / (1.0 + (iprod*iprod)/(q.q0*q.q0))); @@ -1052,7 +1052,7 @@ public: (4.0 * q.q0 / (cos_spin_2*cos_spin_2) * (iprod/q.q0) / (1.0 + (iprod*iprod)/(q.q0*q.q0))); - return cvm::quaternion (d_cos_theta_dq0, + return cvm::quaternion (d_cos_theta_dq0, d_cos_theta_dqn * axis.x, d_cos_theta_dqn * axis.y, d_cos_theta_dqn * axis.z); diff --git a/lib/colvars/colvarvalue.cpp b/lib/colvars/colvarvalue.cpp index f7d6aa6bf6..18a3bd00c5 100644 --- a/lib/colvars/colvarvalue.cpp +++ b/lib/colvars/colvarvalue.cpp @@ -28,7 +28,7 @@ void colvarvalue::error_rside cvm::fatal_error ("Trying to assign a colvar value with type \""+ type_desc[this->value_type]+"\" to one with type \""+ type_desc[vt]+"\".\n"); -} +} void colvarvalue::error_lside (colvarvalue::Type const &vt) const @@ -36,7 +36,7 @@ void colvarvalue::error_lside cvm::fatal_error ("Trying to use a colvar value with type \""+ type_desc[vt]+"\" as one of type \""+ type_desc[this->value_type]+"\".\n"); -} +} @@ -125,7 +125,7 @@ void colvarvalue::p2leg_opt (colvarvalue const &x, break; case colvarvalue::type_vector: while (xvi != xv_end) { - cvm::real const cosine = + cvm::real const cosine = ((xvi)->rvector_value * x.rvector_value) / ((xvi)->rvector_value.norm() * x.rvector_value.norm()); xvi++; @@ -167,7 +167,7 @@ void colvarvalue::p2leg_opt (colvarvalue const &x, break; case colvarvalue::type_vector: while (xvi != xv_end) { - cvm::real const cosine = + cvm::real const cosine = ((xvi)->rvector_value * x.rvector_value) / ((xvi)->rvector_value.norm() * x.rvector_value.norm()); xvi++; @@ -225,11 +225,11 @@ std::istream & operator >> (std::istream &is, colvarvalue &x) switch (x.type()) { case colvarvalue::type_scalar: - is >> x.real_value; + is >> x.real_value; break; case colvarvalue::type_vector: case colvarvalue::type_unitvector: - is >> x.rvector_value; + is >> x.rvector_value; break; case colvarvalue::type_quaternion: is >> x.quaternion_value; @@ -242,7 +242,7 @@ std::istream & operator >> (std::istream &is, colvarvalue &x) } -size_t colvarvalue::output_width (size_t const &real_width) +size_t colvarvalue::output_width (size_t const &real_width) const { switch (this->value_type) { case colvarvalue::type_scalar: diff --git a/lib/colvars/colvarvalue.h b/lib/colvars/colvarvalue.h index 134585af00..e262e7febe 100644 --- a/lib/colvars/colvarvalue.h +++ b/lib/colvars/colvarvalue.h @@ -37,7 +37,7 @@ /// the problem, because \link colvarvalue \endlink objects are first /// initialized in the configuration, and the stream operation will be /// performed only when reading restart files. -/// +/// /// No problem of course with the output streams: \code os << x; /// \endcode will print a different output according to the value of /// colvarvalue::value_type, and the width of such output is returned @@ -92,7 +92,7 @@ public: cvm::quaternion quaternion_value; /// Current type of this colvarvalue object - Type value_type; + Type value_type; static inline bool type_checking() { @@ -146,7 +146,7 @@ public: case type_scalar: real_value = x.real_value; break; - case type_vector: + case type_vector: case type_unitvector: rvector_value = x.rvector_value; break; @@ -254,7 +254,7 @@ public: /// Ensure that the two types are the same within a binary operator void static check_types (colvarvalue const &x1, colvarvalue const &x2); - /// Undefined operation + /// Undefined operation void undef_op() const; /// Trying to assign this \link colvarvalue \endlink object to @@ -265,10 +265,10 @@ public: /// with a different type to this object void error_rside (Type const &vt) const; - ///�Give the number of characters required to output this + /// Give the number of characters required to output this /// colvarvalue, given the current type assigned and the number of /// characters for a real number - size_t output_width (size_t const &real_width); + size_t output_width (size_t const &real_width) const; // optimized routines for operations with an array; xv and inner as @@ -324,7 +324,7 @@ inline void colvarvalue::reset() case colvarvalue::type_notset: default: break; - } + } } @@ -344,7 +344,7 @@ inline void colvarvalue::apply_constraints() case colvarvalue::type_notset: default: break; - } + } } @@ -362,7 +362,7 @@ inline cvm::real colvarvalue::norm2() const case colvarvalue::type_notset: default: return 0.0; - } + } } @@ -383,7 +383,7 @@ inline colvarvalue colvarvalue::inverse() const case colvarvalue::type_notset: default: undef_op(); - } + } return colvarvalue(); } @@ -391,11 +391,11 @@ inline colvarvalue colvarvalue::inverse() const inline colvarvalue & colvarvalue::operator = (colvarvalue const &x) { if (this->value_type != type_notset) - if (this->value_type != x.value_type) + if (this->value_type != x.value_type) error_lside (x.value_type); this->value_type = x.value_type; - + switch (this->value_type) { case colvarvalue::type_scalar: this->real_value = x.real_value; From 6dc2447695310c5d9a02795d9e195a8e578659d1 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:48:40 +0000 Subject: [PATCH 19/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10119 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- lib/colvars/Makefile.lammps.debug | 5 ++ lib/colvars/Makefile.lammps.empty | 5 ++ lib/colvars/Makefile.mingw32-cross-mpi | 13 +++ lib/colvars/Makefile.mingw64-cross | 109 +++++++++++++++++++++++++ lib/colvars/Makefile.mingw64-cross-mpi | 13 +++ 5 files changed, 145 insertions(+) create mode 100644 lib/colvars/Makefile.lammps.debug create mode 100644 lib/colvars/Makefile.lammps.empty create mode 100644 lib/colvars/Makefile.mingw32-cross-mpi create mode 100644 lib/colvars/Makefile.mingw64-cross create mode 100644 lib/colvars/Makefile.mingw64-cross-mpi diff --git a/lib/colvars/Makefile.lammps.debug b/lib/colvars/Makefile.lammps.debug new file mode 100644 index 0000000000..1ef229d58a --- /dev/null +++ b/lib/colvars/Makefile.lammps.debug @@ -0,0 +1,5 @@ +# Settings that the LAMMPS build will import when this package library is used + +colvars_SYSINC = # -DCOLVARS_DEBUG +colvars_SYSLIB = +colvars_SYSPATH = diff --git a/lib/colvars/Makefile.lammps.empty b/lib/colvars/Makefile.lammps.empty new file mode 100644 index 0000000000..1ef229d58a --- /dev/null +++ b/lib/colvars/Makefile.lammps.empty @@ -0,0 +1,5 @@ +# Settings that the LAMMPS build will import when this package library is used + +colvars_SYSINC = # -DCOLVARS_DEBUG +colvars_SYSLIB = +colvars_SYSPATH = diff --git a/lib/colvars/Makefile.mingw32-cross-mpi b/lib/colvars/Makefile.mingw32-cross-mpi new file mode 100644 index 0000000000..cc2a76111a --- /dev/null +++ b/lib/colvars/Makefile.mingw32-cross-mpi @@ -0,0 +1,13 @@ +# -*- makefile -*- wrapper for non-MPI libraries + +SHELL=/bin/sh + +all: + $(MAKE) $(MFLAGS) mingw32-cross + rm -f Obj_mingw32-mpi + ln -s Obj_mingw32 Obj_mingw32-mpi + +clean: + $(MAKE) $(MFLAGS) clean-mingw32-cross + rm -f Obj_mingw32-mpi + diff --git a/lib/colvars/Makefile.mingw64-cross b/lib/colvars/Makefile.mingw64-cross new file mode 100644 index 0000000000..85d63eae54 --- /dev/null +++ b/lib/colvars/Makefile.mingw64-cross @@ -0,0 +1,109 @@ +# library build -*- makefile -*- for colvars module + +# which file will be copied to Makefile.lammps + +EXTRAMAKE = Makefile.lammps.empty + +# ------ SETTINGS ------ + +CXX = x86_64-w64-mingw32-g++ +CXXFLAGS = -O2 -march=core2 -mtune=core2 -mpc64 -msse2 \ + -fno-rtti -fno-exceptions -finline-functions \ + -ffast-math -funroll-loops -fstrict-aliasing \ + -Wall -W -Wno-uninitialized +ARCHIVE = x86_64-w64-mingw32-ar +ARCHFLAG = -rscv +SHELL = /bin/sh + +# ------ DEFINITIONS ------ + +SRC = colvaratoms.cpp colvarbias_abf.cpp colvarbias.cpp colvarbias_meta.cpp \ + colvar.cpp colvarcomp_angles.cpp colvarcomp.cpp colvarcomp_coordnums.cpp \ + colvarcomp_distances.cpp colvarcomp_protein.cpp colvarcomp_rotations.cpp \ + colvargrid.cpp colvarmodule.cpp colvarparse.cpp colvarvalue.cpp + +DIR = Obj_mingw64/ +LIB = $(DIR)libcolvars.a +OBJ = $(SRC:%.cpp=$(DIR)%.o) +EXE = #colvars_standalone + +# ------ MAKE PROCEDURE ------ + +default: $(DIR) $(LIB) $(EXE) Makefile.lammps + +$(DIR): + mkdir $(DIR) + +Makefile.lammps: + @cp $(EXTRAMAKE) Makefile.lammps + +$(LIB): $(DIR) $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ) + +$(DIR)colvars_standalone: colvars_main.o colvarproxy_standalone.o $(LIB) + $(CXX) -o $@ $(CXXFLAGS) $^ + +# ------ MAKE FLAGS ------ + +.SUFFIXES: +.SUFFIXES: .cpp .o + +.PHONY: default clean + +# ------ COMPILE RULES ------ + +$(DIR)%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +# ------ DEPENDENCIES ------ +# +$(DIR)colvars_main.o: colvars_main.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarproxy_standalone.h colvaratoms.h colvarparse.h colvarvalue.h +$(DIR)colvarproxy_standalone.o: colvarproxy_standalone.cpp colvarmodule.h \ + colvartypes.h colvarproxy.h colvaratoms.h colvarparse.h colvarvalue.h \ + colvarproxy_standalone.h +$(DIR)colvaratoms.o: colvaratoms.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarparse.h colvarvalue.h colvaratoms.h +$(DIR)colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h colvartypes.h \ + colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarbias_abf.h \ + colvarbias.h colvargrid.h +$(DIR)colvarbias.o: colvarbias.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarvalue.h colvarbias.h colvar.h colvarparse.h +$(DIR)colvarbias_meta.o: colvarbias_meta.cpp colvar.h colvarmodule.h \ + colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h \ + colvarbias_meta.h colvarbias.h colvargrid.h +$(DIR)colvar.o: colvar.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h +$(DIR)colvarcomp_angles.o: colvarcomp_angles.cpp colvarmodule.h colvartypes.h \ + colvarproxy.h colvar.h colvarvalue.h colvarparse.h colvarcomp.h \ + colvaratoms.h +$(DIR)colvarcomp.o: colvarcomp.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarvalue.h colvar.h colvarparse.h colvarcomp.h colvaratoms.h +$(DIR)colvarcomp_coordnums.o: colvarcomp_coordnums.cpp colvarmodule.h \ + colvartypes.h colvarproxy.h colvarparse.h colvarvalue.h colvaratoms.h \ + colvar.h colvarcomp.h +$(DIR)colvarcomp_distances.o: colvarcomp_distances.cpp colvarmodule.h \ + colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \ + colvarcomp.h colvaratoms.h +$(DIR)colvarcomp_protein.o: colvarcomp_protein.cpp colvarmodule.h colvartypes.h \ + colvarproxy.h colvarvalue.h colvarparse.h colvar.h colvarcomp.h \ + colvaratoms.h +$(DIR)colvarcomp_rotations.o: colvarcomp_rotations.cpp colvarmodule.h \ + colvartypes.h colvarproxy.h colvarvalue.h colvarparse.h colvar.h \ + colvarcomp.h colvaratoms.h +$(DIR)colvargrid.o: colvargrid.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarvalue.h colvarparse.h colvar.h colvarcomp.h colvaratoms.h \ + colvargrid.h +$(DIR)colvarmodule.o: colvarmodule.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarparse.h colvarvalue.h colvar.h colvarbias.h colvarbias_meta.h \ + colvargrid.h colvarbias_abf.h +$(DIR)colvarparse.o: colvarparse.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarvalue.h colvarparse.h +$(DIR)colvarvalue.o: colvarvalue.cpp colvarmodule.h colvartypes.h colvarproxy.h \ + colvarvalue.h + +# ------ CLEAN ------ + +clean: + -rm $(DIR)*.o *~ $(LIB) + -rmdir $(DIR) diff --git a/lib/colvars/Makefile.mingw64-cross-mpi b/lib/colvars/Makefile.mingw64-cross-mpi new file mode 100644 index 0000000000..1ec1a0995b --- /dev/null +++ b/lib/colvars/Makefile.mingw64-cross-mpi @@ -0,0 +1,13 @@ +# -*- makefile -*- wrapper for non-MPI libraries + +SHELL=/bin/sh + +all: + $(MAKE) $(MFLAGS) mingw64-cross + rm -f Obj_mingw64-mpi + ln -s Obj_mingw64 Obj_mingw64-mpi + +clean: + $(MAKE) $(MFLAGS) clean-mingw64-cross + rm -f Obj_mingw64-mpi + From 738d900762112aeb07a538c2a5a657f2dc04c195 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:50:07 +0000 Subject: [PATCH 20/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10120 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/USER-COLVARS/Install.sh | 2 +- src/USER-COLVARS/README | 30 +-- src/USER-COLVARS/colvarproxy_lammps.cpp | 73 +++++-- src/USER-COLVARS/colvarproxy_lammps.h | 17 +- src/USER-COLVARS/fix_colvars.cpp | 259 +++++++++++------------- src/USER-COLVARS/fix_colvars.h | 59 +++++- 6 files changed, 259 insertions(+), 181 deletions(-) diff --git a/src/USER-COLVARS/Install.sh b/src/USER-COLVARS/Install.sh index a50799f47a..d4e252c223 100755 --- a/src/USER-COLVARS/Install.sh +++ b/src/USER-COLVARS/Install.sh @@ -35,7 +35,7 @@ if (test $1 = 1) then if (test -e ../Makefile.package) then sed -i -e 's/[^ \t]*colvars[^ \t]* //g' ../Makefile.package sed -i -e 's|^PKG_INC =[ \t]*|&-I..\/..\/lib\/colvars |' ../Makefile.package - sed -i -e 's|^PKG_PATH =[ \t]*|&-L..\/..\/lib\/colvars |' ../Makefile.package + sed -i -e 's|^PKG_PATH =[ \t]*|&-L..\/..\/lib\/colvars$(LIBOBJDIR) |' ../Makefile.package sed -i -e 's|^PKG_LIB =[ \t]*|&-lcolvars |' ../Makefile.package sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(colvars_SYSINC) |' ../Makefile.package sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(colvars_SYSLIB) |' ../Makefile.package diff --git a/src/USER-COLVARS/README b/src/USER-COLVARS/README index d027f0b1b5..43a25e18a7 100644 --- a/src/USER-COLVARS/README +++ b/src/USER-COLVARS/README @@ -7,12 +7,20 @@ Sampling and Restraints. This code consists of two parts: - a portable collective variable module library written and maintained by Giacomo Fiorin (ICMS, Temple University, - Philadelphia, PA, USA) and Jerome Henin (LISM, CNRS, - Marseille, France). + Philadelphia, PA, USA) and Jerome Henin (IBPC, CNRS, + Paris, France). + + A copy of this code is located in the directory lib/colvars + and needs to be compiled first. More info about this code + can be found at: http://colvars.github.io/ + + and in the publications: + + Using collective variables to drive molecular dynamics + simulations, + Giacomo Fiorin , Michael L. Klein & Jérôme Hénin (2013): + Molecular Physics DOI:10.1080/00268976.2013.813594 - This code is located in the directory lib/colvars and - needs to be compiled first. More info about this code - can be found in the publication: Exploring Multidimensional Free Energy Landscapes Using Time-Dependent Biases on Collective Variables, J. Hénin, G. Fiorin, C. Chipot, and M. L. Klein, @@ -20,23 +28,17 @@ Sampling and Restraints. This code consists of two parts: - the colvars fix and a thin interface layer, which exchanges information between LAMMPS and the collective variable module. + This interface was written and is maintained by + Axel Kohlmeyer (akohlmey@gmail.com) See the doc page of fix colvars for more details There are example scripts for using this package in examples/USER/colvars -This is a very new interface that does not yet support all -features in the module and will see future optimizations -and improvements. The colvars module library is also available -in NAMD has been thoroughly used and tested there. Bugs and -problems are likely due to the interface layers code. -Thus the current version of this package should be considered -beta quality. - The person who created this package is Axel Kohlmeyer at Temple U (akohlmey at gmail.com). Contact him directly if you have questions. --------------------------------- -version: 0.1 / 2012-03-03 +Version: 2013-06-27 diff --git a/src/USER-COLVARS/colvarproxy_lammps.cpp b/src/USER-COLVARS/colvarproxy_lammps.cpp index f5a0a5df68..8eeddf2087 100644 --- a/src/USER-COLVARS/colvarproxy_lammps.cpp +++ b/src/USER-COLVARS/colvarproxy_lammps.cpp @@ -56,13 +56,11 @@ static void my_backup_file(const char *filename, const char *extension) //////////////////////////////////////////////////////////////////////// colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp, - const char *conf_file, const char *inp_name, const char *out_name, const int seed, - const double temp, - const int *typemap) - : _lmp(lmp), _typemap(typemap) + const double temp) + : _lmp(lmp) { if (cvm::debug()) log("Initializing the colvars proxy object.\n"); @@ -87,15 +85,20 @@ colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp, restart_prefix_str = std::string("rest"); // try to extract a restart prefix from a potential restart command. - if (_lmp->output->restart_every_single > 0) { - restart_prefix_str = std::string(_lmp->output->restart1); - } else if (_lmp->output->restart_every_double > 0) { - restart_prefix_str = std::string(_lmp->output->restart2a); + LAMMPS_NS::Output *outp = _lmp->output; + if ((outp->restart_every_single > 0) && (outp->restart1 != 0)) { + restart_prefix_str = std::string(outp->restart1); + } else if ((outp->restart_every_double > 0) && (outp->restart2a != 0)) { + restart_prefix_str = std::string(outp->restart2a); } // trim off unwanted stuff from the restart prefix if (restart_prefix_str.rfind(".*") != std::string::npos) restart_prefix_str.erase(restart_prefix_str.rfind(".*"),2); +} + +void colvarproxy_lammps::init(const char *conf_file) +{ // create the colvarmodule instance colvars = new colvarmodule(conf_file,this); @@ -112,14 +115,11 @@ colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp, log(cvm::line_marker); log("Info: done initializing the colvars proxy object.\n"); } - // this is only valid through the constructor. - _typemap = NULL; } colvarproxy_lammps::~colvarproxy_lammps() { delete _random; - if (colvars != NULL) { colvars->write_output_files(); delete colvars; @@ -127,6 +127,12 @@ colvarproxy_lammps::~colvarproxy_lammps() } } +// re-initialize data where needed +void colvarproxy_lammps::setup() +{ + colvars->setup(); +} + // trigger colvars computation double colvarproxy_lammps::compute() { @@ -149,6 +155,11 @@ double colvarproxy_lammps::compute() "Updating internal data.\n"); } + // zero the forces on the atoms, so that they can be accumulated by the colvars + for (size_t i = 0; i < applied_forces.size(); i++) { + applied_forces[i].x = applied_forces[i].y = applied_forces[i].z = 0.0; + } + // call the collective variable module colvars->calc(); @@ -164,6 +175,26 @@ double colvarproxy_lammps::compute() return bias_energy; } +void colvarproxy_lammps::serialize_status(std::string &rst) +{ + std::ostringstream os; + colvars->write_restart(os); + rst = os.str(); +} + +// set status from string +bool colvarproxy_lammps::deserialize_status(std::string &rst) +{ + std::istringstream is; + is.str(rst); + + if (!colvars->read_restart(is)) { + return false; + } else { + return true; + } +} + cvm::rvector colvarproxy_lammps::position_distance(cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) { @@ -308,7 +339,7 @@ void colvarproxy_lammps::backup_file(char const *filename) int colvarproxy_lammps::init_lammps_atom(const int &aid, cvm::atom *atom) { atom->id = aid; - atom->mass = _lmp->atom->mass[_typemap[aid]]; + atom->mass = 0.0; for (size_t i = 0; i < colvars_atoms.size(); i++) { if (colvars_atoms[i] == aid) { @@ -323,7 +354,7 @@ int colvarproxy_lammps::init_lammps_atom(const int &aid, cvm::atom *atom) colvars_atoms.push_back(aid); struct commdata c; c.tag = aid; - c.type = _typemap[aid]; + c.type = 0; c.x = c.y = c.z = 0.0; positions.push_back(c); total_forces.push_back(c); @@ -385,18 +416,20 @@ cvm::atom::atom(cvm::atom const &a) cvm::atom::~atom() { - colvarproxy_lammps *cp = (colvarproxy_lammps *) cvm::proxy; - if (cp->colvars_atoms_ncopies[this->index] > 0) - cp->colvars_atoms_ncopies[this->index] -= 1; + if (this->index >= 0) { + colvarproxy_lammps *cp = (colvarproxy_lammps *) cvm::proxy; + if (cp->colvars_atoms_ncopies[this->index] > 0) + cp->colvars_atoms_ncopies[this->index] -= 1; + } } - void cvm::atom::read_position() { colvarproxy_lammps const * const cp = (colvarproxy_lammps *) cvm::proxy; this->pos.x = cp->positions[this->index].x; this->pos.y = cp->positions[this->index].y; this->pos.z = cp->positions[this->index].z; + this->mass = cp->positions[this->index].m; } void cvm::atom::read_velocity() @@ -418,7 +451,7 @@ void cvm::atom::read_system_force() void cvm::atom::apply_force(cvm::rvector const &new_force) { colvarproxy_lammps *cp = (colvarproxy_lammps *) cvm::proxy; - cp->applied_forces[this->index].x = new_force.x; - cp->applied_forces[this->index].y = new_force.y; - cp->applied_forces[this->index].z = new_force.z; + cp->applied_forces[this->index].x += new_force.x; + cp->applied_forces[this->index].y += new_force.y; + cp->applied_forces[this->index].z += new_force.z; } diff --git a/src/USER-COLVARS/colvarproxy_lammps.h b/src/USER-COLVARS/colvarproxy_lammps.h index 7d2f3b7fe5..4f6c4aef63 100644 --- a/src/USER-COLVARS/colvarproxy_lammps.h +++ b/src/USER-COLVARS/colvarproxy_lammps.h @@ -17,7 +17,7 @@ /* struct for packed data communication of coordinates and forces. */ struct commdata { int tag,type; - double x,y,z; + double x,y,z,m; }; inline std::ostream & operator<< (std::ostream &out, const commdata &cd) @@ -38,9 +38,6 @@ class colvarproxy_lammps : public colvarproxy { class LAMMPS_NS::LAMMPS *_lmp; class LAMMPS_NS::RanPark *_random; - // pointers to LAMMPS provided storage - const int *_typemap; - // state of LAMMPS properties double t_target; double bias_energy; @@ -63,9 +60,11 @@ class colvarproxy_lammps : public colvarproxy { public: friend class cvm::atom; - colvarproxy_lammps (LAMMPS_NS::LAMMPS *lmp, const char *, const char *, - const char *, const int, const double, const int *); + colvarproxy_lammps (LAMMPS_NS::LAMMPS *lmp, const char *, + const char *, const int, const double); virtual ~colvarproxy_lammps(); + void init(const char*); + void setup(); // disable default and copy constructor private: @@ -88,6 +87,12 @@ class colvarproxy_lammps : public colvarproxy { // perform colvars computation. returns biasing energy double compute(); + // dump status to string + void serialize_status(std::string &); + + // set status from string + bool deserialize_status(std::string &); + // implementation of pure methods from base class public: diff --git a/src/USER-COLVARS/fix_colvars.cpp b/src/USER-COLVARS/fix_colvars.cpp index f8298cae26..9968f193d3 100644 --- a/src/USER-COLVARS/fix_colvars.cpp +++ b/src/USER-COLVARS/fix_colvars.cpp @@ -293,20 +293,15 @@ FixColvars::FixColvars(LAMMPS *lmp, int narg, char **arg) : if (narg < 4) error->all(FLERR,"Illegal fix colvars command: too few arguments"); - if (atom->tag_enable == 0) - error->all(FLERR,"Cannot use fix colvars without atom IDs defined"); - - if (atom->rmass_flag) - error->all(FLERR,"Cannot use fix colvars for atoms with rmass attribute"); - if (instances > 0) - error->all(FLERR,"Only one fix colvars can be active at a time"); + error->all(FLERR,"Only one colvars fix can be active at a time"); ++instances; scalar_flag = 1; global_freq = 1; nevery = 1; extscalar = 1; + restart_global = 1; me = comm->me; @@ -320,13 +315,17 @@ FixColvars::FixColvars(LAMMPS *lmp, int narg, char **arg) : /* parse optional arguments */ int argsdone = 4; - while (argsdone+1 < narg) { + while (argsdone < narg) { + // we have keyword/value pairs. check if value is missing + if (argsdone+1 == narg) + error->all(FLERR,"Missing argument to keyword"); + if (0 == strcmp(arg[argsdone], "input")) { inp_name = strdup(arg[argsdone+1]); } else if (0 == strcmp(arg[argsdone], "output")) { out_name = strdup(arg[argsdone+1]); } else if (0 == strcmp(arg[argsdone], "seed")) { - rng_seed = atoi(arg[argsdone+1]); + rng_seed = force->inumeric(FLERR,arg[argsdone+1]); } else if (0 == strcmp(arg[argsdone], "unwrap")) { if (0 == strcmp(arg[argsdone+1], "yes")) { unwrap_flag = 1; @@ -349,6 +348,7 @@ FixColvars::FixColvars(LAMMPS *lmp, int narg, char **arg) : tstat_id = -1; energy = 0.0; nlevels_respa = 0; + init_flag = 0; num_coords = 0; coords = forces = oforce = NULL; comm_buf = NULL; @@ -369,15 +369,7 @@ FixColvars::~FixColvars() memory->sfree(inp_name); memory->sfree(out_name); memory->sfree(tmp_name); - deallocate(); - --instances; -} - -/* ---------------------------------------------------------------------- */ - -void FixColvars::deallocate() -{ - memory->destroy(comm_buf); + memory->sfree(comm_buf); if (proxy) { delete proxy; @@ -386,21 +378,7 @@ void FixColvars::deallocate() delete hashtable; } - proxy = NULL; - idmap = NULL; - coords = NULL; - forces = NULL; - oforce = NULL; - comm_buf = NULL; -} - -/* ---------------------------------------------------------------------- */ - -void FixColvars::post_run() -{ - deallocate(); - memory->sfree(inp_name); - inp_name = strdup(out_name); + --instances; } /* ---------------------------------------------------------------------- */ @@ -412,127 +390,99 @@ int FixColvars::setmask() mask |= MIN_POST_FORCE; mask |= POST_FORCE; mask |= POST_FORCE_RESPA; - mask |= POST_RUN; mask |= END_OF_STEP; return mask; } /* ---------------------------------------------------------------------- */ -// initial setup of colvars run. +// initial checks for colvars run. void FixColvars::init() { + if (atom->tag_enable == 0) + error->all(FLERR,"Cannot use fix colvars without atom IDs"); + + if (atom->map_style == 0) + error->all(FLERR,"Fix colvars requires an atom map"); + + if ((me == 0) && (update->whichflag == 2)) + error->warning(FLERR,"Using fix colvars with minimization"); + if (strstr(update->integrate_style,"respa")) nlevels_respa = ((Respa *) update->integrate)->nlevels; +} +/* ---------------------------------------------------------------------- */ - int i,nme,tmp,ndata; +void FixColvars::setup(int vflag) +{ + const int * const tag = atom->tag; + const int * const type = atom->type; + int i,nme,tmp,ndata,nlocal_max,tag_max,max; + int nlocal = atom->nlocal; MPI_Status status; MPI_Request request; - // collect a list of atom type by atom id for the entire system. - // the colvar module requires this information to set masses. :-( + // one time initialization + if (init_flag == 0) { + init_flag = 1; - int *typemap,*type_buf; - int nlocal_max,tag_max,max; - const int * const tag = atom->tag; - const int * const type = atom->type; - int nlocal = atom->nlocal; + // now create and initialize the colvars proxy - max=0; - for (i = 0; i < nlocal; i++) max = MAX(max,tag[i]); - MPI_Allreduce(&max,&tag_max,1,MPI_INT,MPI_MAX,world); - MPI_Allreduce(&nlocal,&nlocal_max,1,MPI_INT,MPI_MAX,world); + if (me == 0) { - if (me == 0) { - typemap = new int[tag_max+1]; - memset(typemap,0,sizeof(int)*tag_max); - } - type_buf = new int[2*nlocal_max]; + // input (= restart) name == "NULL" means, no restart. + if (inp_name) { + if (strcmp(inp_name,"NULL") == 0) { + memory->sfree(inp_name); + inp_name = NULL; + } + } - if (me == 0) { - for (i=0; ifind_fix(tmp_name); + if (tstat_id < 0) error->one(FLERR,"Could not find tstat fix ID"); + double *tt = (double*)modify->fix[tstat_id]->extract("t_target",tmp); + if (tt) t_target = *tt; + } + } - // loop over procs to receive and apply remote data - - for (i=1; i < comm->nprocs; ++i) { - MPI_Irecv(type_buf, 2*nlocal_max, MPI_INT, i, 0, world, &request); - MPI_Send(&tmp, 0, MPI_INT, i, 0, world); - MPI_Wait(&request, &status); - MPI_Get_count(&status, MPI_INT, &ndata); - - for (int k=0; kinit(conf_file); + coords = proxy->get_coords(); + forces = proxy->get_forces(); + oforce = proxy->get_oforce(); + num_coords = coords->size(); } - } else { // me != 0 - // copy tag/type data into communication buffer + // send the list of all colvar atom IDs to all nodes. + // also initialize and build hashtable on master. - nme = 0; - for (i=0; icreate(taglist,num_coords,"colvars:taglist"); + memory->create(force_buf,3*num_coords,"colvars:force_buf"); - // now create and initialize the colvar proxy + if (me == 0) { + std::vector *tags_list = proxy->get_tags(); + std::vector &tl = *tags_list; + inthash_t *hashtable=new inthash_t; + inthash_init(hashtable, num_coords); + idmap = (void *)hashtable; - if (me == 0) { - - if (inp_name) { - if (strcmp(inp_name,"NULL") == 0) { - memory->sfree(inp_name); - inp_name = NULL; + for (i=0; i < num_coords; ++i) { + taglist[i] = tl[i]; + inthash_insert(hashtable, tl[i], i); } } - - double t_target = 0.0; - if (tmp_name) { - if (strcmp(tmp_name,"NULL") == 0) - tstat_id = -1; - else { - tstat_id = modify->find_fix(tmp_name); - if (tstat_id < 0) error->one(FLERR,"Could not find tstat fix ID"); - double *tt = (double*)modify->fix[tstat_id]->extract("t_target",tmp); - if (tt) t_target = *tt; - } - } - - proxy = new colvarproxy_lammps(lmp,conf_file,inp_name,out_name, - rng_seed,t_target,typemap); - coords = proxy->get_coords(); - forces = proxy->get_forces(); - oforce = proxy->get_oforce(); - num_coords = coords->size(); - } - - // send the list of all colvar atom IDs to all nodes. - // also initialize and build hashtable on master. - - MPI_Bcast(&num_coords, 1, MPI_INT, 0, world); - memory->create(taglist,num_coords,"colvars:taglist"); - memory->create(force_buf,3*num_coords,"colvars:force_buf"); - - if (me == 0) { - std::vector *tags_list = proxy->get_tags(); - std::vector &tl = *tags_list; - inthash_t *hashtable=new inthash_t; - inthash_init(hashtable, num_coords); - idmap = (void *)hashtable; - - for (i=0; i < num_coords; ++i) { - taglist[i] = tl[i]; - inthash_insert(hashtable, tl[i], i); - } - } - MPI_Bcast(taglist, num_coords, MPI_INT, 0, world); + MPI_Bcast(taglist, num_coords, MPI_INT, 0, world); + } // end of one time initialization // determine size of comm buffer nme=0; @@ -583,6 +533,11 @@ void FixColvars::init() cd[i].y = x[k][1]; cd[i].z = x[k][2]; } + if (atom->rmass_flag) { + cd[i].m = atom->rmass[k]; + } else { + cd[i].m = atom->mass[type[k]]; + } } } @@ -604,6 +559,7 @@ void FixColvars::init() cd[j].x = comm_buf[k].x; cd[j].y = comm_buf[k].y; cd[j].z = comm_buf[k].z; + cd[j].m = comm_buf[k].m; of[j].x = of[j].y = of[j].z = 0.0; } } @@ -634,6 +590,12 @@ void FixColvars::init() comm_buf[nme].z = x[k][2]; } + if (atom->rmass_flag) { + comm_buf[nme].m = atom->rmass[k]; + } else { + comm_buf[nme].m = atom->mass[type[k]]; + } + ++nme; } } @@ -642,19 +604,17 @@ void FixColvars::init() MPI_Rsend(comm_buf, nme*size_one, MPI_BYTE, 0, 0, world); } - // clear temporary storage - if (me == 0) delete typemap; - delete type_buf; -} + // run pre-run setup in colvarproxy + proxy->setup(); -/* ---------------------------------------------------------------------- */ - -void FixColvars::setup(int vflag) -{ - if (strstr(update->integrate_style,"verlet")) + // initialize forces + if (strstr(update->integrate_style,"verlet") || (update->whichflag == 2)) post_force(vflag); - else - post_force_respa(vflag,0,0); + else { + ((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1); + post_force_respa(vflag,nlevels_respa-1,0); + ((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1); + } } /* ---------------------------------------------------------------------- */ @@ -665,7 +625,7 @@ void FixColvars::post_force(int vflag) // some housekeeping: update status of the proxy as needed. if (me == 0) { if (proxy->want_exit()) - error->one(FLERR,"Run halted on request from colvars module.\n"); + error->one(FLERR,"Run aborted on request from colvars module.\n"); if (tstat_id < 0) { proxy->set_temperature(0.0); @@ -921,6 +881,31 @@ void FixColvars::end_of_step() /* ---------------------------------------------------------------------- */ +void FixColvars::write_restart(FILE *fp) +{ + if (me == 0) { + std::string rest_text(""); + proxy->serialize_status(rest_text); + const char *cvm_state = rest_text.c_str(); + int len = strlen(cvm_state) + 1; // need to include terminating NULL byte. + fwrite(&len,sizeof(int),1,fp); + fwrite(cvm_state,1,len,fp); + } +} + +/* ---------------------------------------------------------------------- */ + +void FixColvars::restart(char *buf) +{ + init(); + if (me == 0) { + std::string rest_text(buf); + proxy->deserialize_status(rest_text); + } +} + +/* ---------------------------------------------------------------------- */ + double FixColvars::compute_scalar() { return energy; diff --git a/src/USER-COLVARS/fix_colvars.h b/src/USER-COLVARS/fix_colvars.h index a6738bf949..3b706ad76a 100644 --- a/src/USER-COLVARS/fix_colvars.h +++ b/src/USER-COLVARS/fix_colvars.h @@ -42,16 +42,16 @@ class FixColvars : public Fix { virtual int setmask(); virtual void init(); virtual void setup(int); + virtual void min_setup(int vflag) {setup(vflag);}; virtual void min_post_force(int); virtual void post_force(int); virtual void post_force_respa(int, int, int); - virtual void post_run(); virtual void end_of_step(); virtual double compute_scalar(); virtual double memory_usage(); - protected: - void deallocate(); // free internal buffers + virtual void write_restart(FILE *); + virtual void restart(char *); protected: class colvarproxy_lammps *proxy; // pointer to the colvars proxy class @@ -81,6 +81,7 @@ class FixColvars : public Fix { int nlevels_respa; // flag to determine respa levels. int store_forces; // flag to determine whether to store total forces int unwrap_flag; // 1 if atom coords are unwrapped, 0 if not + int init_flag; // 1 if initialized, 0 if not static int instances; // count fix instances, since colvars currently // only supports one instance at a time }; @@ -90,3 +91,55 @@ class FixColvars : public Fix { #endif #endif +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Cannot use fix colvars for atoms with rmass attribute + +The colvars library assigns atom masses per atom type, thus atom styles +which allow setting individual per atom masses are not supported. + +E: Missing argument to keyword + +Self-explanatory. A keyword was recognized, but no corresponding value +found. Check the input script syntax and compare to the documentation +for the command. + +E: Incorrect fix colvars unwrap flag + +Self-explanatory. Check the input script syntax. + +E: Unknown fix colvars parameter + +Self-explanatory. Check your input script syntax. + +E: Cannot use fix colvars without atom IDs + +Atom IDs are not defined, but fix colvars needs them to identify an atom. + +E: Fix colvars requires an atom map + +Use the atom_modify command to create an atom map. + +W: Using fix colvars with minimization + +Some of the functionality supported with the colvars library is not +meaningful with minimization calculations. + +E: Could not find tstat fix ID + +Self-explanatory. The thermostat fix ID provided with the tstat keyword +is not defined (anymore). Check your input file. + +E: Run aborted on request from colvars module + +Some error condition happened inside the colvars library that prohibits +it from continuing. Please examine the output for additional information. + +*/ + From 040ec54ecd583268dc204efd0c2302009286d635 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:50:14 +0000 Subject: [PATCH 21/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10121 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/USER-COLVARS/group_ndx.cpp | 174 +++++++++++++++++++++++++++++++++ src/USER-COLVARS/group_ndx.h | 55 +++++++++++ 2 files changed, 229 insertions(+) create mode 100644 src/USER-COLVARS/group_ndx.cpp create mode 100644 src/USER-COLVARS/group_ndx.h diff --git a/src/USER-COLVARS/group_ndx.cpp b/src/USER-COLVARS/group_ndx.cpp new file mode 100644 index 0000000000..de63fc7d97 --- /dev/null +++ b/src/USER-COLVARS/group_ndx.cpp @@ -0,0 +1,174 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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: Axel Kohlmeyer (Temple U) +------------------------------------------------------------------------- */ + +#include "group_ndx.h" +#include "atom.h" +#include "comm.h" +#include "group.h" +#include "memory.h" +#include "error.h" + +#include +#include + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- + helper function. integer comparison for qsort() + ---------------------------------------------------------------------- */ + +static int cmpint(const void *p1, const void *p2) +{ + const int i1 = * static_cast(p1); + const int i2 = * static_cast(p2); + if (i1 == i2) return 0; + else { + if (i1 < i2) return -1; + else return 1; + } +} + +/* ---------------------------------------------------------------------- + helper function. writes out one group to a gromacs style index file + ---------------------------------------------------------------------- */ + +static void write_group(FILE *fp, int gid, Atom *atom, Group *group, int me, + int np, MPI_Comm world, FILE *screen, FILE *logfile) +{ + char fmt[8]; + int *sendlist, *recvlist; + bigint num = group->count(gid); + int lnum, cols; + + if (me == 0) { + if (screen) fprintf(screen, " writing group %s... ", group->names[gid]); + if (logfile) fprintf(logfile, " writing group %s... ", group->names[gid]); + + // the "all" group in LAMMPS is called "System" in gromacs + if (gid == 0) { + fputs("[ System ]\n", fp); + } else { + fprintf(fp,"[ %s ]\n", group->names[gid]); + } + + // derive format string for index lists + bigint j = atom->natoms; + int i = 0; + while (j > 0) { + ++i; + j /= 10; + } + sprintf(fmt,"%%%dd ", i); + cols = 80 / (i+1); + } + + if (num > 0) { + const int * const mask = atom->mask; + const int * const tag = atom->tag; + const int groupbit = group->bitmask[gid]; + const int nlocal = atom->nlocal; + int i,j; + + sendlist = new int[nlocal]; + recvlist = new int[num]; + lnum = 0; + for (i = 0; i < nlocal; i++) + if (mask[i] & groupbit) sendlist[lnum++] = tag[i]; + + MPI_Status status; + MPI_Request request; + int nrecv,allrecv; + if (me == 0) { + for (i = 0; i < lnum; i++) + recvlist[i] = sendlist[i]; + + allrecv = lnum; + for (int i=1; i < np; ++i) { + MPI_Irecv(recvlist+allrecv,num-allrecv,MPI_INT,i,0, world,&request); + MPI_Send(&nrecv,0,MPI_INT,i,0,world); + MPI_Wait(&request,&status); + MPI_Get_count(&status,MPI_INT,&nrecv); + allrecv += nrecv; + } + + // sort received list + qsort((void *)recvlist, num, sizeof(int), cmpint); + } else { + MPI_Recv(&nrecv,0,MPI_INT,0,0,world,&status); + MPI_Rsend(sendlist,lnum,MPI_INT,0,0,world); + } + delete [] sendlist; + } + + if (me == 0) { + int i, j; + for(i = 0, j = 0; i < num; ++i) { + fprintf(fp,fmt,recvlist[i]); + ++j; + if (j == cols) { + fputs("\n",fp); + j = 0; + } + } + if (j > 0) fputs("\n",fp); + if (screen) fputs("done\n",screen); + if (logfile) fputs("done\n",logfile); + } + if (num > 0) delete[] recvlist; +} + +/* ---------------------------------------------------------------------- */ + +void Group2Ndx::command(int narg, char **arg) +{ + FILE *fp; + + if (narg < 1) error->all(FLERR,"Illegal group2ndx command"); + + if (atom->tag_enable == 0) + error->all(FLERR,"Must have atom IDs for group2ndx command"); + + if (comm->me == 0) { + fp = fopen(arg[0], "w"); + if (fp == NULL) + error->one(FLERR,"Cannot open index file for writing"); + + if (screen) + fprintf(screen, "Writing groups to index file %s:\n",arg[0]); + if (logfile) + fprintf(logfile,"Writing groups to index file %s:\n",arg[0]); + } + + if (narg == 1) { // write out all groups + for (int i=0; i < group->ngroup; ++i) { + write_group(fp,i,atom,group,comm->me,comm->nprocs,world,screen,logfile); + } + + } else { // write only selected groups + for (int i=1; i < narg; ++i) { + int gid = group->find(arg[i]); + if (gid < 0) error->all(FLERR, "Non-existing group requested"); + write_group(fp,gid,atom,group,comm->me,comm->nprocs,world,screen,logfile); + } + } + + if (comm->me == 0) { + if (screen) fputs("\n",screen); + if (logfile) fputs("\n",logfile); + fclose(fp); + } +} + diff --git a/src/USER-COLVARS/group_ndx.h b/src/USER-COLVARS/group_ndx.h new file mode 100644 index 0000000000..e0104f176f --- /dev/null +++ b/src/USER-COLVARS/group_ndx.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 COMMAND_CLASS + +CommandStyle(group2ndx,Group2Ndx) + +#else + +#ifndef LMP_GROUP_NDX_H +#define LMP_GROUP_NDX_H + +#include "pointers.h" + +namespace LAMMPS_NS { + +class Group2Ndx : protected Pointers { + public: + Group2Ndx(class LAMMPS *lmp) : Pointers(lmp) {}; + void command(int, char **); +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Must have atom IDs for group2ndx command + +There are no atom IDs defined in the system, but they are required +to identify atoms in a gromacs style index file. + +E: Cannot open index file for writing + +Self-explanatory. Check your filename, permissions, and disk space or quota. + +*/ From 1d2961508066d1832d47909db59e294cf09f64fc Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 22:56:04 +0000 Subject: [PATCH 22/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10122 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/Section_commands.html | 7 +++++++ doc/Section_commands.txt | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/doc/Section_commands.html b/doc/Section_commands.html index 3545904929..12b78f3b4f 100644 --- a/doc/Section_commands.html +++ b/doc/Section_commands.html @@ -364,6 +364,13 @@ in the command's documentation. velocitywrite_datawrite_restart +

    These are commands contributed by users, which can be used if LAMMPS +is built with the appropriate package. +

    + +

    Fix styles diff --git a/doc/Section_commands.txt b/doc/Section_commands.txt index f8ad82a57a..e30d5af4a8 100644 --- a/doc/Section_commands.txt +++ b/doc/Section_commands.txt @@ -430,6 +430,11 @@ in the command's documentation. "write_data"_write_data.html, "write_restart"_write_restart.html :tb(c=6,ea=c) +These are commands contributed by users, which can be used if "LAMMPS +is built with the appropriate package"_Section_start.html#start_3. + +"group2ndx"_group2ndx.html :tb(c=1,ea=c) + :line Fix styles :h4 From 5e57d70d408b0c7e7b43649755659686ced1d760 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:07:28 +0000 Subject: [PATCH 23/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10123 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 5969b7cda1..2122e86ca9 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define LAMMPS_VERSION "24 Jun 2013" +#define LAMMPS_VERSION "25 Jun 2013" From 07c9a7f6a687fa2ef96e8237538da1e0fa46f34c Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:07:29 +0000 Subject: [PATCH 24/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10124 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/Manual.html | 2 +- doc/Manual.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Manual.html b/doc/Manual.html index 7174aa3c2c..8e419019b9 100644 --- a/doc/Manual.html +++ b/doc/Manual.html @@ -22,7 +22,7 @@

    LAMMPS Documentation

    -

    24 Jun 2013 version +

    25 Jun 2013 version

    Version info:

    diff --git a/doc/Manual.txt b/doc/Manual.txt index 0a0f819d87..5b03883408 100644 --- a/doc/Manual.txt +++ b/doc/Manual.txt @@ -18,7 +18,7 @@

    LAMMPS Documentation :c,h3 -24 Jun 2013 version :c,h4 +25 Jun 2013 version :c,h4 Version info: :h4 From cf667e892fb5369fe3b29522018a4fc1f320ea70 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:10:30 +0000 Subject: [PATCH 25/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10126 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/USER-COLVARS/README | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/USER-COLVARS/README b/src/USER-COLVARS/README index 43a25e18a7..47638236e3 100644 --- a/src/USER-COLVARS/README +++ b/src/USER-COLVARS/README @@ -12,7 +12,7 @@ Sampling and Restraints. This code consists of two parts: A copy of this code is located in the directory lib/colvars and needs to be compiled first. More info about this code - can be found at: http://colvars.github.io/ + can be found at: http://colvars.github.io and in the publications: @@ -31,14 +31,17 @@ Sampling and Restraints. This code consists of two parts: This interface was written and is maintained by Axel Kohlmeyer (akohlmey@gmail.com) -See the doc page of fix colvars for more details +See the doc page of fix colvars for more details. + +There is a reference manual for the package included with the LAMMPS +doc pages: doc/PDF/colvars-refman-lammps.pdf There are example scripts for using this package in -examples/USER/colvars +examples/USER/colvars. The person who created this package is Axel Kohlmeyer at Temple U (akohlmey at gmail.com). Contact him directly if you have questions. - + --------------------------------- Version: 2013-06-27 From 3f5e6b3fab4802aae0233185b7f9ea9af382ff05 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:24:46 +0000 Subject: [PATCH 26/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10128 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/fix_tune_kspace.html | 21 +++++++++++---------- doc/fix_tune_kspace.txt | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/doc/fix_tune_kspace.html b/doc/fix_tune_kspace.html index c38a754031..91fa49b6de 100644 --- a/doc/fix_tune_kspace.html +++ b/doc/fix_tune_kspace.html @@ -38,16 +38,17 @@ to use for maximum simulation speed. The kspace parameters may include the style, cutoff, grid points in each direction, order, Ewald parameter, MSM parallelization cut-point, MPI tasks to use, etc.

    -

    The rationale for this fix is to provide the user with as-fast-as-possible -simulations that include long-range electrostatics (kspace) while meeting -the user-prescribed accuracy requirement. A simple heuristic could never -capture the optimal combination of parameters for every possible run-time -scenario. But by performing short tests of various kspace parameter sets, -this fix allows parameters to be tailored specifically to the user’s -machine, MPI ranks, use of threading or accelerators, the simulated system, -and the simulation details. In addition, it is possible that parameters -could be evolved with the simulation on-the-fly, which is useful for -systems that are dynamically evolving (e.g. changes in box size/shape or +

    The rationale for this fix is to provide the user with +as-fast-as-possible simulations that include long-range electrostatics +(kspace) while meeting the user-prescribed accuracy requirement. A +simple heuristic could never capture the optimal combination of +parameters for every possible run-time scenario. But by performing +short tests of various kspace parameter sets, this fix allows +parameters to be tailored specifically to the user's machine, MPI +ranks, use of threading or accelerators, the simulated system, and the +simulation details. In addition, it is possible that parameters could +be evolved with the simulation on-the-fly, which is useful for systems +that are dynamically evolving (e.g. changes in box size/shape or number of particles).

    When this fix is invoked, LAMMPS will perform short timed tests of diff --git a/doc/fix_tune_kspace.txt b/doc/fix_tune_kspace.txt index d38c695482..61b56d70ce 100644 --- a/doc/fix_tune_kspace.txt +++ b/doc/fix_tune_kspace.txt @@ -32,16 +32,17 @@ to use for maximum simulation speed. The kspace parameters may include the style, cutoff, grid points in each direction, order, Ewald parameter, MSM parallelization cut-point, MPI tasks to use, etc. -The rationale for this fix is to provide the user with as-fast-as-possible -simulations that include long-range electrostatics (kspace) while meeting -the user-prescribed accuracy requirement. A simple heuristic could never -capture the optimal combination of parameters for every possible run-time -scenario. But by performing short tests of various kspace parameter sets, -this fix allows parameters to be tailored specifically to the user’s -machine, MPI ranks, use of threading or accelerators, the simulated system, -and the simulation details. In addition, it is possible that parameters -could be evolved with the simulation on-the-fly, which is useful for -systems that are dynamically evolving (e.g. changes in box size/shape or +The rationale for this fix is to provide the user with +as-fast-as-possible simulations that include long-range electrostatics +(kspace) while meeting the user-prescribed accuracy requirement. A +simple heuristic could never capture the optimal combination of +parameters for every possible run-time scenario. But by performing +short tests of various kspace parameter sets, this fix allows +parameters to be tailored specifically to the user's machine, MPI +ranks, use of threading or accelerators, the simulated system, and the +simulation details. In addition, it is possible that parameters could +be evolved with the simulation on-the-fly, which is useful for systems +that are dynamically evolving (e.g. changes in box size/shape or number of particles). When this fix is invoked, LAMMPS will perform short timed tests of From 1e4b3bcf876a401b0a5e46772ada1b391d49f289 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:48:54 +0000 Subject: [PATCH 27/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10129 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/Section_accelerate.html | 24 +++++++++++++++++++++ doc/Section_accelerate.txt | 24 +++++++++++++++++++++ doc/kspace_style.html | 42 ++++++++++++++++++++++++++++++++++--- doc/kspace_style.txt | 40 ++++++++++++++++++++++++++++++++--- 4 files changed, 124 insertions(+), 6 deletions(-) diff --git a/doc/Section_accelerate.html b/doc/Section_accelerate.html index ff31520e64..d3f6f67d02 100644 --- a/doc/Section_accelerate.html +++ b/doc/Section_accelerate.html @@ -92,6 +92,7 @@ simply trying them out.

    • rRESPA
    • 2-FFT PPPM +
    • Staggered PPPM
    • single vs double PPPM
    • partial charge PPPM
    • verlet/split @@ -100,6 +101,29 @@ simply trying them out.
    • processor command for layout
    • OMP when lots of cores
    +

    2-FFT PPPM, also called analytic differentiation or ad PPPM, uses 2 FFTs +instead of the 4 FFTs used by the default ik differentiation PPPM. However, +2-FFT PPPM also requires a slightly larger mesh size to achieve the same accuracy +as 4-FFT PPPM. For problems where the FFT cost is the performance bottleneck (typically +large problems running on many processors), 2-FFT PPPM may be faster than 4-FFT PPPM. +

    +

    Staggered PPPM performs calculations using two different meshes, one shifted slightly with +respect to the other. This can reduce force aliasing errors and increase the accuracy of the +method, but also doubles the amount of work required. For high relative accuracy, using staggered +PPPM allows one to half the mesh size in each dimension as compared to regular PPPM, +which can give around a 4x speedup in the kspace time. However, for low relative +accuracy, using staggered PPPM gives little benefit and can be up to 2x slower in the +kspace time. For example, the rhodopsin benchmark was run on a single processor, +and results for kspace time vs. relative accuracy for the different methods are shown +in the figure below. For this system, staggered PPPM (using ik differentiation) +becomes useful when using a relative accuracy of slightly greater than 1e-5 and above. +

    +
    +
    +

    IMPORTANT NOTE: Using staggered PPPM may not give the same increase in accuracy of energy and pressure +as it does in forces, so some caution must be used if energy and/or pressure are quantities of interest, such +as when using a barostat. +


    5.3 Packages with optimized styles diff --git a/doc/Section_accelerate.txt b/doc/Section_accelerate.txt index 557ab159b3..bc35e25faa 100644 --- a/doc/Section_accelerate.txt +++ b/doc/Section_accelerate.txt @@ -88,6 +88,7 @@ simply trying them out. rRESPA 2-FFT PPPM +Staggered PPPM single vs double PPPM partial charge PPPM verlet/split @@ -96,6 +97,29 @@ load-balancing: balance and fix balance processor command for layout OMP when lots of cores :ul +2-FFT PPPM, also called {analytic differentiation} or {ad} PPPM, uses 2 FFTs +instead of the 4 FFTs used by the default {ik differentiation} PPPM. However, +2-FFT PPPM also requires a slightly larger mesh size to achieve the same accuracy +as 4-FFT PPPM. For problems where the FFT cost is the performance bottleneck (typically +large problems running on many processors), 2-FFT PPPM may be faster than 4-FFT PPPM. + +Staggered PPPM performs calculations using two different meshes, one shifted slightly with +respect to the other. This can reduce force aliasing errors and increase the accuracy of the +method, but also doubles the amount of work required. For high relative accuracy, using staggered +PPPM allows one to half the mesh size in each dimension as compared to regular PPPM, +which can give around a 4x speedup in the kspace time. However, for low relative +accuracy, using staggered PPPM gives little benefit and can be up to 2x slower in the +kspace time. For example, the rhodopsin benchmark was run on a single processor, +and results for kspace time vs. relative accuracy for the different methods are shown +in the figure below. For this system, staggered PPPM (using ik differentiation) +becomes useful when using a relative accuracy of slightly greater than 1e-5 and above. + +:c,image(JPG/rhodo_staggered.jpg) + +IMPORTANT NOTE: Using staggered PPPM may not give the same increase in accuracy of energy and pressure +as it does in forces, so some caution must be used if energy and/or pressure are quantities of interest, such +as when using a barostat. + :line 5.3 Packages with optimized styles :h4,link(acc_3) diff --git a/doc/kspace_style.html b/doc/kspace_style.html index 2f875ebc2f..d7f7874b0f 100644 --- a/doc/kspace_style.html +++ b/doc/kspace_style.html @@ -15,7 +15,7 @@

    kspace_style style value 
     
    -
    • style = none or ewald or ewald/disp or ewald/omp or pppm or pppm/cg or pppm/disp or pppm/tip4p or pppm/disp/tip4p or pppm/gpu or pppm/omp or pppm/cg/omp or pppm/tip4p/omp or msm or msm/cg or msm/omp or msm/cg/omp +
      • style = none or ewald or ewald/disp or ewald/omp or pppm or pppm/cg or pppm/disp or pppm/tip4p or pppm/stagger or pppm/disp/tip4p or pppm/gpu or pppm/omp or pppm/cg/omp or pppm/tip4p/omp or msm or msm/cg or msm/omp or msm/cg/omp
          none value = none
           ewald value = accuracy
        @@ -43,6 +43,8 @@
             accuracy = desired relative error in forces
           pppm/tip4p/omp value = accuracy
             accuracy = desired relative error in forces
        +  pppm/stagger value = accuracy
        +    accuracy = desired relative error in forces
           msm value = accuracy
             accuracy = desired relative error in forces
           msm/cg value = accuracy (smallq)
        @@ -100,8 +102,9 @@ any solid-state physics text.
         but in a more efficient manner than the ewald style.  The 1/r^6
         capability means that Lennard-Jones or Buckingham potentials can be
         used without a cutoff, i.e. they become full long-range potentials.
        -The ewald/disp style can also be used with point-dipoles (Toukmaji)
        -and is currently the only kspace solver in LAMMPS with this capability.
        +The ewald/disp style can also be used with point-dipoles
        +(Toukmaji) and is currently the only kspace solver in
        +LAMMPS with this capability.
         


        @@ -127,6 +130,30 @@ adds a charge at the massless 4th site in each TIP4P water molecule. It should be used with pair styles with a tip4p/long in their style name.

        +

        The pppm/stagger style performs calculations using two different +meshes, one shifted slightly with respect to the other. This can +reduce force aliasing errors and increase the accuracy of the method +for a given mesh size. Or a coarser mesh can be used for the same +target accuracy, which saves CPU time. However, there is a trade-off +since FFTs on two meshes are now performed which increases the +compuation required. See (Cerutti), (Neelov), +and (Hockney) for details of the method. +

        +

        For high relative accuracy, using staggered PPPM allows the mesh size +to be reduced by a factor of 2 in each dimension as compared to +regular PPPM (for the same target accuracy). This can give up to a 4x +speedup in the KSpace time (8x less mesh points, 2x more expensive). +However, for low relative accuracy, the staggered PPPM mesh size may +be essentially the same as for regular PPPM, which means the method +will be up to 2x slower in the KSpace time (simply 2x more expensive). +For more details and timings, see +Section_accelerate. +

        +

        IMPORTANT NOTE: Using pppm/stagger may not give the same increase in +the accuracy of energy and pressure as it does in forces, so some +caution must be used if energy and/or pressure are quantities of +interest, such as when using a barostat. +


        The pppm/disp and pppm/disp/tip4p styles add a mesh-based long-range @@ -318,6 +345,15 @@ Adam Hilger, NY (1989).

        (Pollock) Pollock and Glosli, Comp Phys Comm, 95, 93 (1996).

        + + +

        (Cerutti) Cerutti, Duke, Darden, Lybrand, Journal of Chemical Theory +and Computation 5, 2322 (2009) +

        + + +

        (Neelov) Neelov, Holm, J Chem Phys 132, 234103 (2010) +

        (Veld) In 't Veld, Ismail, Grest, J Chem Phys, 127, 144711 (2007). diff --git a/doc/kspace_style.txt b/doc/kspace_style.txt index 5979d7ab31..a8021a7a55 100644 --- a/doc/kspace_style.txt +++ b/doc/kspace_style.txt @@ -12,7 +12,7 @@ kspace_style command :h3 kspace_style style value :pre -style = {none} or {ewald} or {ewald/disp} or {ewald/omp} or {pppm} or {pppm/cg} or {pppm/disp} or {pppm/tip4p} or {pppm/disp/tip4p} or {pppm/gpu} or {pppm/omp} or {pppm/cg/omp} or {pppm/tip4p/omp} or {msm} or {msm/cg} or {msm/omp} or {msm/cg/omp} :ulb,l +style = {none} or {ewald} or {ewald/disp} or {ewald/omp} or {pppm} or {pppm/cg} or {pppm/disp} or {pppm/tip4p} or {pppm/stagger} or {pppm/disp/tip4p} or {pppm/gpu} or {pppm/omp} or {pppm/cg/omp} or {pppm/tip4p/omp} or {msm} or {msm/cg} or {msm/omp} or {msm/cg/omp} :ulb,l {none} value = none {ewald} value = accuracy accuracy = desired relative error in forces @@ -39,6 +39,8 @@ style = {none} or {ewald} or {ewald/disp} or {ewald/omp} or {pppm} or {pppm/cg} accuracy = desired relative error in forces {pppm/tip4p/omp} value = accuracy accuracy = desired relative error in forces + {pppm/stagger} value = accuracy + accuracy = desired relative error in forces {msm} value = accuracy accuracy = desired relative error in forces {msm/cg} value = accuracy (smallq) @@ -93,8 +95,9 @@ The {ewald/disp} style adds a long-range dispersion sum option for but in a more efficient manner than the {ewald} style. The 1/r^6 capability means that Lennard-Jones or Buckingham potentials can be used without a cutoff, i.e. they become full long-range potentials. -The {ewald/disp} style can also be used with point-dipoles "(Toukmaji)"_#Toukmaji -and is currently the only kspace solver in LAMMPS with this capability. +The {ewald/disp} style can also be used with point-dipoles +"(Toukmaji)"_#Toukmaji and is currently the only kspace solver in +LAMMPS with this capability. :line @@ -120,6 +123,30 @@ adds a charge at the massless 4th site in each TIP4P water molecule. It should be used with "pair styles"_pair_style.html with a {tip4p/long} in their style name. +The {pppm/stagger} style performs calculations using two different +meshes, one shifted slightly with respect to the other. This can +reduce force aliasing errors and increase the accuracy of the method +for a given mesh size. Or a coarser mesh can be used for the same +target accuracy, which saves CPU time. However, there is a trade-off +since FFTs on two meshes are now performed which increases the +compuation required. See "(Cerutti)"_#Cerutti, "(Neelov)"_#Neelov, +and "(Hockney)"_#Hockney for details of the method. + +For high relative accuracy, using staggered PPPM allows the mesh size +to be reduced by a factor of 2 in each dimension as compared to +regular PPPM (for the same target accuracy). This can give up to a 4x +speedup in the KSpace time (8x less mesh points, 2x more expensive). +However, for low relative accuracy, the staggered PPPM mesh size may +be essentially the same as for regular PPPM, which means the method +will be up to 2x slower in the KSpace time (simply 2x more expensive). +For more details and timings, see +"Section_accelerate"_Section_accelerate.html. + +IMPORTANT NOTE: Using {pppm/stagger} may not give the same increase in +the accuracy of energy and pressure as it does in forces, so some +caution must be used if energy and/or pressure are quantities of +interest, such as when using a barostat. + :line The {pppm/disp} and {pppm/disp/tip4p} styles add a mesh-based long-range @@ -304,6 +331,13 @@ Adam Hilger, NY (1989). :link(Pollock) [(Pollock)] Pollock and Glosli, Comp Phys Comm, 95, 93 (1996). +:link(Cerutti) +[(Cerutti)] Cerutti, Duke, Darden, Lybrand, Journal of Chemical Theory +and Computation 5, 2322 (2009) + +:link(Neelov) +[(Neelov)] Neelov, Holm, J Chem Phys 132, 234103 (2010) + :link(Veld) [(Veld)] In 't Veld, Ismail, Grest, J Chem Phys, 127, 144711 (2007). From 440d38977984f6fc9475b65c28f88f9794411a83 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:50:33 +0000 Subject: [PATCH 28/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10130 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm.cpp | 6784 ++++++++++++++++++++++--------------------- src/KSPACE/pppm.h | 670 ++--- src/kspace.cpp | 893 +++--- src/kspace.h | 398 +-- 4 files changed, 4378 insertions(+), 4367 deletions(-) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 5bacae48be..44e03aeb35 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -1,3388 +1,3396 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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: Roy Pollock (LLNL), Paul Crozier (SNL) - per-atom energy/virial & group/group energy/force added by Stan Moore (BYU) - analytic diff (2 FFT) option added by Rolf Isele-Holder (Aachen University) - triclinic added by Stan Moore (SNL) -------------------------------------------------------------------------- */ - -#include "lmptype.h" -#include "mpi.h" -#include "string.h" -#include "stdio.h" -#include "stdlib.h" -#include "math.h" -#include "pppm.h" -#include "atom.h" -#include "comm.h" -#include "commgrid.h" -#include "neighbor.h" -#include "force.h" -#include "pair.h" -#include "bond.h" -#include "angle.h" -#include "domain.h" -#include "fft3d_wrap.h" -#include "remap_wrap.h" -#include "memory.h" -#include "error.h" - -#include "math_const.h" -#include "math_special.h" - -using namespace LAMMPS_NS; -using namespace MathConst; -using namespace MathSpecial; - -#define MAXORDER 7 -#define OFFSET 16384 -#define SMALL 0.00001 -#define LARGE 10000.0 -#define EPS_HOC 1.0e-7 - -enum{REVERSE_RHO}; -enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; - -#ifdef FFT_SINGLE -#define ZEROF 0.0f -#define ONEF 1.0f -#else -#define ZEROF 0.0 -#define ONEF 1.0 -#endif - -/* ---------------------------------------------------------------------- */ - -PPPM::PPPM(LAMMPS *lmp, int narg, char **arg) : KSpace(lmp, narg, arg) -{ - if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm command"); - - pppmflag = 1; - group_group_enable = 1; - - accuracy_relative = atof(arg[0]); - - nfactors = 3; - factors = new int[nfactors]; - factors[0] = 2; - factors[1] = 3; - factors[2] = 5; - - MPI_Comm_rank(world,&me); - MPI_Comm_size(world,&nprocs); - - density_brick = vdx_brick = vdy_brick = vdz_brick = NULL; - density_fft = NULL; - u_brick = NULL; - v0_brick = v1_brick = v2_brick = v3_brick = v4_brick = v5_brick = NULL; - greensfn = NULL; - work1 = work2 = NULL; - vg = NULL; - fkx = fky = fkz = NULL; - - sf_precoeff1 = sf_precoeff2 = sf_precoeff3 = - sf_precoeff4 = sf_precoeff5 = sf_precoeff6 = NULL; - - density_A_brick = density_B_brick = NULL; - density_A_fft = density_B_fft = NULL; - - gf_b = NULL; - rho1d = rho_coeff = drho1d = drho_coeff = NULL; - - fft1 = fft2 = NULL; - remap = NULL; - cg = NULL; - cg_peratom = NULL; - - nmax = 0; - part2grid = NULL; - - peratom_allocate_flag = 0; - group_allocate_flag = 0; - - // define acons coefficients for estimation of kspace errors - // see JCP 109, pg 7698 for derivation of coefficients - // higher order coefficients may be computed if needed - - memory->create(acons,8,7,"pppm:acons"); - acons[1][0] = 2.0 / 3.0; - acons[2][0] = 1.0 / 50.0; - acons[2][1] = 5.0 / 294.0; - acons[3][0] = 1.0 / 588.0; - acons[3][1] = 7.0 / 1440.0; - acons[3][2] = 21.0 / 3872.0; - acons[4][0] = 1.0 / 4320.0; - acons[4][1] = 3.0 / 1936.0; - acons[4][2] = 7601.0 / 2271360.0; - acons[4][3] = 143.0 / 28800.0; - acons[5][0] = 1.0 / 23232.0; - acons[5][1] = 7601.0 / 13628160.0; - acons[5][2] = 143.0 / 69120.0; - acons[5][3] = 517231.0 / 106536960.0; - acons[5][4] = 106640677.0 / 11737571328.0; - acons[6][0] = 691.0 / 68140800.0; - acons[6][1] = 13.0 / 57600.0; - acons[6][2] = 47021.0 / 35512320.0; - acons[6][3] = 9694607.0 / 2095994880.0; - acons[6][4] = 733191589.0 / 59609088000.0; - acons[6][5] = 326190917.0 / 11700633600.0; - acons[7][0] = 1.0 / 345600.0; - acons[7][1] = 3617.0 / 35512320.0; - acons[7][2] = 745739.0 / 838397952.0; - acons[7][3] = 56399353.0 / 12773376000.0; - acons[7][4] = 25091609.0 / 1560084480.0; - acons[7][5] = 1755948832039.0 / 36229939200000.0; - acons[7][6] = 4887769399.0 / 37838389248.0; -} - -/* ---------------------------------------------------------------------- - free all memory -------------------------------------------------------------------------- */ - -PPPM::~PPPM() -{ - delete [] factors; - deallocate(); - if (peratom_allocate_flag) deallocate_peratom(); - if (group_allocate_flag) deallocate_groups(); - memory->destroy(part2grid); - memory->destroy(acons); -} - -/* ---------------------------------------------------------------------- - called once before run -------------------------------------------------------------------------- */ - -void PPPM::init() -{ - if (me == 0) { - if (screen) fprintf(screen,"PPPM initialization ...\n"); - if (logfile) fprintf(logfile,"PPPM initialization ...\n"); - } - - // error check - - triclinic_check(); - if (domain->triclinic && differentiation_flag == 1) - error->all(FLERR,"Cannot (yet) use PPPM with triclinic box " - "and kspace_modify diff a'"); - if (domain->triclinic && slabflag) - error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and " - "slab correction"); - if (domain->dimension == 2) error->all(FLERR, - "Cannot use PPPM with 2d simulation"); - - if (!atom->q_flag) error->all(FLERR,"Kspace style requires atom attribute q"); - - if (slabflag == 0 && domain->nonperiodic > 0) - error->all(FLERR,"Cannot use nonperiodic boundaries with PPPM"); - if (slabflag) { - if (domain->xperiodic != 1 || domain->yperiodic != 1 || - domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) - error->all(FLERR,"Incorrect boundaries with slab PPPM"); - } - - if (order < 2 || order > MAXORDER) { - char str[128]; - sprintf(str,"PPPM order cannot be < 2 or > than %d",MAXORDER); - error->all(FLERR,str); - } - - // extract short-range Coulombic cutoff from pair style - - triclinic = domain->triclinic; - scale = 1.0; - - pair_check(); - - int itmp = 0; - double *p_cutoff = (double *) force->pair->extract("cut_coul",itmp); - if (p_cutoff == NULL) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - cutoff = *p_cutoff; - - // if kspace is TIP4P, extract TIP4P params from pair style - // bond/angle are not yet init(), so insure equilibrium request is valid - - qdist = 0.0; - - if (tip4pflag) { - double *p_qdist = (double *) force->pair->extract("qdist",itmp); - int *p_typeO = (int *) force->pair->extract("typeO",itmp); - int *p_typeH = (int *) force->pair->extract("typeH",itmp); - int *p_typeA = (int *) force->pair->extract("typeA",itmp); - int *p_typeB = (int *) force->pair->extract("typeB",itmp); - if (!p_qdist || !p_typeO || !p_typeH || !p_typeA || !p_typeB) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - qdist = *p_qdist; - typeO = *p_typeO; - typeH = *p_typeH; - int typeA = *p_typeA; - int typeB = *p_typeB; - - if (force->angle == NULL || force->bond == NULL) - error->all(FLERR,"Bond and angle potentials must be defined for TIP4P"); - if (typeA < 1 || typeA > atom->nangletypes || - force->angle->setflag[typeA] == 0) - error->all(FLERR,"Bad TIP4P angle type for PPPM/TIP4P"); - if (typeB < 1 || typeB > atom->nbondtypes || - force->bond->setflag[typeB] == 0) - error->all(FLERR,"Bad TIP4P bond type for PPPM/TIP4P"); - double theta = force->angle->equilibrium_angle(typeA); - double blen = force->bond->equilibrium_distance(typeB); - alpha = qdist / (cos(0.5*theta) * blen); - if (domain->triclinic) - error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and TIP4P"); - } - - // compute qsum & qsqsum and warn if not charge-neutral - - qsum = qsqsum = 0.0; - for (int i = 0; i < atom->nlocal; i++) { - qsum += atom->q[i]; - qsqsum += atom->q[i]*atom->q[i]; - } - - double tmp; - MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); - qsum = tmp; - MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); - qsqsum = tmp; - q2 = qsqsum * force->qqrd2e / force->dielectric; - - if (qsqsum == 0.0) - error->all(FLERR,"Cannot use kspace solver on system with no charge"); - if (fabs(qsum) > SMALL && me == 0) { - char str[128]; - sprintf(str,"System is not charge neutral, net charge = %g",qsum); - error->warning(FLERR,str); - } - - // set accuracy (force units) from accuracy_relative or accuracy_absolute - - if (accuracy_absolute >= 0.0) accuracy = accuracy_absolute; - else accuracy = accuracy_relative * two_charge_force; - - // free all arrays previously allocated - - deallocate(); - if (peratom_allocate_flag) deallocate_peratom(); - if (group_allocate_flag) deallocate_groups(); - - // setup FFT grid resolution and g_ewald - // normally one iteration thru while loop is all that is required - // if grid stencil does not extend beyond neighbor proc - // or overlap is allowed, then done - // else reduce order and try again - - int (*procneigh)[2] = comm->procneigh; - - CommGrid *cgtmp = NULL; - int iteration = 0; - - while (order >= minorder) { - if (iteration && me == 0) - error->warning(FLERR,"Reducing PPPM order b/c stencil extends " - "beyond nearest neighbor processor"); - - set_grid_global(); - set_grid_local(); - if (overlap_allowed) break; - - cgtmp = new CommGrid(lmp,world,1,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); - cgtmp->ghost_notify(); - if (!cgtmp->ghost_overlap()) break; - delete cgtmp; - - order--; - iteration++; - } - - if (order < minorder) error->all(FLERR,"PPPM order < minimum allowed order"); - if (!overlap_allowed && cgtmp->ghost_overlap()) - error->all(FLERR,"PPPM grid stencil extends " - "beyond nearest neighbor processor"); - if (cgtmp) delete cgtmp; - - // adjust g_ewald - - if (!gewaldflag) adjust_gewald(); - - // calculate the final accuracy - - double estimated_accuracy = final_accuracy(); - - // print stats - - int ngrid_max,nfft_both_max; - MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world); - MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world); - - if (me == 0) { - -#ifdef FFT_SINGLE - const char fft_prec[] = "single"; -#else - const char fft_prec[] = "double"; -#endif - - if (screen) { - fprintf(screen," G vector (1/distance)= %g\n",g_ewald); - fprintf(screen," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); - fprintf(screen," stencil order = %d\n",order); - fprintf(screen," estimated absolute RMS force accuracy = %g\n", - estimated_accuracy); - fprintf(screen," estimated relative force accuracy = %g\n", - estimated_accuracy/two_charge_force); - fprintf(screen," using %s precision FFTs\n",fft_prec); - fprintf(screen," 3d grid and FFT values/proc = %d %d\n", - ngrid_max,nfft_both_max); - } - if (logfile) { - fprintf(logfile," G vector (1/distance) = %g\n",g_ewald); - fprintf(logfile," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); - fprintf(logfile," stencil order = %d\n",order); - fprintf(logfile," estimated absolute RMS force accuracy = %g\n", - estimated_accuracy); - fprintf(logfile," estimated relative force accuracy = %g\n", - estimated_accuracy/two_charge_force); - fprintf(logfile," using %s precision FFTs\n",fft_prec); - fprintf(logfile," 3d grid and FFT values/proc = %d %d\n", - ngrid_max,nfft_both_max); - } - } - - // allocate K-space dependent memory - // don't invoke allocate peratom() or group(), will be allocated when needed - - allocate(); - cg->ghost_notify(); - cg->setup(); - - // pre-compute Green's function denomiator expansion - // pre-compute 1d charge distribution coefficients - - compute_gf_denom(); - if (differentiation_flag == 1) compute_sf_precoeff(); - compute_rho_coeff(); -} - -/* ---------------------------------------------------------------------- - adjust PPPM coeffs, called initially and whenever volume has changed -------------------------------------------------------------------------- */ - -void PPPM::setup() -{ - if (triclinic) { - setup_triclinic(); - return; - } - - int i,j,k,n; - double *prd; - - // volume-dependent factors - // adjust z dimension for 2d slab PPPM - // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 - - if (triclinic == 0) prd = domain->prd; - else prd = domain->prd_lamda; - - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - double zprd_slab = zprd*slab_volfactor; - volume = xprd * yprd * zprd_slab; - - delxinv = nx_pppm/xprd; - delyinv = ny_pppm/yprd; - delzinv = nz_pppm/zprd_slab; - - delvolinv = delxinv*delyinv*delzinv; - - double unitkx = (MY_2PI/xprd); - double unitky = (MY_2PI/yprd); - double unitkz = (MY_2PI/zprd_slab); - - // fkx,fky,fkz for my FFT grid pts - - double per; - - for (i = nxlo_fft; i <= nxhi_fft; i++) { - per = i - nx_pppm*(2*i/nx_pppm); - fkx[i] = unitkx*per; - } - - for (i = nylo_fft; i <= nyhi_fft; i++) { - per = i - ny_pppm*(2*i/ny_pppm); - fky[i] = unitky*per; - } - - for (i = nzlo_fft; i <= nzhi_fft; i++) { - per = i - nz_pppm*(2*i/nz_pppm); - fkz[i] = unitkz*per; - } - - // virial coefficients - - double sqk,vterm; - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) { - for (j = nylo_fft; j <= nyhi_fft; j++) { - for (i = nxlo_fft; i <= nxhi_fft; i++) { - sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k]; - if (sqk == 0.0) { - vg[n][0] = 0.0; - vg[n][1] = 0.0; - vg[n][2] = 0.0; - vg[n][3] = 0.0; - vg[n][4] = 0.0; - vg[n][5] = 0.0; - } else { - vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); - vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i]; - vg[n][1] = 1.0 + vterm*fky[j]*fky[j]; - vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k]; - vg[n][3] = vterm*fkx[i]*fky[j]; - vg[n][4] = vterm*fkx[i]*fkz[k]; - vg[n][5] = vterm*fky[j]*fkz[k]; - } - n++; - } - } - } - - if (differentiation_flag == 1) compute_gf_ad(); - else compute_gf_ik(); -} - -/* ---------------------------------------------------------------------- - adjust PPPM coeffs, called initially and whenever volume has changed - for a triclinic system -------------------------------------------------------------------------- */ - -void PPPM::setup_triclinic() -{ - int i,j,k,n; - double *prd; - - // volume-dependent factors - // adjust z dimension for 2d slab PPPM - // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 - - prd = domain->prd; - - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - double zprd_slab = zprd*slab_volfactor; - volume = xprd * yprd * zprd_slab; - - // use lamda (0-1) coordinates - - delxinv = nx_pppm; - delyinv = ny_pppm; - delzinv = nz_pppm; - delvolinv = delxinv*delyinv*delzinv/volume; - - // fkx,fky,fkz for my FFT grid pts - - double per_i,per_j,per_k; - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) { - per_k = k - nz_pppm*(2*k/nz_pppm); - for (j = nylo_fft; j <= nyhi_fft; j++) { - per_j = j - ny_pppm*(2*j/ny_pppm); - for (i = nxlo_fft; i <= nxhi_fft; i++) { - per_i = i - nx_pppm*(2*i/nx_pppm); - - double unitk_lamda[3]; - unitk_lamda[0] = 2.0*MY_PI*per_i; - unitk_lamda[1] = 2.0*MY_PI*per_j; - unitk_lamda[2] = 2.0*MY_PI*per_k; - x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); - fkx[n] = unitk_lamda[0]; - fky[n] = unitk_lamda[1]; - fkz[n] = unitk_lamda[2]; - n++; - } - } - } - - // virial coefficients - - double sqk,vterm; - - for (n = 0; n < nfft; n++) { - sqk = fkx[n]*fkx[n] + fky[n]*fky[n] + fkz[n]*fkz[n]; - if (sqk == 0.0) { - vg[n][0] = 0.0; - vg[n][1] = 0.0; - vg[n][2] = 0.0; - vg[n][3] = 0.0; - vg[n][4] = 0.0; - vg[n][5] = 0.0; - } else { - vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); - vg[n][0] = 1.0 + vterm*fkx[n]*fkx[n]; - vg[n][1] = 1.0 + vterm*fky[n]*fky[n]; - vg[n][2] = 1.0 + vterm*fkz[n]*fkz[n]; - vg[n][3] = vterm*fkx[n]*fky[n]; - vg[n][4] = vterm*fkx[n]*fkz[n]; - vg[n][5] = vterm*fky[n]*fkz[n]; - } - } - - compute_gf_ik_triclinic(); -} - -/* ---------------------------------------------------------------------- - reset local grid arrays and communication stencils - called by fix balance b/c it changed sizes of processor sub-domains -------------------------------------------------------------------------- */ - -void PPPM::setup_grid() -{ - // free all arrays previously allocated - - deallocate(); - if (peratom_allocate_flag) deallocate_peratom(); - if (group_allocate_flag) deallocate_groups(); - - // reset portion of global grid that each proc owns - - set_grid_local(); - - // reallocate K-space dependent memory - // check if grid communication is now overlapping if not allowed - // don't invoke allocate peratom() or group(), will be allocated when needed - - allocate(); - - cg->ghost_notify(); - if (overlap_allowed == 0 && cg->ghost_overlap()) - error->all(FLERR,"PPPM grid stencil extends " - "beyond nearest neighbor processor"); - cg->setup(); - - // pre-compute Green's function denomiator expansion - // pre-compute 1d charge distribution coefficients - - compute_gf_denom(); - if (differentiation_flag == 1) compute_sf_precoeff(); - compute_rho_coeff(); - - // pre-compute volume-dependent coeffs - - setup(); -} - -/* ---------------------------------------------------------------------- - compute the PPPM long-range force, energy, virial -------------------------------------------------------------------------- */ - -void PPPM::compute(int eflag, int vflag) -{ - int i,j; - - // set energy/virial flags - // invoke allocate_peratom() if needed for first time - - if (eflag || vflag) ev_setup(eflag,vflag); - else evflag = evflag_atom = eflag_global = vflag_global = - eflag_atom = vflag_atom = 0; - - if (evflag_atom && !peratom_allocate_flag) { - allocate_peratom(); - cg_peratom->ghost_notify(); - cg_peratom->setup(); - } - - // convert atoms from box to lamda coords - - if (triclinic == 0) boxlo = domain->boxlo; - else { - boxlo = domain->boxlo_lamda; - domain->x2lamda(atom->nlocal); - } - - // extend size of per-atom arrays if necessary - - if (atom->nlocal > nmax) { - memory->destroy(part2grid); - nmax = atom->nmax; - memory->create(part2grid,nmax,3,"pppm:part2grid"); - } - - // find grid points for all my particles - // map my particle charge onto my local 3d density grid - - particle_map(); - make_rho(); - - // all procs communicate density values from their ghost cells - // to fully sum contribution in their 3d bricks - // remap from 3d decomposition to FFT decomposition - - cg->reverse_comm(this,REVERSE_RHO); - brick2fft(); - - // compute potential gradient on my FFT grid and - // portion of e_long on this proc's FFT grid - // return gradients (electric fields) in 3d brick decomposition - // also performs per-atom calculations via poisson_peratom() - - poisson(); - - // all procs communicate E-field values - // to fill ghost cells surrounding their 3d bricks - - if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); - else cg->forward_comm(this,FORWARD_IK); - - // extra per-atom energy/virial communication - - if (evflag_atom) { - if (differentiation_flag == 1 && vflag_atom) - cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); - else if (differentiation_flag == 0) - cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); - } - - // calculate the force on my particles - - fieldforce(); - - // extra per-atom energy/virial communication - - if (evflag_atom) fieldforce_peratom(); - - // sum global energy across procs and add in volume-dependent term - - const double qscale = force->qqrd2e * scale; - - if (eflag_global) { - double energy_all; - MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); - energy = energy_all; - - energy *= 0.5*volume; - energy -= g_ewald*qsqsum/MY_PIS + - MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); - energy *= qscale; - } - - // sum global virial across procs - - if (vflag_global) { - double virial_all[6]; - MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); - for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]; - } - - // per-atom energy/virial - // energy includes self-energy correction - // notal accounts for TIP4P tallying eatom/vatom for ghost atoms - - if (evflag_atom) { - double *q = atom->q; - int nlocal = atom->nlocal; - int ntotal = nlocal; - if (tip4pflag) ntotal += atom->nghost; - - if (eflag_atom) { - for (i = 0; i < nlocal; i++) { - eatom[i] *= 0.5; - eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / - (g_ewald*g_ewald*volume); - eatom[i] *= qscale; - } - for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; - } - - if (vflag_atom) { - for (i = 0; i < ntotal; i++) - for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; - } - } - - // 2d slab correction - - if (slabflag == 1) slabcorr(); - - // convert atoms back from lamda to box coords - - if (triclinic) domain->lamda2x(atom->nlocal); -} - -/* ---------------------------------------------------------------------- - allocate memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::allocate() -{ - memory->create3d_offset(density_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:density_brick"); - - memory->create(density_fft,nfft_both,"pppm:density_fft"); - memory->create(greensfn,nfft_both,"pppm:greensfn"); - memory->create(work1,2*nfft_both,"pppm:work1"); - memory->create(work2,2*nfft_both,"pppm:work2"); - memory->create(vg,nfft_both,6,"pppm:vg"); - - if (triclinic == 0) { - memory->create1d_offset(fkx,nxlo_fft,nxhi_fft,"pppm:fkx"); - memory->create1d_offset(fky,nylo_fft,nyhi_fft,"pppm:fky"); - memory->create1d_offset(fkz,nzlo_fft,nzhi_fft,"pppm:fkz"); - } else { - memory->create(fkx,nfft_both,"pppm:fkx"); - memory->create(fky,nfft_both,"pppm:fky"); - memory->create(fkz,nfft_both,"pppm:fkz"); - } - - if (differentiation_flag == 1) { - memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:u_brick"); - - memory->create(sf_precoeff1,nfft_both,"pppm:sf_precoeff1"); - memory->create(sf_precoeff2,nfft_both,"pppm:sf_precoeff2"); - memory->create(sf_precoeff3,nfft_both,"pppm:sf_precoeff3"); - memory->create(sf_precoeff4,nfft_both,"pppm:sf_precoeff4"); - memory->create(sf_precoeff5,nfft_both,"pppm:sf_precoeff5"); - memory->create(sf_precoeff6,nfft_both,"pppm:sf_precoeff6"); - - } else { - memory->create3d_offset(vdx_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:vdx_brick"); - memory->create3d_offset(vdy_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:vdy_brick"); - memory->create3d_offset(vdz_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:vdz_brick"); - } - - // summation coeffs - - memory->create(gf_b,order,"pppm:gf_b"); - memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); - memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); - memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); - memory->create2d_offset(drho_coeff,order,(1-order)/2,order/2, - "pppm:drho_coeff"); - - // create 2 FFTs and a Remap - // 1st FFT keeps data in FFT decompostion - // 2nd FFT returns data in 3d brick decomposition - // remap takes data from 3d brick to FFT decomposition - - int tmp; - - fft1 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - 0,0,&tmp); - - fft2 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - 0,0,&tmp); - - remap = new Remap(lmp,world, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - 1,0,0,FFT_PRECISION); - - // create ghost grid object for rho and electric field communication - - int (*procneigh)[2] = comm->procneigh; - - if (differentiation_flag == 1) - cg = new CommGrid(lmp,world,1,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); - else - cg = new CommGrid(lmp,world,3,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); -} - -/* ---------------------------------------------------------------------- - deallocate memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::deallocate() -{ - memory->destroy3d_offset(density_brick,nzlo_out,nylo_out,nxlo_out); - - if (differentiation_flag == 1) { - memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy(sf_precoeff1); - memory->destroy(sf_precoeff2); - memory->destroy(sf_precoeff3); - memory->destroy(sf_precoeff4); - memory->destroy(sf_precoeff5); - memory->destroy(sf_precoeff6); - } else { - memory->destroy3d_offset(vdx_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(vdy_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(vdz_brick,nzlo_out,nylo_out,nxlo_out); - } - - memory->destroy(density_fft); - memory->destroy(greensfn); - memory->destroy(work1); - memory->destroy(work2); - memory->destroy(vg); - - if (triclinic == 0) { - memory->destroy1d_offset(fkx,nxlo_fft); - memory->destroy1d_offset(fky,nylo_fft); - memory->destroy1d_offset(fkz,nzlo_fft); - } else { - memory->destroy(fkx); - memory->destroy(fky); - memory->destroy(fkz); - } - - memory->destroy(gf_b); - memory->destroy2d_offset(rho1d,-order/2); - memory->destroy2d_offset(drho1d,-order/2); - memory->destroy2d_offset(rho_coeff,(1-order)/2); - memory->destroy2d_offset(drho_coeff,(1-order)/2); - - delete fft1; - delete fft2; - delete remap; - delete cg; -} - -/* ---------------------------------------------------------------------- - allocate per-atom memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::allocate_peratom() -{ - peratom_allocate_flag = 1; - - if (differentiation_flag != 1) - memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:u_brick"); - - memory->create3d_offset(v0_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v0_brick"); - - memory->create3d_offset(v1_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v1_brick"); - memory->create3d_offset(v2_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v2_brick"); - memory->create3d_offset(v3_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v3_brick"); - memory->create3d_offset(v4_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v4_brick"); - memory->create3d_offset(v5_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v5_brick"); - - // create ghost grid object for rho and electric field communication - - int (*procneigh)[2] = comm->procneigh; - - if (differentiation_flag == 1) - cg_peratom = - new CommGrid(lmp,world,6,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); - else - cg_peratom = - new CommGrid(lmp,world,7,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); -} - -/* ---------------------------------------------------------------------- - deallocate per-atom memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::deallocate_peratom() -{ - peratom_allocate_flag = 0; - - memory->destroy3d_offset(v0_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v1_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v2_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v3_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v4_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v5_brick,nzlo_out,nylo_out,nxlo_out); - - if (differentiation_flag != 1) - memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); - - delete cg_peratom; -} - -/* ---------------------------------------------------------------------- - set global size of PPPM grid = nx,ny,nz_pppm - used for charge accumulation, FFTs, and electric field interpolation -------------------------------------------------------------------------- */ - -void PPPM::set_grid_global() -{ - // use xprd,yprd,zprd (even if triclinic, and then scale later) - // adjust z dimension for 2d slab PPPM - // 3d PPPM just uses zprd since slab_volfactor = 1.0 - - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - double zprd_slab = zprd*slab_volfactor; - - // make initial g_ewald estimate - // based on desired accuracy and real space cutoff - // fluid-occupied volume used to estimate real-space error - // zprd used rather than zprd_slab - - double h; - bigint natoms = atom->natoms; - - if (!gewaldflag) { - if (accuracy <= 0.0) - error->all(FLERR,"KSpace accuracy must be > 0"); - g_ewald = accuracy*sqrt(natoms*cutoff*xprd*yprd*zprd) / (2.0*q2); - if (g_ewald >= 1.0) g_ewald = (1.35 - 0.15*log(accuracy))/cutoff; - else g_ewald = sqrt(-log(g_ewald)) / cutoff; - } - - // set optimal nx_pppm,ny_pppm,nz_pppm based on order and accuracy - // nz_pppm uses extended zprd_slab instead of zprd - // reduce it until accuracy target is met - - if (!gridflag) { - - if (differentiation_flag == 1) { - - h = h_x = h_y = h_z = 4.0/g_ewald; - int count = 0; - while (1) { - - // set grid dimension - nx_pppm = static_cast (xprd/h_x); - ny_pppm = static_cast (yprd/h_y); - nz_pppm = static_cast (zprd_slab/h_z); - - if (nx_pppm <= 1) nx_pppm = 2; - if (ny_pppm <= 1) ny_pppm = 2; - if (nz_pppm <= 1) nz_pppm = 2; - - //set local grid dimension - int npey_fft,npez_fft; - if (nz_pppm >= nprocs) { - npey_fft = 1; - npez_fft = nprocs; - } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); - - int me_y = me % npey_fft; - int me_z = me / npey_fft; - - nxlo_fft = 0; - nxhi_fft = nx_pppm - 1; - nylo_fft = me_y*ny_pppm/npey_fft; - nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; - nzlo_fft = me_z*nz_pppm/npez_fft; - nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; - - double df_kspace = compute_df_kspace(); - - count++; - - // break loop if the accuracy has been reached or - // too many loops have been performed - - if (df_kspace <= accuracy) break; - if (count > 500) error->all(FLERR, "Could not compute grid size"); - h *= 0.95; - h_x = h_y = h_z = h; - } - - } else { - - double err; - h_x = h_y = h_z = 1.0/g_ewald; - - nx_pppm = static_cast (xprd/h_x) + 1; - ny_pppm = static_cast (yprd/h_y) + 1; - nz_pppm = static_cast (zprd_slab/h_z) + 1; - - err = estimate_ik_error(h_x,xprd,natoms); - while (err > accuracy) { - err = estimate_ik_error(h_x,xprd,natoms); - nx_pppm++; - h_x = xprd/nx_pppm; - } - - err = estimate_ik_error(h_y,yprd,natoms); - while (err > accuracy) { - err = estimate_ik_error(h_y,yprd,natoms); - ny_pppm++; - h_y = yprd/ny_pppm; - } - - err = estimate_ik_error(h_z,zprd_slab,natoms); - while (err > accuracy) { - err = estimate_ik_error(h_z,zprd_slab,natoms); - nz_pppm++; - h_z = zprd_slab/nz_pppm; - } - } - - // scale grid for triclinic skew - - if (triclinic) { - double tmp[3]; - tmp[0] = nx_pppm/xprd; - tmp[1] = ny_pppm/yprd; - tmp[2] = nz_pppm/zprd; - lamda2xT(&tmp[0],&tmp[0]); - nx_pppm = static_cast(tmp[0]) + 1; - ny_pppm = static_cast(tmp[1]) + 1; - nz_pppm = static_cast(tmp[2]) + 1; - } - } - - // boost grid size until it is factorable - - while (!factorable(nx_pppm)) nx_pppm++; - while (!factorable(ny_pppm)) ny_pppm++; - while (!factorable(nz_pppm)) nz_pppm++; - - if (triclinic == 0) { - h_x = xprd/nx_pppm; - h_y = yprd/ny_pppm; - h_z = zprd_slab/nz_pppm; - } else { - double tmp[3]; - tmp[0] = nx_pppm; - tmp[1] = ny_pppm; - tmp[2] = nz_pppm; - x2lamdaT(&tmp[0],&tmp[0]); - h_x = 1.0/tmp[0]; - h_y = 1.0/tmp[1]; - h_z = 1.0/tmp[2]; - } - - if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET) - error->all(FLERR,"PPPM grid is too large"); -} - -/* ---------------------------------------------------------------------- - check if all factors of n are in list of factors - return 1 if yes, 0 if no -------------------------------------------------------------------------- */ - -int PPPM::factorable(int n) -{ - int i; - - while (n > 1) { - for (i = 0; i < nfactors; i++) { - if (n % factors[i] == 0) { - n /= factors[i]; - break; - } - } - if (i == nfactors) return 0; - } - - return 1; -} - -/* ---------------------------------------------------------------------- - compute estimated kspace force error -------------------------------------------------------------------------- */ - -double PPPM::compute_df_kspace() -{ - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - double zprd_slab = zprd*slab_volfactor; - bigint natoms = atom->natoms; - double df_kspace = 0.0; - if (differentiation_flag == 1) { - double qopt = compute_qopt(); - df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab); - } else { - double lprx = estimate_ik_error(h_x,xprd,natoms); - double lpry = estimate_ik_error(h_y,yprd,natoms); - double lprz = estimate_ik_error(h_z,zprd_slab,natoms); - df_kspace = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); - } - return df_kspace; -} - -/* ---------------------------------------------------------------------- - compute qopt -------------------------------------------------------------------------- */ - -double PPPM::compute_qopt() -{ - double qopt = 0.0; - double *prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - volume = xprd * yprd * zprd_slab; - - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double u1, u2, sqk; - double sum1,sum2,sum3,sum4,dot2; - - int k,l,m,nx,ny,nz; - const int twoorder = 2*order; - - for (m = nzlo_fft; m <= nzhi_fft; m++) { - const int mper = m - nz_pppm*(2*m/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - const int lper = l - ny_pppm*(2*l/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - const int kper = k - nx_pppm*(2*k/nx_pppm); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - - sum1 = 0.0; - sum2 = 0.0; - sum3 = 0.0; - sum4 = 0.0; - for (nx = -2; nx <= 2; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - qx *= qx; - - for (ny = -2; ny <= 2; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - qy *= qy; - - for (nz = -2; nz <= 2; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - qz *= qz; - - dot2 = qx+qy+qz; - u1 = sx*sy*sz; - u2 = wx*wy*wz; - sum1 += u1*u1/dot2*MY_4PI*MY_4PI; - sum2 += u1 * u2 * MY_4PI; - sum3 += u2; - sum4 += dot2*u2; - } - } - } - sum2 *= sum2; - qopt += sum1 - sum2/(sum3*sum4); - } - } - } - } - double qopt_all; - MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); - return qopt_all; -} - -/* ---------------------------------------------------------------------- - estimate kspace force error for ik method -------------------------------------------------------------------------- */ - -double PPPM::estimate_ik_error(double h, double prd, bigint natoms) -{ - double sum = 0.0; - for (int m = 0; m < order; m++) - sum += acons[order][m] * pow(h*g_ewald,2.0*m); - double value = q2 * pow(h*g_ewald,(double)order) * - sqrt(g_ewald*prd*sqrt(MY_2PI)*sum/natoms) / (prd*prd); - - return value; -} - -/* ---------------------------------------------------------------------- - adjust the g_ewald parameter to near its optimal value - using a Newton-Raphson solver -------------------------------------------------------------------------- */ - -void PPPM::adjust_gewald() -{ - double dx; - - for (int i = 0; i < LARGE; i++) { - dx = newton_raphson_f() / derivf(); - g_ewald -= dx; - if (fabs(newton_raphson_f()) < SMALL) return; - } - - char str[128]; - sprintf(str, "Could not compute g_ewald"); - error->all(FLERR, str); -} - -/* ---------------------------------------------------------------------- - Calculate f(x) using Newton-Raphson solver - ------------------------------------------------------------------------- */ - -double PPPM::newton_raphson_f() -{ - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - bigint natoms = atom->natoms; - - double df_rspace = 2.0*q2*exp(-g_ewald*g_ewald*cutoff*cutoff) / - sqrt(natoms*cutoff*xprd*yprd*zprd); - - double df_kspace = compute_df_kspace(); - - return df_rspace - df_kspace; -} - -/* ---------------------------------------------------------------------- - Calculate numerical derivative f'(x) using forward difference - [f(x + h) - f(x)] / h - ------------------------------------------------------------------------- */ - -double PPPM::derivf() -{ - double h = 0.000001; //Derivative step-size - double df,f1,f2,g_ewald_old; - - f1 = newton_raphson_f(); - g_ewald_old = g_ewald; - g_ewald += h; - f2 = newton_raphson_f(); - g_ewald = g_ewald_old; - df = (f2 - f1)/h; - - return df; -} - -/* ---------------------------------------------------------------------- - Calculate the final estimate of the accuracy -------------------------------------------------------------------------- */ - -double PPPM::final_accuracy() -{ - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - double zprd_slab = zprd*slab_volfactor; - bigint natoms = atom->natoms; - - double df_kspace = compute_df_kspace(); - double q2_over_sqrt = q2 / sqrt(natoms*cutoff*xprd*yprd*zprd); - double df_rspace = 2.0 * q2_over_sqrt * exp(-g_ewald*g_ewald*cutoff*cutoff); - double df_table = estimate_table_accuracy(q2_over_sqrt,df_rspace); - double estimated_accuracy = sqrt(df_kspace*df_kspace + df_rspace*df_rspace + - df_table*df_table); - - return estimated_accuracy; -} - -/* ---------------------------------------------------------------------- - set local subset of PPPM/FFT grid that I own - n xyz lo/hi in = 3d brick that I own (inclusive) - n xyz lo/hi out = 3d brick + ghost cells in 6 directions (inclusive) - n xyz lo/hi fft = FFT columns that I own (all of x dim, 2d decomp in yz) -------------------------------------------------------------------------- */ - -void PPPM::set_grid_local() -{ - // global indices of PPPM grid range from 0 to N-1 - // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of - // global PPPM grid that I own without ghost cells - // for slab PPPM, assign z grid as if it were not extended - - nxlo_in = static_cast (comm->xsplit[comm->myloc[0]] * nx_pppm); - nxhi_in = static_cast (comm->xsplit[comm->myloc[0]+1] * nx_pppm) - 1; - - nylo_in = static_cast (comm->ysplit[comm->myloc[1]] * ny_pppm); - nyhi_in = static_cast (comm->ysplit[comm->myloc[1]+1] * ny_pppm) - 1; - - nzlo_in = static_cast - (comm->zsplit[comm->myloc[2]] * nz_pppm/slab_volfactor); - nzhi_in = static_cast - (comm->zsplit[comm->myloc[2]+1] * nz_pppm/slab_volfactor) - 1; - - // nlower,nupper = stencil size for mapping particles to PPPM grid - - nlower = -(order-1)/2; - nupper = order/2; - - // shift values for particle <-> grid mapping - // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 - - if (order % 2) shift = OFFSET + 0.5; - else shift = OFFSET; - if (order % 2) shiftone = 0.0; - else shiftone = 0.5; - - // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of - // global PPPM grid that my particles can contribute charge to - // effectively nlo_in,nhi_in + ghost cells - // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest - // position a particle in my box can be at - // dist[3] = particle position bound = subbox + skin/2.0 + qdist - // qdist = offset due to TIP4P fictitious charge - // convert to triclinic if necessary - // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping - // for slab PPPM, assign z grid as if it were not extended - - double *prd,*sublo,*subhi; - - if (triclinic == 0) { - prd = domain->prd; - boxlo = domain->boxlo; - sublo = domain->sublo; - subhi = domain->subhi; - } else { - prd = domain->prd_lamda; - boxlo = domain->boxlo_lamda; - sublo = domain->sublo_lamda; - subhi = domain->subhi_lamda; - } - - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - double zprd_slab = zprd*slab_volfactor; - - double dist[3]; - double cuthalf = 0.5*neighbor->skin + qdist; - if (triclinic == 0) dist[0] = dist[1] = dist[2] = cuthalf; - else kspacebbox(cuthalf,&dist[0]); - - int nlo,nhi; - - nlo = static_cast ((sublo[0]-dist[0]-boxlo[0]) * - nx_pppm/xprd + shift) - OFFSET; - nhi = static_cast ((subhi[0]+dist[0]-boxlo[0]) * - nx_pppm/xprd + shift) - OFFSET; - nxlo_out = nlo + nlower; - nxhi_out = nhi + nupper; - - nlo = static_cast ((sublo[1]-dist[1]-boxlo[1]) * - ny_pppm/yprd + shift) - OFFSET; - nhi = static_cast ((subhi[1]+dist[1]-boxlo[1]) * - ny_pppm/yprd + shift) - OFFSET; - nylo_out = nlo + nlower; - nyhi_out = nhi + nupper; - - nlo = static_cast ((sublo[2]-dist[2]-boxlo[2]) * - nz_pppm/zprd_slab + shift) - OFFSET; - nhi = static_cast ((subhi[2]+dist[2]-boxlo[2]) * - nz_pppm/zprd_slab + shift) - OFFSET; - nzlo_out = nlo + nlower; - nzhi_out = nhi + nupper; - - // for slab PPPM, change the grid boundary for processors at +z end - // to include the empty volume between periodically repeating slabs - // for slab PPPM, want charge data communicated from -z proc to +z proc, - // but not vice versa, also want field data communicated from +z proc to - // -z proc, but not vice versa - // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells) - // also insure no other procs use ghost cells beyond +z limit - - if (slabflag) { - if (comm->myloc[2] == comm->procgrid[2]-1) - nzhi_in = nzhi_out = nz_pppm - 1; - nzhi_out = MIN(nzhi_out,nz_pppm-1); - } - - // decomposition of FFT mesh - // global indices range from 0 to N-1 - // proc owns entire x-dimension, clumps of columns in y,z dimensions - // npey_fft,npez_fft = # of procs in y,z dims - // if nprocs is small enough, proc can own 1 or more entire xy planes, - // else proc owns 2d sub-blocks of yz plane - // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions - // nlo_fft,nhi_fft = lower/upper limit of the section - // of the global FFT mesh that I own - - int npey_fft,npez_fft; - if (nz_pppm >= nprocs) { - npey_fft = 1; - npez_fft = nprocs; - } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); - - int me_y = me % npey_fft; - int me_z = me / npey_fft; - - nxlo_fft = 0; - nxhi_fft = nx_pppm - 1; - nylo_fft = me_y*ny_pppm/npey_fft; - nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; - nzlo_fft = me_z*nz_pppm/npez_fft; - nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; - - // PPPM grid pts owned by this proc, including ghosts - - ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * - (nzhi_out-nzlo_out+1); - - // FFT grids owned by this proc, without ghosts - // nfft = FFT points in FFT decomposition on this proc - // nfft_brick = FFT points in 3d brick-decomposition on this proc - // nfft_both = greater of 2 values - - nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) * - (nzhi_fft-nzlo_fft+1); - int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) * - (nzhi_in-nzlo_in+1); - nfft_both = MAX(nfft,nfft_brick); -} - -/* ---------------------------------------------------------------------- - pre-compute Green's function denominator expansion coeffs, Gamma(2n) -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_denom() -{ - int k,l,m; - - for (l = 1; l < order; l++) gf_b[l] = 0.0; - gf_b[0] = 1.0; - - for (m = 1; m < order; m++) { - for (l = m; l > 0; l--) - gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); - gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); - } - - bigint ifact = 1; - for (k = 1; k < 2*order; k++) ifact *= k; - double gaminv = 1.0/ifact; - for (l = 0; l < order; l++) gf_b[l] *= gaminv; -} - -/* ---------------------------------------------------------------------- - pre-compute modified (Hockney-Eastwood) Coulomb Green's function -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_ik() -{ - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,dot1,dot2; - double numerator,denominator; - double sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - numerator = 12.5663706/sqk; - denominator = gf_denom(snx,sny,snz); - sum1 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; - dot2 = qx*qx+qy*qy+qz*qz; - sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; - } - } - } - greensfn[n++] = numerator*sum1/denominator; - } else greensfn[n++] = 0.0; - } - } - } -} - -/* ---------------------------------------------------------------------- - pre-compute modified (Hockney-Eastwood) Coulomb Green's function - for a triclinic system -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_ik_triclinic() -{ - double snx,sny,snz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,dot1,dot2; - double numerator,denominator; - double sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - double tmp[3]; - tmp[0] = (g_ewald/(MY_PI*nx_pppm)) * pow(-log(EPS_HOC),0.25); - tmp[1] = (g_ewald/(MY_PI*ny_pppm)) * pow(-log(EPS_HOC),0.25); - tmp[2] = (g_ewald/(MY_PI*nz_pppm)) * pow(-log(EPS_HOC),0.25); - lamda2xT(&tmp[0],&tmp[0]); - const int nbx = static_cast (tmp[0]); - const int nby = static_cast (tmp[1]); - const int nbz = static_cast (tmp[2]); - - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(MY_PI*mper/nz_pppm)); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(MY_PI*lper/ny_pppm)); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(MY_PI*kper/nx_pppm)); - - double unitk_lamda[3]; - unitk_lamda[0] = 2.0*MY_PI*kper; - unitk_lamda[1] = 2.0*MY_PI*lper; - unitk_lamda[2] = 2.0*MY_PI*mper; - x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); - - sqk = square(unitk_lamda[0]) + square(unitk_lamda[1]) + square(unitk_lamda[2]); - - if (sqk != 0.0) { - numerator = 12.5663706/sqk; - denominator = gf_denom(snx,sny,snz); - sum1 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - argx = MY_PI*kper/nx_pppm + MY_PI*nx; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - argy = MY_PI*lper/ny_pppm + MY_PI*ny; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - argz = MY_PI*mper/nz_pppm + MY_PI*nz; - wz = powsinxx(argz,twoorder); - - double b[3]; - b[0] = 2.0*MY_PI*nx_pppm*nx; - b[1] = 2.0*MY_PI*ny_pppm*ny; - b[2] = 2.0*MY_PI*nz_pppm*nz; - x2lamdaT(&b[0],&b[0]); - - qx = unitk_lamda[0]+b[0]; - sx = exp(-0.25*square(qx/g_ewald)); - - qy = unitk_lamda[1]+b[1]; - sy = exp(-0.25*square(qy/g_ewald)); - - qz = unitk_lamda[2]+b[2]; - sz = exp(-0.25*square(qz/g_ewald)); - - dot1 = unitk_lamda[0]*qx + unitk_lamda[1]*qy + unitk_lamda[2]*qz; - dot2 = qx*qx+qy*qy+qz*qz; - sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; - } - } - } - greensfn[n++] = numerator*sum1/denominator; - } else greensfn[n++] = 0.0; - } - } - } -} - -/* ---------------------------------------------------------------------- - compute optimized Green's function for energy calculation -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_ad() -{ - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz,sqk; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double numerator,denominator; - int k,l,m,n,kper,lper,mper; - - const int twoorder = 2*order; - - for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - qz = unitkz*mper; - snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - qy = unitky*lper; - sny = square(sin(0.5*qy*yprd/ny_pppm)); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - qx = unitkx*kper; - snx = square(sin(0.5*qx*xprd/nx_pppm)); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - sqk = qx*qx + qy*qy + qz*qz; - - if (sqk != 0.0) { - numerator = MY_4PI/sqk; - denominator = gf_denom(snx,sny,snz); - greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; - sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; - sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; - sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; - sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; - sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; - sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; - n++; - } else { - greensfn[n] = 0.0; - sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; - sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; - sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; - sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; - sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; - sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; - n++; - } - } - } - } - - // compute the coefficients for the self-force correction - - double prex, prey, prez; - prex = prey = prez = MY_PI/volume; - prex *= nx_pppm/xprd; - prey *= ny_pppm/yprd; - prez *= nz_pppm/zprd_slab; - sf_coeff[0] *= prex; - sf_coeff[1] *= prex*2; - sf_coeff[2] *= prey; - sf_coeff[3] *= prey*2; - sf_coeff[4] *= prez; - sf_coeff[5] *= prez*2; - - // communicate values with other procs - - double tmp[6]; - MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); - for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; -} - -/* ---------------------------------------------------------------------- - compute self force coefficients for ad-differentiation scheme -------------------------------------------------------------------------- */ - -void PPPM::compute_sf_precoeff() -{ - int i,k,l,m,n; - int nx,ny,nz,kper,lper,mper; - double wx0[5],wy0[5],wz0[5],wx1[5],wy1[5],wz1[5],wx2[5],wy2[5],wz2[5]; - double qx0,qy0,qz0,qx1,qy1,qz1,qx2,qy2,qz2; - double u0,u1,u2,u3,u4,u5,u6; - double sum1,sum2,sum3,sum4,sum5,sum6; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - - sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0.0; - for (i = 0; i < 5; i++) { - - qx0 = MY_2PI*(kper+nx_pppm*(i-2)); - qx1 = MY_2PI*(kper+nx_pppm*(i-1)); - qx2 = MY_2PI*(kper+nx_pppm*(i )); - wx0[i] = powsinxx(0.5*qx0/nx_pppm,order); - wx1[i] = powsinxx(0.5*qx1/nx_pppm,order); - wx2[i] = powsinxx(0.5*qx2/nx_pppm,order); - - qy0 = MY_2PI*(lper+ny_pppm*(i-2)); - qy1 = MY_2PI*(lper+ny_pppm*(i-1)); - qy2 = MY_2PI*(lper+ny_pppm*(i )); - wy0[i] = powsinxx(0.5*qy0/ny_pppm,order); - wy1[i] = powsinxx(0.5*qy1/ny_pppm,order); - wy2[i] = powsinxx(0.5*qy2/ny_pppm,order); - - qz0 = MY_2PI*(mper+nz_pppm*(i-2)); - qz1 = MY_2PI*(mper+nz_pppm*(i-1)); - qz2 = MY_2PI*(mper+nz_pppm*(i )); - - wz0[i] = powsinxx(0.5*qz0/nz_pppm,order); - wz1[i] = powsinxx(0.5*qz1/nz_pppm,order); - wz2[i] = powsinxx(0.5*qz2/nz_pppm,order); - } - - for (nx = 0; nx < 5; nx++) { - for (ny = 0; ny < 5; ny++) { - for (nz = 0; nz < 5; nz++) { - u0 = wx0[nx]*wy0[ny]*wz0[nz]; - u1 = wx1[nx]*wy0[ny]*wz0[nz]; - u2 = wx2[nx]*wy0[ny]*wz0[nz]; - u3 = wx0[nx]*wy1[ny]*wz0[nz]; - u4 = wx0[nx]*wy2[ny]*wz0[nz]; - u5 = wx0[nx]*wy0[ny]*wz1[nz]; - u6 = wx0[nx]*wy0[ny]*wz2[nz]; - - sum1 += u0*u1; - sum2 += u0*u2; - sum3 += u0*u3; - sum4 += u0*u4; - sum5 += u0*u5; - sum6 += u0*u6; - } - } - } - - // store values - - sf_precoeff1[n] = sum1; - sf_precoeff2[n] = sum2; - sf_precoeff3[n] = sum3; - sf_precoeff4[n] = sum4; - sf_precoeff5[n] = sum5; - sf_precoeff6[n++] = sum6; - } - } - } -} - -/* ---------------------------------------------------------------------- - find center grid pt for each of my particles - check that full stencil for the particle will fit in my 3d brick - store central grid pt indices in part2grid array -------------------------------------------------------------------------- */ - -void PPPM::particle_map() -{ - int nx,ny,nz; - - double **x = atom->x; - int nlocal = atom->nlocal; - - int flag = 0; - for (int i = 0; i < nlocal; i++) { - - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // current particle coord can be outside global and local box - // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 - - nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift) - OFFSET; - ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift) - OFFSET; - nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift) - OFFSET; - - part2grid[i][0] = nx; - part2grid[i][1] = ny; - part2grid[i][2] = nz; - - // check that entire stencil around nx,ny,nz will fit in my 3d brick - - if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || - ny+nlower < nylo_out || ny+nupper > nyhi_out || - nz+nlower < nzlo_out || nz+nupper > nzhi_out) - flag = 1; - } - - if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); -} - -/* ---------------------------------------------------------------------- - create discretized "density" on section of global grid due to my particles - density(x,y,z) = charge "density" at grid points of my 3d brick - (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) - in global grid -------------------------------------------------------------------------- */ - -void PPPM::make_rho() -{ - int l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - - // clear 3d density array - - memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - // loop over my charges, add their contribution to nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - - for (int i = 0; i < nlocal; i++) { - - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - z0 = delvolinv * q[i]; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - y0 = z0*rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - x0 = y0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - density_brick[mz][my][mx] += x0*rho1d[0][l]; - } - } - } - } -} - -/* ---------------------------------------------------------------------- - remap density from 3d brick decomposition to FFT decomposition -------------------------------------------------------------------------- */ - -void PPPM::brick2fft() -{ - int n,ix,iy,iz; - - // copy grabs inner portion of density from 3d brick - // remap could be done as pre-stage of FFT, - // but this works optimally on only double values, not complex values - - n = 0; - for (iz = nzlo_in; iz <= nzhi_in; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - density_fft[n++] = density_brick[iz][iy][ix]; - - remap->perform(density_fft,density_fft,work1); -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver -------------------------------------------------------------------------- */ - -void PPPM::poisson() -{ - if (differentiation_flag == 1) poisson_ad(); - else poisson_ik(); -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for ik -------------------------------------------------------------------------- */ - -void PPPM::poisson_ik() -{ - int i,j,k,n; - double eng; - - // transform charge density (r -> k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] = density_fft[i]; - work1[n++] = ZEROF; - } - - fft1->compute(work1,work1,1); - - // global energy and virial contribution - - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); - double s2 = scaleinv*scaleinv; - - if (eflag_global || vflag_global) { - if (vflag_global) { - n = 0; - for (i = 0; i < nfft; i++) { - eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; - if (eflag_global) energy += eng; - n += 2; - } - } else { - n = 0; - for (i = 0; i < nfft; i++) { - energy += - s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - n += 2; - } - } - } - - // scale by 1/total-grid-pts to get rho(k) - // multiply by Green's function to get V(k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] *= scaleinv * greensfn[i]; - work1[n++] *= scaleinv * greensfn[i]; - } - - // extra FFTs for per-atom energy/virial - - if (evflag_atom) poisson_peratom(); - - // triclinic system - - if (triclinic) { - poisson_ik_triclinic(); - return; - } - - // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) - // FFT leaves data in 3d brick decomposition - // copy it into inner portion of vdx,vdy,vdz arrays - - // x direction gradient - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - work2[n] = fkx[i]*work1[n+1]; - work2[n+1] = -fkx[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdx_brick[k][j][i] = work2[n]; - n += 2; - } - - // y direction gradient - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - work2[n] = fky[j]*work1[n+1]; - work2[n+1] = -fky[j]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdy_brick[k][j][i] = work2[n]; - n += 2; - } - - // z direction gradient - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - work2[n] = fkz[k]*work1[n+1]; - work2[n+1] = -fkz[k]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdz_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for ik for a triclinic system -------------------------------------------------------------------------- */ - -void PPPM::poisson_ik_triclinic() -{ - int i,j,k,n; - - // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) - // FFT leaves data in 3d brick decomposition - // copy it into inner portion of vdx,vdy,vdz arrays - - // x direction gradient - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = fkx[i]*work1[n+1]; - work2[n+1] = -fkx[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdx_brick[k][j][i] = work2[n]; - n += 2; - } - - // y direction gradient - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = fky[i]*work1[n+1]; - work2[n+1] = -fky[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdy_brick[k][j][i] = work2[n]; - n += 2; - } - - // z direction gradient - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = fkz[i]*work1[n+1]; - work2[n+1] = -fkz[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdz_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for ad -------------------------------------------------------------------------- */ - -void PPPM::poisson_ad() -{ - int i,j,k,n; - double eng; - - // transform charge density (r -> k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] = density_fft[i]; - work1[n++] = ZEROF; - } - - fft1->compute(work1,work1,1); - - // global energy and virial contribution - - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); - double s2 = scaleinv*scaleinv; - - if (eflag_global || vflag_global) { - if (vflag_global) { - n = 0; - for (i = 0; i < nfft; i++) { - eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; - if (eflag_global) energy += eng; - n += 2; - } - } else { - n = 0; - for (i = 0; i < nfft; i++) { - energy += - s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - n += 2; - } - } - } - - // scale by 1/total-grid-pts to get rho(k) - // multiply by Green's function to get V(k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] *= scaleinv * greensfn[i]; - work1[n++] *= scaleinv * greensfn[i]; - } - - // extra FFTs for per-atom energy/virial - - if (vflag_atom) poisson_peratom(); - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]; - work2[n+1] = work1[n+1]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - u_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for per-atom energy/virial -------------------------------------------------------------------------- */ - -void PPPM::poisson_peratom() -{ - int i,j,k,n; - - // energy - - if (eflag_atom && differentiation_flag != 1) { - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]; - work2[n+1] = work1[n+1]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - u_brick[k][j][i] = work2[n]; - n += 2; - } - } - - // 6 components of virial in v0 thru v5 - - if (!vflag_atom) return; - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][0]; - work2[n+1] = work1[n+1]*vg[i][0]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v0_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][1]; - work2[n+1] = work1[n+1]*vg[i][1]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v1_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][2]; - work2[n+1] = work1[n+1]*vg[i][2]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v2_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][3]; - work2[n+1] = work1[n+1]*vg[i][3]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v3_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][4]; - work2[n+1] = work1[n+1]*vg[i][4]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v4_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][5]; - work2[n+1] = work1[n+1]*vg[i][5]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v5_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles -------------------------------------------------------------------------- */ - -void PPPM::fieldforce() -{ - if (differentiation_flag == 1) fieldforce_ad(); - else fieldforce_ik(); -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles for ik -------------------------------------------------------------------------- */ - -void PPPM::fieldforce_ik() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - FFT_SCALAR ekx,eky,ekz; - - // loop over my charges, interpolate electric field from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - // ek = 3 components of E-field on particle - - double *q = atom->q; - double **x = atom->x; - double **f = atom->f; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - ekx = eky = ekz = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - z0 = rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - y0 = z0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - x0 = y0*rho1d[0][l]; - ekx -= x0*vdx_brick[mz][my][mx]; - eky -= x0*vdy_brick[mz][my][mx]; - ekz -= x0*vdz_brick[mz][my][mx]; - } - } - } - - // convert E-field to force - - const double qfactor = force->qqrd2e * scale * q[i]; - f[i][0] += qfactor*ekx; - f[i][1] += qfactor*eky; - if (slabflag != 2) f[i][2] += qfactor*ekz; - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles for ad -------------------------------------------------------------------------- */ - -void PPPM::fieldforce_ad() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz; - FFT_SCALAR ekx,eky,ekz; - double s1,s2,s3; - double sf = 0.0; - double *prd; - - prd = domain->prd; - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - - double hx_inv = nx_pppm/xprd; - double hy_inv = ny_pppm/yprd; - double hz_inv = nz_pppm/zprd; - - // loop over my charges, interpolate electric field from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - // ek = 3 components of E-field on particle - - double *q = atom->q; - double **x = atom->x; - double **f = atom->f; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - compute_drho1d(dx,dy,dz); - - ekx = eky = ekz = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; - eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; - ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; - } - } - } - ekx *= hx_inv; - eky *= hy_inv; - ekz *= hz_inv; - - // convert E-field to force and substract self forces - - const double qfactor = force->qqrd2e * scale; - - s1 = x[i][0]*hx_inv; - s2 = x[i][1]*hy_inv; - s3 = x[i][2]*hz_inv; - sf = sf_coeff[0]*sin(2*MY_PI*s1); - sf += sf_coeff[1]*sin(4*MY_PI*s1); - sf *= 2*q[i]*q[i]; - f[i][0] += qfactor*(ekx*q[i] - sf); - - sf = sf_coeff[2]*sin(2*MY_PI*s2); - sf += sf_coeff[3]*sin(4*MY_PI*s2); - sf *= 2*q[i]*q[i]; - f[i][1] += qfactor*(eky*q[i] - sf); - - - sf = sf_coeff[4]*sin(2*MY_PI*s3); - sf += sf_coeff[5]*sin(4*MY_PI*s3); - sf *= 2*q[i]*q[i]; - if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get per-atom energy/virial -------------------------------------------------------------------------- */ - -void PPPM::fieldforce_peratom() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - FFT_SCALAR u,v0,v1,v2,v3,v4,v5; - - // loop over my charges, interpolate from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - z0 = rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - y0 = z0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - x0 = y0*rho1d[0][l]; - if (eflag_atom) u += x0*u_brick[mz][my][mx]; - if (vflag_atom) { - v0 += x0*v0_brick[mz][my][mx]; - v1 += x0*v1_brick[mz][my][mx]; - v2 += x0*v2_brick[mz][my][mx]; - v3 += x0*v3_brick[mz][my][mx]; - v4 += x0*v4_brick[mz][my][mx]; - v5 += x0*v5_brick[mz][my][mx]; - } - } - } - } - - if (eflag_atom) eatom[i] += q[i]*u; - if (vflag_atom) { - vatom[i][0] += q[i]*v0; - vatom[i][1] += q[i]*v1; - vatom[i][2] += q[i]*v2; - vatom[i][3] += q[i]*v3; - vatom[i][4] += q[i]*v4; - vatom[i][5] += q[i]*v5; - } - } -} - -/* ---------------------------------------------------------------------- - pack own values to buf to send to another proc -------------------------------------------------------------------------- */ - -void PPPM::pack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - int n = 0; - - if (flag == FORWARD_IK) { - FFT_SCALAR *xsrc = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *ysrc = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *zsrc = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - buf[n++] = xsrc[list[i]]; - buf[n++] = ysrc[list[i]]; - buf[n++] = zsrc[list[i]]; - } - } else if (flag == FORWARD_AD) { - FFT_SCALAR *src = &u_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - buf[i] = src[list[i]]; - } else if (flag == FORWARD_IK_PERATOM) { - FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - if (eflag_atom) buf[n++] = esrc[list[i]]; - if (vflag_atom) { - buf[n++] = v0src[list[i]]; - buf[n++] = v1src[list[i]]; - buf[n++] = v2src[list[i]]; - buf[n++] = v3src[list[i]]; - buf[n++] = v4src[list[i]]; - buf[n++] = v5src[list[i]]; - } - } - } else if (flag == FORWARD_AD_PERATOM) { - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - buf[n++] = v0src[list[i]]; - buf[n++] = v1src[list[i]]; - buf[n++] = v2src[list[i]]; - buf[n++] = v3src[list[i]]; - buf[n++] = v4src[list[i]]; - buf[n++] = v5src[list[i]]; - } - } -} - -/* ---------------------------------------------------------------------- - unpack another proc's own values from buf and set own ghost values -------------------------------------------------------------------------- */ - -void PPPM::unpack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - int n = 0; - - if (flag == FORWARD_IK) { - FFT_SCALAR *xdest = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *ydest = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *zdest = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - xdest[list[i]] = buf[n++]; - ydest[list[i]] = buf[n++]; - zdest[list[i]] = buf[n++]; - } - } else if (flag == FORWARD_AD) { - FFT_SCALAR *dest = &u_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - dest[list[i]] = buf[i]; - } else if (flag == FORWARD_IK_PERATOM) { - FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - if (eflag_atom) esrc[list[i]] = buf[n++]; - if (vflag_atom) { - v0src[list[i]] = buf[n++]; - v1src[list[i]] = buf[n++]; - v2src[list[i]] = buf[n++]; - v3src[list[i]] = buf[n++]; - v4src[list[i]] = buf[n++]; - v5src[list[i]] = buf[n++]; - } - } - } else if (flag == FORWARD_AD_PERATOM) { - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - v0src[list[i]] = buf[n++]; - v1src[list[i]] = buf[n++]; - v2src[list[i]] = buf[n++]; - v3src[list[i]] = buf[n++]; - v4src[list[i]] = buf[n++]; - v5src[list[i]] = buf[n++]; - } - } -} - -/* ---------------------------------------------------------------------- - pack ghost values into buf to send to another proc -------------------------------------------------------------------------- */ - -void PPPM::pack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - if (flag == REVERSE_RHO) { - FFT_SCALAR *src = &density_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - buf[i] = src[list[i]]; - } -} - -/* ---------------------------------------------------------------------- - unpack another proc's ghost values from buf and add to own values -------------------------------------------------------------------------- */ - -void PPPM::unpack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - if (flag == REVERSE_RHO) { - FFT_SCALAR *dest = &density_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - dest[list[i]] += buf[i]; - } -} - -/* ---------------------------------------------------------------------- - map nprocs to NX by NY grid as PX by PY procs - return optimal px,py -------------------------------------------------------------------------- */ - -void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py) -{ - // loop thru all possible factorizations of nprocs - // surf = surface area of largest proc sub-domain - // innermost if test minimizes surface area and surface/volume ratio - - int bestsurf = 2 * (nx + ny); - int bestboxx = 0; - int bestboxy = 0; - - int boxx,boxy,surf,ipx,ipy; - - ipx = 1; - while (ipx <= nprocs) { - if (nprocs % ipx == 0) { - ipy = nprocs/ipx; - boxx = nx/ipx; - if (nx % ipx) boxx++; - boxy = ny/ipy; - if (ny % ipy) boxy++; - surf = boxx + boxy; - if (surf < bestsurf || - (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) { - bestsurf = surf; - bestboxx = boxx; - bestboxy = boxy; - *px = ipx; - *py = ipy; - } - } - ipx++; - } -} - -/* ---------------------------------------------------------------------- - charge assignment into rho1d - dx,dy,dz = distance of particle from "lower left" grid point -------------------------------------------------------------------------- */ - -void PPPM::compute_rho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, - const FFT_SCALAR &dz) -{ - int k,l; - FFT_SCALAR r1,r2,r3; - - for (k = (1-order)/2; k <= order/2; k++) { - r1 = r2 = r3 = ZEROF; - - for (l = order-1; l >= 0; l--) { - r1 = rho_coeff[l][k] + r1*dx; - r2 = rho_coeff[l][k] + r2*dy; - r3 = rho_coeff[l][k] + r3*dz; - } - rho1d[0][k] = r1; - rho1d[1][k] = r2; - rho1d[2][k] = r3; - } -} - -/* ---------------------------------------------------------------------- - charge assignment into drho1d - dx,dy,dz = distance of particle from "lower left" grid point -------------------------------------------------------------------------- */ - -void PPPM::compute_drho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, - const FFT_SCALAR &dz) -{ - int k,l; - FFT_SCALAR r1,r2,r3; - - for (k = (1-order)/2; k <= order/2; k++) { - r1 = r2 = r3 = ZEROF; - - for (l = order-2; l >= 0; l--) { - r1 = drho_coeff[l][k] + r1*dx; - r2 = drho_coeff[l][k] + r2*dy; - r3 = drho_coeff[l][k] + r3*dz; - } - drho1d[0][k] = r1; - drho1d[1][k] = r2; - drho1d[2][k] = r3; - } -} - -/* ---------------------------------------------------------------------- - generate coeffients for the weight function of order n - - (n-1) - Wn(x) = Sum wn(k,x) , Sum is over every other integer - k=-(n-1) - For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1 - k is odd integers if n is even and even integers if n is odd - --- - | n-1 - | Sum a(l,j)*(x-k/2)**l if abs(x-k/2) < 1/2 - wn(k,x) = < l=0 - | - | 0 otherwise - --- - a coeffients are packed into the array rho_coeff to eliminate zeros - rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k) -------------------------------------------------------------------------- */ - -void PPPM::compute_rho_coeff() -{ - int j,k,l,m; - FFT_SCALAR s; - - FFT_SCALAR **a; - memory->create2d_offset(a,order,-order,order,"pppm:a"); - - for (k = -order; k <= order; k++) - for (l = 0; l < order; l++) - a[l][k] = 0.0; - - a[0][0] = 1.0; - for (j = 1; j < order; j++) { - for (k = -j; k <= j; k += 2) { - s = 0.0; - for (l = 0; l < j; l++) { - a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1); -#ifdef FFT_SINGLE - s += powf(0.5,(float) l+1) * - (a[l][k-1] + powf(-1.0,(float) l) * a[l][k+1]) / (l+1); -#else - s += pow(0.5,(double) l+1) * - (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1); -#endif - } - a[0][k] = s; - } - } - - m = (1-order)/2; - for (k = -(order-1); k < order; k += 2) { - for (l = 0; l < order; l++) - rho_coeff[l][m] = a[l][k]; - for (l = 1; l < order; l++) - drho_coeff[l-1][m] = l*a[l][k]; - m++; - } - - memory->destroy2d_offset(a,-order); -} - -/* ---------------------------------------------------------------------- - Slab-geometry correction term to dampen inter-slab interactions between - periodically repeating slabs. Yields good approximation to 2D Ewald if - adequate empty space is left between repeating slabs (J. Chem. Phys. - 111, 3155). Slabs defined here to be parallel to the xy plane. -------------------------------------------------------------------------- */ - -void PPPM::slabcorr() -{ - // compute local contribution to global dipole moment - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - - double dipole = 0.0; - for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; - - // sum local contributions to get global dipole moment - - double dipole_all; - MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); - - // compute corrections - - const double e_slabcorr = MY_2PI*dipole_all*dipole_all/volume; - const double qscale = force->qqrd2e * scale; - - if (eflag_global) energy += qscale * e_slabcorr; - - // per-atom energy - - if (eflag_atom) { - double efact = MY_2PI*dipole_all/volume; - for (int i = 0; i < nlocal; i++) eatom[i] += qscale * q[i]*x[i][2]*efact; - } - - // add on force corrections - - double ffact = -4.0*MY_PI*dipole_all/volume; - double **f = atom->f; - - for (int i = 0; i < nlocal; i++) f[i][2] += qscale * q[i]*ffact; -} - -/* ---------------------------------------------------------------------- - perform and time the 1d FFTs required for N timesteps -------------------------------------------------------------------------- */ - -int PPPM::timing_1d(int n, double &time1d) -{ - double time1,time2; - - for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; - - MPI_Barrier(world); - time1 = MPI_Wtime(); - - for (int i = 0; i < n; i++) { - fft1->timing1d(work1,nfft_both,1); - fft2->timing1d(work1,nfft_both,-1); - if (differentiation_flag != 1) { - fft2->timing1d(work1,nfft_both,-1); - fft2->timing1d(work1,nfft_both,-1); - } - } - - MPI_Barrier(world); - time2 = MPI_Wtime(); - time1d = time2 - time1; - - if (differentiation_flag) return 2; - return 4; -} - -/* ---------------------------------------------------------------------- - perform and time the 3d FFTs required for N timesteps -------------------------------------------------------------------------- */ - -int PPPM::timing_3d(int n, double &time3d) -{ - double time1,time2; - - for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; - - MPI_Barrier(world); - time1 = MPI_Wtime(); - - for (int i = 0; i < n; i++) { - fft1->compute(work1,work1,1); - fft2->compute(work1,work1,-1); - if (differentiation_flag != 1) { - fft2->compute(work1,work1,-1); - fft2->compute(work1,work1,-1); - } - } - - MPI_Barrier(world); - time2 = MPI_Wtime(); - time3d = time2 - time1; - - if (differentiation_flag) return 2; - return 4; -} - -/* ---------------------------------------------------------------------- - memory usage of local arrays -------------------------------------------------------------------------- */ - -double PPPM::memory_usage() -{ - double bytes = nmax*3 * sizeof(double); - int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * - (nzhi_out-nzlo_out+1); - if (differentiation_flag == 1) { - bytes += 2 * nbrick * sizeof(FFT_SCALAR); - } else { - bytes += 4 * nbrick * sizeof(FFT_SCALAR); - } - if (triclinic) bytes += 3 * nfft_both * sizeof(double); - bytes += 6 * nfft_both * sizeof(double); - bytes += nfft_both * sizeof(double); - bytes += nfft_both*5 * sizeof(FFT_SCALAR); - - if (peratom_allocate_flag) - bytes += 6 * nbrick * sizeof(FFT_SCALAR); - - if (group_allocate_flag) { - bytes += 2 * nbrick * sizeof(FFT_SCALAR); - bytes += 2 * nfft_both * sizeof(FFT_SCALAR);; - } - - bytes += cg->memory_usage(); - - return bytes; -} - -/* ---------------------------------------------------------------------- - group-group interactions - ------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - compute the PPPM total long-range force and energy for groups A and B - ------------------------------------------------------------------------- */ - -void PPPM::compute_group_group(int groupbit_A, int groupbit_B, int BA_flag) -{ - if (slabflag) - error->all(FLERR,"Cannot (yet) use K-space slab " - "correction with compute group/group"); - - if (differentiation_flag) - error->all(FLERR,"Cannot (yet) use 'kspace_modify " - "diff ad' with compute group/group"); - - if (!group_allocate_flag) allocate_groups(); - - // convert atoms from box to lamda coords - - if (triclinic == 0) boxlo = domain->boxlo; - else { - boxlo = domain->boxlo_lamda; - domain->x2lamda(atom->nlocal); - } - - e2group = 0; //energy - f2group[0] = 0; //force in x-direction - f2group[1] = 0; //force in y-direction - f2group[2] = 0; //force in z-direction - - // map my particle charge onto my local 3d density grid - - make_rho_groups(groupbit_A,groupbit_B,BA_flag); - - // all procs communicate density values from their ghost cells - // to fully sum contribution in their 3d bricks - // remap from 3d decomposition to FFT decomposition - - // temporarily store and switch pointers so we can - // use brick2fft() for groups A and B (without - // writing an additional function) - - FFT_SCALAR ***density_brick_real = density_brick; - FFT_SCALAR *density_fft_real = density_fft; - - // group A - - density_brick = density_A_brick; - density_fft = density_A_fft; - - cg->reverse_comm(this,REVERSE_RHO); - brick2fft(); - - // group B - - density_brick = density_B_brick; - density_fft = density_B_fft; - - cg->reverse_comm(this,REVERSE_RHO); - brick2fft(); - - // switch back pointers - - density_brick = density_brick_real; - density_fft = density_fft_real; - - // compute potential gradient on my FFT grid and - // portion of group-group energy/force on this proc's FFT grid - - poisson_groups(BA_flag); - - const double qscale = force->qqrd2e * scale; - - // total group A <--> group B energy - // self and boundary correction terms are in compute_group_group.cpp - - double e2group_all; - MPI_Allreduce(&e2group,&e2group_all,1,MPI_DOUBLE,MPI_SUM,world); - e2group = e2group_all; - - e2group *= qscale*0.5*volume; - - // total group A <--> group B force - - double f2group_all[3]; - MPI_Allreduce(f2group,f2group_all,3,MPI_DOUBLE,MPI_SUM,world); - - for (int i = 0; i < 3; i++) f2group[i] = qscale*volume*f2group_all[i]; - - // convert atoms back from lamda to box coords - - if (triclinic) domain->lamda2x(atom->nlocal); -} - -/* ---------------------------------------------------------------------- - allocate group-group memory that depends on # of K-vectors and order - ------------------------------------------------------------------------- */ - -void PPPM::allocate_groups() -{ - group_allocate_flag = 1; - - memory->create3d_offset(density_A_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:density_A_brick"); - memory->create3d_offset(density_B_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:density_B_brick"); - memory->create(density_A_fft,nfft_both,"pppm:density_A_fft"); - memory->create(density_B_fft,nfft_both,"pppm:density_B_fft"); -} - -/* ---------------------------------------------------------------------- - deallocate group-group memory that depends on # of K-vectors and order - ------------------------------------------------------------------------- */ - -void PPPM::deallocate_groups() -{ - group_allocate_flag = 0; - - memory->destroy3d_offset(density_A_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(density_B_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy(density_A_fft); - memory->destroy(density_B_fft); -} - -/* ---------------------------------------------------------------------- - create discretized "density" on section of global grid due to my particles - density(x,y,z) = charge "density" at grid points of my 3d brick - (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) - in global grid for group-group interactions - ------------------------------------------------------------------------- */ - -void PPPM::make_rho_groups(int groupbit_A, int groupbit_B, int BA_flag) -{ - int l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - - // clear 3d density arrays - - memset(&(density_A_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - memset(&(density_B_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - // loop over my charges, add their contribution to nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - int *mask = atom->mask; - - for (int i = 0; i < nlocal; i++) { - - if ((mask[i] & groupbit_A) && (mask[i] & groupbit_B)) - if (BA_flag) continue; - - if ((mask[i] & groupbit_A) || (mask[i] & groupbit_B)) { - - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - z0 = delvolinv * q[i]; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - y0 = z0*rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - x0 = y0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - - // group A - - if (mask[i] & groupbit_A) - density_A_brick[mz][my][mx] += x0*rho1d[0][l]; - - // group B - - if (mask[i] & groupbit_B) - density_B_brick[mz][my][mx] += x0*rho1d[0][l]; - } - } - } - } - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for group-group interactions - ------------------------------------------------------------------------- */ - -void PPPM::poisson_groups(int BA_flag) -{ - int i,j,k,n; - - // reuse memory (already declared) - - FFT_SCALAR *work_A = work1; - FFT_SCALAR *work_B = work2; - - // transform charge density (r -> k) - - // group A - - n = 0; - for (i = 0; i < nfft; i++) { - work_A[n++] = density_A_fft[i]; - work_A[n++] = ZEROF; - } - - fft1->compute(work_A,work_A,1); - - // group B - - n = 0; - for (i = 0; i < nfft; i++) { - work_B[n++] = density_B_fft[i]; - work_B[n++] = ZEROF; - } - - fft1->compute(work_B,work_B,1); - - // group-group energy and force contribution, - // keep everything in reciprocal space so - // no inverse FFTs needed - - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); - double s2 = scaleinv*scaleinv; - - // energy - - n = 0; - for (i = 0; i < nfft; i++) { - e2group += s2 * greensfn[i] * - (work_A[n]*work_B[n] + work_A[n+1]*work_B[n+1]); - n += 2; - } - - if (BA_flag) return; - - - // multiply by Green's function and s2 - // (only for work_A so it is not squared below) - - n = 0; - for (i = 0; i < nfft; i++) { - work_A[n++] *= s2 * greensfn[i]; - work_A[n++] *= s2 * greensfn[i]; - } - - // triclinic system - - if (triclinic) { - poisson_groups_triclinic(); - return; - } - - double partial_group; - - // force, x direction - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[0] += fkx[i] * partial_group; - n += 2; - } - - // force, y direction - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[1] += fky[j] * partial_group; - n += 2; - } - - // force, z direction - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[2] += fkz[k] * partial_group; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for group-group interactions - for a triclinic system - ------------------------------------------------------------------------- */ - -void PPPM::poisson_groups_triclinic() -{ - int i,j,k,n; - - // reuse memory (already declared) - - FFT_SCALAR *work_A = work1; - FFT_SCALAR *work_B = work2; - - double partial_group; - - // force, x direction - - n = 0; - for (i = 0; i < nfft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[0] += fkx[i] * partial_group; - n += 2; - } - - // force, y direction - - n = 0; - for (i = 0; i < nfft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[1] += fky[i] * partial_group; - n += 2; - } - - // force, z direction - - n = 0; - for (i = 0; i < nfft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[2] += fkz[i] * partial_group; - n += 2; - } -} +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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: Roy Pollock (LLNL), Paul Crozier (SNL) + per-atom energy/virial & group/group energy/force added by Stan Moore (BYU) + analytic diff (2 FFT) option added by Rolf Isele-Holder (Aachen University) + triclinic added by Stan Moore (SNL) +------------------------------------------------------------------------- */ + +#include "lmptype.h" +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" +#include "pppm.h" +#include "atom.h" +#include "comm.h" +#include "commgrid.h" +#include "neighbor.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "domain.h" +#include "fft3d_wrap.h" +#include "remap_wrap.h" +#include "memory.h" +#include "error.h" + +#include "math_const.h" +#include "math_special.h" + +using namespace LAMMPS_NS; +using namespace MathConst; +using namespace MathSpecial; + +#define MAXORDER 7 +#define OFFSET 16384 +#define SMALL 0.00001 +#define LARGE 10000.0 +#define EPS_HOC 1.0e-7 + +enum{REVERSE_RHO}; +enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; + +#ifdef FFT_SINGLE +#define ZEROF 0.0f +#define ONEF 1.0f +#else +#define ZEROF 0.0 +#define ONEF 1.0 +#endif + +/* ---------------------------------------------------------------------- */ + +PPPM::PPPM(LAMMPS *lmp, int narg, char **arg) : KSpace(lmp, narg, arg) +{ + if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm command"); + + pppmflag = 1; + group_group_enable = 1; + + accuracy_relative = atof(arg[0]); + + nfactors = 3; + factors = new int[nfactors]; + factors[0] = 2; + factors[1] = 3; + factors[2] = 5; + + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + density_brick = vdx_brick = vdy_brick = vdz_brick = NULL; + density_fft = NULL; + u_brick = NULL; + v0_brick = v1_brick = v2_brick = v3_brick = v4_brick = v5_brick = NULL; + greensfn = NULL; + work1 = work2 = NULL; + vg = NULL; + fkx = fky = fkz = NULL; + + sf_precoeff1 = sf_precoeff2 = sf_precoeff3 = + sf_precoeff4 = sf_precoeff5 = sf_precoeff6 = NULL; + + density_A_brick = density_B_brick = NULL; + density_A_fft = density_B_fft = NULL; + + gf_b = NULL; + rho1d = rho_coeff = drho1d = drho_coeff = NULL; + + fft1 = fft2 = NULL; + remap = NULL; + cg = NULL; + cg_peratom = NULL; + + nmax = 0; + part2grid = NULL; + + peratom_allocate_flag = 0; + group_allocate_flag = 0; + + // define acons coefficients for estimation of kspace errors + // see JCP 109, pg 7698 for derivation of coefficients + // higher order coefficients may be computed if needed + + memory->create(acons,8,7,"pppm:acons"); + acons[1][0] = 2.0 / 3.0; + acons[2][0] = 1.0 / 50.0; + acons[2][1] = 5.0 / 294.0; + acons[3][0] = 1.0 / 588.0; + acons[3][1] = 7.0 / 1440.0; + acons[3][2] = 21.0 / 3872.0; + acons[4][0] = 1.0 / 4320.0; + acons[4][1] = 3.0 / 1936.0; + acons[4][2] = 7601.0 / 2271360.0; + acons[4][3] = 143.0 / 28800.0; + acons[5][0] = 1.0 / 23232.0; + acons[5][1] = 7601.0 / 13628160.0; + acons[5][2] = 143.0 / 69120.0; + acons[5][3] = 517231.0 / 106536960.0; + acons[5][4] = 106640677.0 / 11737571328.0; + acons[6][0] = 691.0 / 68140800.0; + acons[6][1] = 13.0 / 57600.0; + acons[6][2] = 47021.0 / 35512320.0; + acons[6][3] = 9694607.0 / 2095994880.0; + acons[6][4] = 733191589.0 / 59609088000.0; + acons[6][5] = 326190917.0 / 11700633600.0; + acons[7][0] = 1.0 / 345600.0; + acons[7][1] = 3617.0 / 35512320.0; + acons[7][2] = 745739.0 / 838397952.0; + acons[7][3] = 56399353.0 / 12773376000.0; + acons[7][4] = 25091609.0 / 1560084480.0; + acons[7][5] = 1755948832039.0 / 36229939200000.0; + acons[7][6] = 4887769399.0 / 37838389248.0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +PPPM::~PPPM() +{ + delete [] factors; + deallocate(); + if (peratom_allocate_flag) deallocate_peratom(); + if (group_allocate_flag) deallocate_groups(); + memory->destroy(part2grid); + memory->destroy(acons); +} + +/* ---------------------------------------------------------------------- + called once before run +------------------------------------------------------------------------- */ + +void PPPM::init() +{ + if (me == 0) { + if (screen) fprintf(screen,"PPPM initialization ...\n"); + if (logfile) fprintf(logfile,"PPPM initialization ...\n"); + } + + // error check + + triclinic_check(); + if (domain->triclinic && differentiation_flag == 1) + error->all(FLERR,"Cannot (yet) use PPPM with triclinic box " + "and kspace_modify diff a'"); + if (domain->triclinic && slabflag) + error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and " + "slab correction"); + if (domain->dimension == 2) error->all(FLERR, + "Cannot use PPPM with 2d simulation"); + + if (!atom->q_flag) error->all(FLERR,"Kspace style requires atom attribute q"); + + if (slabflag == 0 && domain->nonperiodic > 0) + error->all(FLERR,"Cannot use nonperiodic boundaries with PPPM"); + if (slabflag) { + if (domain->xperiodic != 1 || domain->yperiodic != 1 || + domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) + error->all(FLERR,"Incorrect boundaries with slab PPPM"); + } + + if (order < 2 || order > MAXORDER) { + char str[128]; + sprintf(str,"PPPM order cannot be < 2 or > than %d",MAXORDER); + error->all(FLERR,str); + } + + // extract short-range Coulombic cutoff from pair style + + triclinic = domain->triclinic; + scale = 1.0; + + pair_check(); + + int itmp = 0; + double *p_cutoff = (double *) force->pair->extract("cut_coul",itmp); + if (p_cutoff == NULL) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + cutoff = *p_cutoff; + + // if kspace is TIP4P, extract TIP4P params from pair style + // bond/angle are not yet init(), so insure equilibrium request is valid + + qdist = 0.0; + + if (tip4pflag) { + double *p_qdist = (double *) force->pair->extract("qdist",itmp); + int *p_typeO = (int *) force->pair->extract("typeO",itmp); + int *p_typeH = (int *) force->pair->extract("typeH",itmp); + int *p_typeA = (int *) force->pair->extract("typeA",itmp); + int *p_typeB = (int *) force->pair->extract("typeB",itmp); + if (!p_qdist || !p_typeO || !p_typeH || !p_typeA || !p_typeB) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + qdist = *p_qdist; + typeO = *p_typeO; + typeH = *p_typeH; + int typeA = *p_typeA; + int typeB = *p_typeB; + + if (force->angle == NULL || force->bond == NULL) + error->all(FLERR,"Bond and angle potentials must be defined for TIP4P"); + if (typeA < 1 || typeA > atom->nangletypes || + force->angle->setflag[typeA] == 0) + error->all(FLERR,"Bad TIP4P angle type for PPPM/TIP4P"); + if (typeB < 1 || typeB > atom->nbondtypes || + force->bond->setflag[typeB] == 0) + error->all(FLERR,"Bad TIP4P bond type for PPPM/TIP4P"); + double theta = force->angle->equilibrium_angle(typeA); + double blen = force->bond->equilibrium_distance(typeB); + alpha = qdist / (cos(0.5*theta) * blen); + if (domain->triclinic) + error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and TIP4P"); + } + + // compute qsum & qsqsum and warn if not charge-neutral + + qsum = qsqsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) { + qsum += atom->q[i]; + qsqsum += atom->q[i]*atom->q[i]; + } + + double tmp; + MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsum = tmp; + MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsqsum = tmp; + q2 = qsqsum * force->qqrd2e / force->dielectric; + + if (qsqsum == 0.0) + error->all(FLERR,"Cannot use kspace solver on system with no charge"); + if (fabs(qsum) > SMALL && me == 0) { + char str[128]; + sprintf(str,"System is not charge neutral, net charge = %g",qsum); + error->warning(FLERR,str); + } + + // set accuracy (force units) from accuracy_relative or accuracy_absolute + + if (accuracy_absolute >= 0.0) accuracy = accuracy_absolute; + else accuracy = accuracy_relative * two_charge_force; + + // free all arrays previously allocated + + deallocate(); + if (peratom_allocate_flag) deallocate_peratom(); + if (group_allocate_flag) deallocate_groups(); + + // setup FFT grid resolution and g_ewald + // normally one iteration thru while loop is all that is required + // if grid stencil does not extend beyond neighbor proc + // or overlap is allowed, then done + // else reduce order and try again + + int (*procneigh)[2] = comm->procneigh; + + CommGrid *cgtmp = NULL; + int iteration = 0; + + while (order >= minorder) { + if (iteration && me == 0) + error->warning(FLERR,"Reducing PPPM order b/c stencil extends " + "beyond nearest neighbor processor"); + + if (stagger_flag && !differentiation_flag) compute_gf_denom(); + set_grid_global(); + set_grid_local(); + if (overlap_allowed) break; + + cgtmp = new CommGrid(lmp,world,1,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + cgtmp->ghost_notify(); + if (!cgtmp->ghost_overlap()) break; + delete cgtmp; + + order--; + iteration++; + } + + if (order < minorder) error->all(FLERR,"PPPM order < minimum allowed order"); + if (!overlap_allowed && cgtmp->ghost_overlap()) + error->all(FLERR,"PPPM grid stencil extends " + "beyond nearest neighbor processor"); + if (cgtmp) delete cgtmp; + + // adjust g_ewald + + if (!gewaldflag) adjust_gewald(); + + // calculate the final accuracy + + double estimated_accuracy = final_accuracy(); + + // print stats + + int ngrid_max,nfft_both_max; + MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world); + MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + +#ifdef FFT_SINGLE + const char fft_prec[] = "single"; +#else + const char fft_prec[] = "double"; +#endif + + if (screen) { + fprintf(screen," G vector (1/distance)= %g\n",g_ewald); + fprintf(screen," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(screen," stencil order = %d\n",order); + fprintf(screen," estimated absolute RMS force accuracy = %g\n", + estimated_accuracy); + fprintf(screen," estimated relative force accuracy = %g\n", + estimated_accuracy/two_charge_force); + fprintf(screen," using %s precision FFTs\n",fft_prec); + fprintf(screen," 3d grid and FFT values/proc = %d %d\n", + ngrid_max,nfft_both_max); + } + if (logfile) { + fprintf(logfile," G vector (1/distance) = %g\n",g_ewald); + fprintf(logfile," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(logfile," stencil order = %d\n",order); + fprintf(logfile," estimated absolute RMS force accuracy = %g\n", + estimated_accuracy); + fprintf(logfile," estimated relative force accuracy = %g\n", + estimated_accuracy/two_charge_force); + fprintf(logfile," using %s precision FFTs\n",fft_prec); + fprintf(logfile," 3d grid and FFT values/proc = %d %d\n", + ngrid_max,nfft_both_max); + } + } + + // allocate K-space dependent memory + // don't invoke allocate peratom() or group(), will be allocated when needed + + allocate(); + cg->ghost_notify(); + cg->setup(); + + // pre-compute Green's function denomiator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + if (differentiation_flag == 1) compute_sf_precoeff(); + compute_rho_coeff(); +} + +/* ---------------------------------------------------------------------- + adjust PPPM coeffs, called initially and whenever volume has changed +------------------------------------------------------------------------- */ + +void PPPM::setup() +{ + if (triclinic) { + setup_triclinic(); + return; + } + + int i,j,k,n; + double *prd; + + // volume-dependent factors + // adjust z dimension for 2d slab PPPM + // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 + + if (triclinic == 0) prd = domain->prd; + else prd = domain->prd_lamda; + + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + delxinv = nx_pppm/xprd; + delyinv = ny_pppm/yprd; + delzinv = nz_pppm/zprd_slab; + + delvolinv = delxinv*delyinv*delzinv; + + double unitkx = (MY_2PI/xprd); + double unitky = (MY_2PI/yprd); + double unitkz = (MY_2PI/zprd_slab); + + // fkx,fky,fkz for my FFT grid pts + + double per; + + for (i = nxlo_fft; i <= nxhi_fft; i++) { + per = i - nx_pppm*(2*i/nx_pppm); + fkx[i] = unitkx*per; + } + + for (i = nylo_fft; i <= nyhi_fft; i++) { + per = i - ny_pppm*(2*i/ny_pppm); + fky[i] = unitky*per; + } + + for (i = nzlo_fft; i <= nzhi_fft; i++) { + per = i - nz_pppm*(2*i/nz_pppm); + fkz[i] = unitkz*per; + } + + // virial coefficients + + double sqk,vterm; + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) { + for (j = nylo_fft; j <= nyhi_fft; j++) { + for (i = nxlo_fft; i <= nxhi_fft; i++) { + sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k]; + if (sqk == 0.0) { + vg[n][0] = 0.0; + vg[n][1] = 0.0; + vg[n][2] = 0.0; + vg[n][3] = 0.0; + vg[n][4] = 0.0; + vg[n][5] = 0.0; + } else { + vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); + vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i]; + vg[n][1] = 1.0 + vterm*fky[j]*fky[j]; + vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k]; + vg[n][3] = vterm*fkx[i]*fky[j]; + vg[n][4] = vterm*fkx[i]*fkz[k]; + vg[n][5] = vterm*fky[j]*fkz[k]; + } + n++; + } + } + } + + if (differentiation_flag == 1) compute_gf_ad(); + else compute_gf_ik(); +} + +/* ---------------------------------------------------------------------- + adjust PPPM coeffs, called initially and whenever volume has changed + for a triclinic system +------------------------------------------------------------------------- */ + +void PPPM::setup_triclinic() +{ + int i,j,k,n; + double *prd; + + // volume-dependent factors + // adjust z dimension for 2d slab PPPM + // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 + + prd = domain->prd; + + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + // use lamda (0-1) coordinates + + delxinv = nx_pppm; + delyinv = ny_pppm; + delzinv = nz_pppm; + delvolinv = delxinv*delyinv*delzinv/volume; + + // fkx,fky,fkz for my FFT grid pts + + double per_i,per_j,per_k; + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) { + per_k = k - nz_pppm*(2*k/nz_pppm); + for (j = nylo_fft; j <= nyhi_fft; j++) { + per_j = j - ny_pppm*(2*j/ny_pppm); + for (i = nxlo_fft; i <= nxhi_fft; i++) { + per_i = i - nx_pppm*(2*i/nx_pppm); + + double unitk_lamda[3]; + unitk_lamda[0] = 2.0*MY_PI*per_i; + unitk_lamda[1] = 2.0*MY_PI*per_j; + unitk_lamda[2] = 2.0*MY_PI*per_k; + x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); + fkx[n] = unitk_lamda[0]; + fky[n] = unitk_lamda[1]; + fkz[n] = unitk_lamda[2]; + n++; + } + } + } + + // virial coefficients + + double sqk,vterm; + + for (n = 0; n < nfft; n++) { + sqk = fkx[n]*fkx[n] + fky[n]*fky[n] + fkz[n]*fkz[n]; + if (sqk == 0.0) { + vg[n][0] = 0.0; + vg[n][1] = 0.0; + vg[n][2] = 0.0; + vg[n][3] = 0.0; + vg[n][4] = 0.0; + vg[n][5] = 0.0; + } else { + vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); + vg[n][0] = 1.0 + vterm*fkx[n]*fkx[n]; + vg[n][1] = 1.0 + vterm*fky[n]*fky[n]; + vg[n][2] = 1.0 + vterm*fkz[n]*fkz[n]; + vg[n][3] = vterm*fkx[n]*fky[n]; + vg[n][4] = vterm*fkx[n]*fkz[n]; + vg[n][5] = vterm*fky[n]*fkz[n]; + } + } + + compute_gf_ik_triclinic(); +} + +/* ---------------------------------------------------------------------- + reset local grid arrays and communication stencils + called by fix balance b/c it changed sizes of processor sub-domains +------------------------------------------------------------------------- */ + +void PPPM::setup_grid() +{ + // free all arrays previously allocated + + deallocate(); + if (peratom_allocate_flag) deallocate_peratom(); + if (group_allocate_flag) deallocate_groups(); + + // reset portion of global grid that each proc owns + + set_grid_local(); + + // reallocate K-space dependent memory + // check if grid communication is now overlapping if not allowed + // don't invoke allocate peratom() or group(), will be allocated when needed + + allocate(); + + cg->ghost_notify(); + if (overlap_allowed == 0 && cg->ghost_overlap()) + error->all(FLERR,"PPPM grid stencil extends " + "beyond nearest neighbor processor"); + cg->setup(); + + // pre-compute Green's function denomiator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + if (differentiation_flag == 1) compute_sf_precoeff(); + compute_rho_coeff(); + + // pre-compute volume-dependent coeffs + + setup(); +} + +/* ---------------------------------------------------------------------- + compute the PPPM long-range force, energy, virial +------------------------------------------------------------------------- */ + +void PPPM::compute(int eflag, int vflag) +{ + int i,j; + + // set energy/virial flags + // invoke allocate_peratom() if needed for first time + + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = evflag_atom = eflag_global = vflag_global = + eflag_atom = vflag_atom = 0; + + if (evflag_atom && !peratom_allocate_flag) { + allocate_peratom(); + cg_peratom->ghost_notify(); + cg_peratom->setup(); + } + + // convert atoms from box to lamda coords + + if (triclinic == 0) boxlo = domain->boxlo; + else { + boxlo = domain->boxlo_lamda; + domain->x2lamda(atom->nlocal); + } + + // extend size of per-atom arrays if necessary + + if (atom->nlocal > nmax) { + memory->destroy(part2grid); + nmax = atom->nmax; + memory->create(part2grid,nmax,3,"pppm:part2grid"); + } + + // find grid points for all my particles + // map my particle charge onto my local 3d density grid + + particle_map(); + make_rho(); + + // all procs communicate density values from their ghost cells + // to fully sum contribution in their 3d bricks + // remap from 3d decomposition to FFT decomposition + + cg->reverse_comm(this,REVERSE_RHO); + brick2fft(); + + // compute potential gradient on my FFT grid and + // portion of e_long on this proc's FFT grid + // return gradients (electric fields) in 3d brick decomposition + // also performs per-atom calculations via poisson_peratom() + + poisson(); + + // all procs communicate E-field values + // to fill ghost cells surrounding their 3d bricks + + if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); + else cg->forward_comm(this,FORWARD_IK); + + // extra per-atom energy/virial communication + + if (evflag_atom) { + if (differentiation_flag == 1 && vflag_atom) + cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); + else if (differentiation_flag == 0) + cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); + } + + // calculate the force on my particles + + fieldforce(); + + // extra per-atom energy/virial communication + + if (evflag_atom) fieldforce_peratom(); + + // sum global energy across procs and add in volume-dependent term + + const double qscale = force->qqrd2e * scale; + + if (eflag_global) { + double energy_all; + MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); + energy = energy_all; + + energy *= 0.5*volume; + energy -= g_ewald*qsqsum/MY_PIS + + MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qscale; + } + + // sum global virial across procs + + if (vflag_global) { + double virial_all[6]; + MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]; + } + + // per-atom energy/virial + // energy includes self-energy correction + // notal accounts for TIP4P tallying eatom/vatom for ghost atoms + + if (evflag_atom) { + double *q = atom->q; + int nlocal = atom->nlocal; + int ntotal = nlocal; + if (tip4pflag) ntotal += atom->nghost; + + if (eflag_atom) { + for (i = 0; i < nlocal; i++) { + eatom[i] *= 0.5; + eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / + (g_ewald*g_ewald*volume); + eatom[i] *= qscale; + } + for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; + } + + if (vflag_atom) { + for (i = 0; i < ntotal; i++) + for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; + } + } + + // 2d slab correction + + if (slabflag == 1) slabcorr(); + + // convert atoms back from lamda to box coords + + if (triclinic) domain->lamda2x(atom->nlocal); +} + +/* ---------------------------------------------------------------------- + allocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::allocate() +{ + memory->create3d_offset(density_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_brick"); + + memory->create(density_fft,nfft_both,"pppm:density_fft"); + memory->create(greensfn,nfft_both,"pppm:greensfn"); + memory->create(work1,2*nfft_both,"pppm:work1"); + memory->create(work2,2*nfft_both,"pppm:work2"); + memory->create(vg,nfft_both,6,"pppm:vg"); + + if (triclinic == 0) { + memory->create1d_offset(fkx,nxlo_fft,nxhi_fft,"pppm:fkx"); + memory->create1d_offset(fky,nylo_fft,nyhi_fft,"pppm:fky"); + memory->create1d_offset(fkz,nzlo_fft,nzhi_fft,"pppm:fkz"); + } else { + memory->create(fkx,nfft_both,"pppm:fkx"); + memory->create(fky,nfft_both,"pppm:fky"); + memory->create(fkz,nfft_both,"pppm:fkz"); + } + + if (differentiation_flag == 1) { + memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:u_brick"); + + memory->create(sf_precoeff1,nfft_both,"pppm:sf_precoeff1"); + memory->create(sf_precoeff2,nfft_both,"pppm:sf_precoeff2"); + memory->create(sf_precoeff3,nfft_both,"pppm:sf_precoeff3"); + memory->create(sf_precoeff4,nfft_both,"pppm:sf_precoeff4"); + memory->create(sf_precoeff5,nfft_both,"pppm:sf_precoeff5"); + memory->create(sf_precoeff6,nfft_both,"pppm:sf_precoeff6"); + + } else { + memory->create3d_offset(vdx_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdx_brick"); + memory->create3d_offset(vdy_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdy_brick"); + memory->create3d_offset(vdz_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdz_brick"); + } + + // summation coeffs + + if (!stagger_flag) memory->create(gf_b,order,"pppm:gf_b"); + memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); + memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); + memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); + memory->create2d_offset(drho_coeff,order,(1-order)/2,order/2, + "pppm:drho_coeff"); + + // create 2 FFTs and a Remap + // 1st FFT keeps data in FFT decompostion + // 2nd FFT returns data in 3d brick decomposition + // remap takes data from 3d brick to FFT decomposition + + int tmp; + + fft1 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 0,0,&tmp); + + fft2 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + 0,0,&tmp); + + remap = new Remap(lmp,world, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 1,0,0,FFT_PRECISION); + + // create ghost grid object for rho and electric field communication + + int (*procneigh)[2] = comm->procneigh; + + if (differentiation_flag == 1) + cg = new CommGrid(lmp,world,1,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + else + cg = new CommGrid(lmp,world,3,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); +} + +/* ---------------------------------------------------------------------- + deallocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::deallocate() +{ + memory->destroy3d_offset(density_brick,nzlo_out,nylo_out,nxlo_out); + + if (differentiation_flag == 1) { + memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy(sf_precoeff1); + memory->destroy(sf_precoeff2); + memory->destroy(sf_precoeff3); + memory->destroy(sf_precoeff4); + memory->destroy(sf_precoeff5); + memory->destroy(sf_precoeff6); + } else { + memory->destroy3d_offset(vdx_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(vdy_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(vdz_brick,nzlo_out,nylo_out,nxlo_out); + } + + memory->destroy(density_fft); + memory->destroy(greensfn); + memory->destroy(work1); + memory->destroy(work2); + memory->destroy(vg); + + if (triclinic == 0) { + memory->destroy1d_offset(fkx,nxlo_fft); + memory->destroy1d_offset(fky,nylo_fft); + memory->destroy1d_offset(fkz,nzlo_fft); + } else { + memory->destroy(fkx); + memory->destroy(fky); + memory->destroy(fkz); + } + + memory->destroy(gf_b); + if (stagger_flag) gf_b = NULL; + memory->destroy2d_offset(rho1d,-order/2); + memory->destroy2d_offset(drho1d,-order/2); + memory->destroy2d_offset(rho_coeff,(1-order)/2); + memory->destroy2d_offset(drho_coeff,(1-order)/2); + + delete fft1; + delete fft2; + delete remap; + delete cg; +} + +/* ---------------------------------------------------------------------- + allocate per-atom memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::allocate_peratom() +{ + peratom_allocate_flag = 1; + + if (differentiation_flag != 1) + memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:u_brick"); + + memory->create3d_offset(v0_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v0_brick"); + + memory->create3d_offset(v1_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v1_brick"); + memory->create3d_offset(v2_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v2_brick"); + memory->create3d_offset(v3_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v3_brick"); + memory->create3d_offset(v4_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v4_brick"); + memory->create3d_offset(v5_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v5_brick"); + + // create ghost grid object for rho and electric field communication + + int (*procneigh)[2] = comm->procneigh; + + if (differentiation_flag == 1) + cg_peratom = + new CommGrid(lmp,world,6,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + else + cg_peratom = + new CommGrid(lmp,world,7,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); +} + +/* ---------------------------------------------------------------------- + deallocate per-atom memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::deallocate_peratom() +{ + peratom_allocate_flag = 0; + + memory->destroy3d_offset(v0_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v1_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v2_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v3_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v4_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v5_brick,nzlo_out,nylo_out,nxlo_out); + + if (differentiation_flag != 1) + memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); + + delete cg_peratom; +} + +/* ---------------------------------------------------------------------- + set global size of PPPM grid = nx,ny,nz_pppm + used for charge accumulation, FFTs, and electric field interpolation +------------------------------------------------------------------------- */ + +void PPPM::set_grid_global() +{ + // use xprd,yprd,zprd (even if triclinic, and then scale later) + // adjust z dimension for 2d slab PPPM + // 3d PPPM just uses zprd since slab_volfactor = 1.0 + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + + // make initial g_ewald estimate + // based on desired accuracy and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + double h; + bigint natoms = atom->natoms; + + if (!gewaldflag) { + if (accuracy <= 0.0) + error->all(FLERR,"KSpace accuracy must be > 0"); + g_ewald = accuracy*sqrt(natoms*cutoff*xprd*yprd*zprd) / (2.0*q2); + if (g_ewald >= 1.0) g_ewald = (1.35 - 0.15*log(accuracy))/cutoff; + else g_ewald = sqrt(-log(g_ewald)) / cutoff; + } + + // set optimal nx_pppm,ny_pppm,nz_pppm based on order and accuracy + // nz_pppm uses extended zprd_slab instead of zprd + // reduce it until accuracy target is met + + if (!gridflag) { + + if (differentiation_flag == 1 || stagger_flag) { + + h = h_x = h_y = h_z = 4.0/g_ewald; + int count = 0; + while (1) { + + // set grid dimension + nx_pppm = static_cast (xprd/h_x); + ny_pppm = static_cast (yprd/h_y); + nz_pppm = static_cast (zprd_slab/h_z); + + if (nx_pppm <= 1) nx_pppm = 2; + if (ny_pppm <= 1) ny_pppm = 2; + if (nz_pppm <= 1) nz_pppm = 2; + + //set local grid dimension + int npey_fft,npez_fft; + if (nz_pppm >= nprocs) { + npey_fft = 1; + npez_fft = nprocs; + } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + + int me_y = me % npey_fft; + int me_z = me / npey_fft; + + nxlo_fft = 0; + nxhi_fft = nx_pppm - 1; + nylo_fft = me_y*ny_pppm/npey_fft; + nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; + nzlo_fft = me_z*nz_pppm/npez_fft; + nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; + + double df_kspace = compute_df_kspace(); + + count++; + + // break loop if the accuracy has been reached or + // too many loops have been performed + + if (df_kspace <= accuracy) break; + if (count > 500) error->all(FLERR, "Could not compute grid size"); + h *= 0.95; + h_x = h_y = h_z = h; + } + + } else { + + double err; + h_x = h_y = h_z = 1.0/g_ewald; + + nx_pppm = static_cast (xprd/h_x) + 1; + ny_pppm = static_cast (yprd/h_y) + 1; + nz_pppm = static_cast (zprd_slab/h_z) + 1; + + err = estimate_ik_error(h_x,xprd,natoms); + while (err > accuracy) { + err = estimate_ik_error(h_x,xprd,natoms); + nx_pppm++; + h_x = xprd/nx_pppm; + } + + err = estimate_ik_error(h_y,yprd,natoms); + while (err > accuracy) { + err = estimate_ik_error(h_y,yprd,natoms); + ny_pppm++; + h_y = yprd/ny_pppm; + } + + err = estimate_ik_error(h_z,zprd_slab,natoms); + while (err > accuracy) { + err = estimate_ik_error(h_z,zprd_slab,natoms); + nz_pppm++; + h_z = zprd_slab/nz_pppm; + } + } + + // scale grid for triclinic skew + + if (triclinic) { + double tmp[3]; + tmp[0] = nx_pppm/xprd; + tmp[1] = ny_pppm/yprd; + tmp[2] = nz_pppm/zprd; + lamda2xT(&tmp[0],&tmp[0]); + nx_pppm = static_cast(tmp[0]) + 1; + ny_pppm = static_cast(tmp[1]) + 1; + nz_pppm = static_cast(tmp[2]) + 1; + } + } + + // boost grid size until it is factorable + + while (!factorable(nx_pppm)) nx_pppm++; + while (!factorable(ny_pppm)) ny_pppm++; + while (!factorable(nz_pppm)) nz_pppm++; + + if (triclinic == 0) { + h_x = xprd/nx_pppm; + h_y = yprd/ny_pppm; + h_z = zprd_slab/nz_pppm; + } else { + double tmp[3]; + tmp[0] = nx_pppm; + tmp[1] = ny_pppm; + tmp[2] = nz_pppm; + x2lamdaT(&tmp[0],&tmp[0]); + h_x = 1.0/tmp[0]; + h_y = 1.0/tmp[1]; + h_z = 1.0/tmp[2]; + } + + if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET) + error->all(FLERR,"PPPM grid is too large"); +} + +/* ---------------------------------------------------------------------- + check if all factors of n are in list of factors + return 1 if yes, 0 if no +------------------------------------------------------------------------- */ + +int PPPM::factorable(int n) +{ + int i; + + while (n > 1) { + for (i = 0; i < nfactors; i++) { + if (n % factors[i] == 0) { + n /= factors[i]; + break; + } + } + if (i == nfactors) return 0; + } + + return 1; +} + +/* ---------------------------------------------------------------------- + compute estimated kspace force error +------------------------------------------------------------------------- */ + +double PPPM::compute_df_kspace() +{ + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + bigint natoms = atom->natoms; + double df_kspace = 0.0; + if (differentiation_flag == 1 || stagger_flag) { + double qopt = compute_qopt(); + df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab); + } else { + double lprx = estimate_ik_error(h_x,xprd,natoms); + double lpry = estimate_ik_error(h_y,yprd,natoms); + double lprz = estimate_ik_error(h_z,zprd_slab,natoms); + df_kspace = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); + } + return df_kspace; +} + +/* ---------------------------------------------------------------------- + compute qopt +------------------------------------------------------------------------- */ + +double PPPM::compute_qopt() +{ + double qopt = 0.0; + double *prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double u1, u2, sqk; + double sum1,sum2,sum3,sum4,dot2; + + int k,l,m,nx,ny,nz; + const int twoorder = 2*order; + + for (m = nzlo_fft; m <= nzhi_fft; m++) { + const int mper = m - nz_pppm*(2*m/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + const int lper = l - ny_pppm*(2*l/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + const int kper = k - nx_pppm*(2*k/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + + sum1 = 0.0; + sum2 = 0.0; + sum3 = 0.0; + sum4 = 0.0; + for (nx = -2; nx <= 2; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + qx *= qx; + + for (ny = -2; ny <= 2; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + qy *= qy; + + for (nz = -2; nz <= 2; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + qz *= qz; + + dot2 = qx+qy+qz; + u1 = sx*sy*sz; + u2 = wx*wy*wz; + sum1 += u1*u1/dot2*MY_4PI*MY_4PI; + sum2 += u1 * u2 * MY_4PI; + sum3 += u2; + sum4 += dot2*u2; + } + } + } + sum2 *= sum2; + qopt += sum1 - sum2/(sum3*sum4); + } + } + } + } + double qopt_all; + MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); + return qopt_all; +} + +/* ---------------------------------------------------------------------- + estimate kspace force error for ik method +------------------------------------------------------------------------- */ + +double PPPM::estimate_ik_error(double h, double prd, bigint natoms) +{ + double sum = 0.0; + for (int m = 0; m < order; m++) + sum += acons[order][m] * pow(h*g_ewald,2.0*m); + double value = q2 * pow(h*g_ewald,(double)order) * + sqrt(g_ewald*prd*sqrt(MY_2PI)*sum/natoms) / (prd*prd); + + return value; +} + +/* ---------------------------------------------------------------------- + adjust the g_ewald parameter to near its optimal value + using a Newton-Raphson solver +------------------------------------------------------------------------- */ + +void PPPM::adjust_gewald() +{ + double dx; + + for (int i = 0; i < LARGE; i++) { + dx = newton_raphson_f() / derivf(); + g_ewald -= dx; + if (fabs(newton_raphson_f()) < SMALL) return; + } + + char str[128]; + sprintf(str, "Could not compute g_ewald"); + error->all(FLERR, str); +} + +/* ---------------------------------------------------------------------- + Calculate f(x) using Newton-Raphson solver + ------------------------------------------------------------------------- */ + +double PPPM::newton_raphson_f() +{ + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + bigint natoms = atom->natoms; + + double df_rspace = 2.0*q2*exp(-g_ewald*g_ewald*cutoff*cutoff) / + sqrt(natoms*cutoff*xprd*yprd*zprd); + + double df_kspace = compute_df_kspace(); + + return df_rspace - df_kspace; +} + +/* ---------------------------------------------------------------------- + Calculate numerical derivative f'(x) using forward difference + [f(x + h) - f(x)] / h + ------------------------------------------------------------------------- */ + +double PPPM::derivf() +{ + double h = 0.000001; //Derivative step-size + double df,f1,f2,g_ewald_old; + + f1 = newton_raphson_f(); + g_ewald_old = g_ewald; + g_ewald += h; + f2 = newton_raphson_f(); + g_ewald = g_ewald_old; + df = (f2 - f1)/h; + + return df; +} + +/* ---------------------------------------------------------------------- + Calculate the final estimate of the accuracy +------------------------------------------------------------------------- */ + +double PPPM::final_accuracy() +{ + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + bigint natoms = atom->natoms; + + double df_kspace = compute_df_kspace(); + double q2_over_sqrt = q2 / sqrt(natoms*cutoff*xprd*yprd*zprd); + double df_rspace = 2.0 * q2_over_sqrt * exp(-g_ewald*g_ewald*cutoff*cutoff); + double df_table = estimate_table_accuracy(q2_over_sqrt,df_rspace); + double estimated_accuracy = sqrt(df_kspace*df_kspace + df_rspace*df_rspace + + df_table*df_table); + + return estimated_accuracy; +} + +/* ---------------------------------------------------------------------- + set local subset of PPPM/FFT grid that I own + n xyz lo/hi in = 3d brick that I own (inclusive) + n xyz lo/hi out = 3d brick + ghost cells in 6 directions (inclusive) + n xyz lo/hi fft = FFT columns that I own (all of x dim, 2d decomp in yz) +------------------------------------------------------------------------- */ + +void PPPM::set_grid_local() +{ + // global indices of PPPM grid range from 0 to N-1 + // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of + // global PPPM grid that I own without ghost cells + // for slab PPPM, assign z grid as if it were not extended + + nxlo_in = static_cast (comm->xsplit[comm->myloc[0]] * nx_pppm); + nxhi_in = static_cast (comm->xsplit[comm->myloc[0]+1] * nx_pppm) - 1; + + nylo_in = static_cast (comm->ysplit[comm->myloc[1]] * ny_pppm); + nyhi_in = static_cast (comm->ysplit[comm->myloc[1]+1] * ny_pppm) - 1; + + nzlo_in = static_cast + (comm->zsplit[comm->myloc[2]] * nz_pppm/slab_volfactor); + nzhi_in = static_cast + (comm->zsplit[comm->myloc[2]+1] * nz_pppm/slab_volfactor) - 1; + + // nlower,nupper = stencil size for mapping particles to PPPM grid + + nlower = -(order-1)/2; + nupper = order/2; + + // shift values for particle <-> grid mapping + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + if (order % 2) shift = OFFSET + 0.5; + else shift = OFFSET; + if (order % 2) shiftone = 0.0; + else shiftone = 0.5; + + // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of + // global PPPM grid that my particles can contribute charge to + // effectively nlo_in,nhi_in + ghost cells + // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest + // position a particle in my box can be at + // dist[3] = particle position bound = subbox + skin/2.0 + qdist + // qdist = offset due to TIP4P fictitious charge + // convert to triclinic if necessary + // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping + // for slab PPPM, assign z grid as if it were not extended + + double *prd,*sublo,*subhi; + + if (triclinic == 0) { + prd = domain->prd; + boxlo = domain->boxlo; + sublo = domain->sublo; + subhi = domain->subhi; + } else { + prd = domain->prd_lamda; + boxlo = domain->boxlo_lamda; + sublo = domain->sublo_lamda; + subhi = domain->subhi_lamda; + } + + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + double zprd_slab = zprd*slab_volfactor; + + double dist[3]; + double cuthalf = 0.5*neighbor->skin + qdist; + if (triclinic == 0) dist[0] = dist[1] = dist[2] = cuthalf; + else kspacebbox(cuthalf,&dist[0]); + + int nlo,nhi; + + nlo = static_cast ((sublo[0]-dist[0]-boxlo[0]) * + nx_pppm/xprd + shift) - OFFSET; + nhi = static_cast ((subhi[0]+dist[0]-boxlo[0]) * + nx_pppm/xprd + shift) - OFFSET; + nxlo_out = nlo + nlower; + nxhi_out = nhi + nupper; + + nlo = static_cast ((sublo[1]-dist[1]-boxlo[1]) * + ny_pppm/yprd + shift) - OFFSET; + nhi = static_cast ((subhi[1]+dist[1]-boxlo[1]) * + ny_pppm/yprd + shift) - OFFSET; + nylo_out = nlo + nlower; + nyhi_out = nhi + nupper; + + nlo = static_cast ((sublo[2]-dist[2]-boxlo[2]) * + nz_pppm/zprd_slab + shift) - OFFSET; + nhi = static_cast ((subhi[2]+dist[2]-boxlo[2]) * + nz_pppm/zprd_slab + shift) - OFFSET; + nzlo_out = nlo + nlower; + nzhi_out = nhi + nupper; + + if (stagger_flag) { + nxhi_out++; + nyhi_out++; + nzhi_out++; + } + + // for slab PPPM, change the grid boundary for processors at +z end + // to include the empty volume between periodically repeating slabs + // for slab PPPM, want charge data communicated from -z proc to +z proc, + // but not vice versa, also want field data communicated from +z proc to + // -z proc, but not vice versa + // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells) + // also insure no other procs use ghost cells beyond +z limit + + if (slabflag) { + if (comm->myloc[2] == comm->procgrid[2]-1) + nzhi_in = nzhi_out = nz_pppm - 1; + nzhi_out = MIN(nzhi_out,nz_pppm-1); + } + + // decomposition of FFT mesh + // global indices range from 0 to N-1 + // proc owns entire x-dimension, clumps of columns in y,z dimensions + // npey_fft,npez_fft = # of procs in y,z dims + // if nprocs is small enough, proc can own 1 or more entire xy planes, + // else proc owns 2d sub-blocks of yz plane + // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions + // nlo_fft,nhi_fft = lower/upper limit of the section + // of the global FFT mesh that I own + + int npey_fft,npez_fft; + if (nz_pppm >= nprocs) { + npey_fft = 1; + npez_fft = nprocs; + } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + + int me_y = me % npey_fft; + int me_z = me / npey_fft; + + nxlo_fft = 0; + nxhi_fft = nx_pppm - 1; + nylo_fft = me_y*ny_pppm/npey_fft; + nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; + nzlo_fft = me_z*nz_pppm/npez_fft; + nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; + + // PPPM grid pts owned by this proc, including ghosts + + ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + + // FFT grids owned by this proc, without ghosts + // nfft = FFT points in FFT decomposition on this proc + // nfft_brick = FFT points in 3d brick-decomposition on this proc + // nfft_both = greater of 2 values + + nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) * + (nzhi_fft-nzlo_fft+1); + int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) * + (nzhi_in-nzlo_in+1); + nfft_both = MAX(nfft,nfft_brick); +} + +/* ---------------------------------------------------------------------- + pre-compute Green's function denominator expansion coeffs, Gamma(2n) +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_denom() +{ + int k,l,m; + + for (l = 1; l < order; l++) gf_b[l] = 0.0; + gf_b[0] = 1.0; + + for (m = 1; m < order; m++) { + for (l = m; l > 0; l--) + gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); + gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); + } + + bigint ifact = 1; + for (k = 1; k < 2*order; k++) ifact *= k; + double gaminv = 1.0/ifact; + for (l = 0; l < order; l++) gf_b[l] *= gaminv; +} + +/* ---------------------------------------------------------------------- + pre-compute modified (Hockney-Eastwood) Coulomb Green's function +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_ik() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + double sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + numerator = 12.5663706/sqk; + denominator = gf_denom(snx,sny,snz); + sum1 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + pre-compute modified (Hockney-Eastwood) Coulomb Green's function + for a triclinic system +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_ik_triclinic() +{ + double snx,sny,snz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + double sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + double tmp[3]; + tmp[0] = (g_ewald/(MY_PI*nx_pppm)) * pow(-log(EPS_HOC),0.25); + tmp[1] = (g_ewald/(MY_PI*ny_pppm)) * pow(-log(EPS_HOC),0.25); + tmp[2] = (g_ewald/(MY_PI*nz_pppm)) * pow(-log(EPS_HOC),0.25); + lamda2xT(&tmp[0],&tmp[0]); + const int nbx = static_cast (tmp[0]); + const int nby = static_cast (tmp[1]); + const int nbz = static_cast (tmp[2]); + + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(MY_PI*mper/nz_pppm)); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(MY_PI*lper/ny_pppm)); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(MY_PI*kper/nx_pppm)); + + double unitk_lamda[3]; + unitk_lamda[0] = 2.0*MY_PI*kper; + unitk_lamda[1] = 2.0*MY_PI*lper; + unitk_lamda[2] = 2.0*MY_PI*mper; + x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); + + sqk = square(unitk_lamda[0]) + square(unitk_lamda[1]) + square(unitk_lamda[2]); + + if (sqk != 0.0) { + numerator = 12.5663706/sqk; + denominator = gf_denom(snx,sny,snz); + sum1 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + argx = MY_PI*kper/nx_pppm + MY_PI*nx; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + argy = MY_PI*lper/ny_pppm + MY_PI*ny; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + argz = MY_PI*mper/nz_pppm + MY_PI*nz; + wz = powsinxx(argz,twoorder); + + double b[3]; + b[0] = 2.0*MY_PI*nx_pppm*nx; + b[1] = 2.0*MY_PI*ny_pppm*ny; + b[2] = 2.0*MY_PI*nz_pppm*nz; + x2lamdaT(&b[0],&b[0]); + + qx = unitk_lamda[0]+b[0]; + sx = exp(-0.25*square(qx/g_ewald)); + + qy = unitk_lamda[1]+b[1]; + sy = exp(-0.25*square(qy/g_ewald)); + + qz = unitk_lamda[2]+b[2]; + sz = exp(-0.25*square(qz/g_ewald)); + + dot1 = unitk_lamda[0]*qx + unitk_lamda[1]*qy + unitk_lamda[2]*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + compute optimized Green's function for energy calculation +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_ad() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz,sqk; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double numerator,denominator; + int k,l,m,n,kper,lper,mper; + + const int twoorder = 2*order; + + for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + qz = unitkz*mper; + snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + qy = unitky*lper; + sny = square(sin(0.5*qy*yprd/ny_pppm)); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + qx = unitkx*kper; + snx = square(sin(0.5*qx*xprd/nx_pppm)); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + sqk = qx*qx + qy*qy + qz*qz; + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = gf_denom(snx,sny,snz); + greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } else { + greensfn[n] = 0.0; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } + } + } + } + + // compute the coefficients for the self-force correction + + double prex, prey, prez; + prex = prey = prez = MY_PI/volume; + prex *= nx_pppm/xprd; + prey *= ny_pppm/yprd; + prez *= nz_pppm/zprd_slab; + sf_coeff[0] *= prex; + sf_coeff[1] *= prex*2; + sf_coeff[2] *= prey; + sf_coeff[3] *= prey*2; + sf_coeff[4] *= prez; + sf_coeff[5] *= prez*2; + + // communicate values with other procs + + double tmp[6]; + MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); + for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; +} + +/* ---------------------------------------------------------------------- + compute self force coefficients for ad-differentiation scheme +------------------------------------------------------------------------- */ + +void PPPM::compute_sf_precoeff() +{ + int i,k,l,m,n; + int nx,ny,nz,kper,lper,mper; + double wx0[5],wy0[5],wz0[5],wx1[5],wy1[5],wz1[5],wx2[5],wy2[5],wz2[5]; + double qx0,qy0,qz0,qx1,qy1,qz1,qx2,qy2,qz2; + double u0,u1,u2,u3,u4,u5,u6; + double sum1,sum2,sum3,sum4,sum5,sum6; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + + sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0.0; + for (i = 0; i < 5; i++) { + + qx0 = MY_2PI*(kper+nx_pppm*(i-2)); + qx1 = MY_2PI*(kper+nx_pppm*(i-1)); + qx2 = MY_2PI*(kper+nx_pppm*(i )); + wx0[i] = powsinxx(0.5*qx0/nx_pppm,order); + wx1[i] = powsinxx(0.5*qx1/nx_pppm,order); + wx2[i] = powsinxx(0.5*qx2/nx_pppm,order); + + qy0 = MY_2PI*(lper+ny_pppm*(i-2)); + qy1 = MY_2PI*(lper+ny_pppm*(i-1)); + qy2 = MY_2PI*(lper+ny_pppm*(i )); + wy0[i] = powsinxx(0.5*qy0/ny_pppm,order); + wy1[i] = powsinxx(0.5*qy1/ny_pppm,order); + wy2[i] = powsinxx(0.5*qy2/ny_pppm,order); + + qz0 = MY_2PI*(mper+nz_pppm*(i-2)); + qz1 = MY_2PI*(mper+nz_pppm*(i-1)); + qz2 = MY_2PI*(mper+nz_pppm*(i )); + + wz0[i] = powsinxx(0.5*qz0/nz_pppm,order); + wz1[i] = powsinxx(0.5*qz1/nz_pppm,order); + wz2[i] = powsinxx(0.5*qz2/nz_pppm,order); + } + + for (nx = 0; nx < 5; nx++) { + for (ny = 0; ny < 5; ny++) { + for (nz = 0; nz < 5; nz++) { + u0 = wx0[nx]*wy0[ny]*wz0[nz]; + u1 = wx1[nx]*wy0[ny]*wz0[nz]; + u2 = wx2[nx]*wy0[ny]*wz0[nz]; + u3 = wx0[nx]*wy1[ny]*wz0[nz]; + u4 = wx0[nx]*wy2[ny]*wz0[nz]; + u5 = wx0[nx]*wy0[ny]*wz1[nz]; + u6 = wx0[nx]*wy0[ny]*wz2[nz]; + + sum1 += u0*u1; + sum2 += u0*u2; + sum3 += u0*u3; + sum4 += u0*u4; + sum5 += u0*u5; + sum6 += u0*u6; + } + } + } + + // store values + + sf_precoeff1[n] = sum1; + sf_precoeff2[n] = sum2; + sf_precoeff3[n] = sum3; + sf_precoeff4[n] = sum4; + sf_precoeff5[n] = sum5; + sf_precoeff6[n++] = sum6; + } + } + } +} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPM::particle_map() +{ + int nx,ny,nz; + + double **x = atom->x; + int nlocal = atom->nlocal; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift) - OFFSET; + ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift) - OFFSET; + nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) + flag = 1; + } + + if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPM::make_rho() +{ + int l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + + // clear 3d density array + + memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + remap density from 3d brick decomposition to FFT decomposition +------------------------------------------------------------------------- */ + +void PPPM::brick2fft() +{ + int n,ix,iy,iz; + + // copy grabs inner portion of density from 3d brick + // remap could be done as pre-stage of FFT, + // but this works optimally on only double values, not complex values + + n = 0; + for (iz = nzlo_in; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_fft[n++] = density_brick[iz][iy][ix]; + + remap->perform(density_fft,density_fft,work1); +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver +------------------------------------------------------------------------- */ + +void PPPM::poisson() +{ + if (differentiation_flag == 1) poisson_ad(); + else poisson_ik(); +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for ik +------------------------------------------------------------------------- */ + +void PPPM::poisson_ik() +{ + int i,j,k,n; + double eng; + + // transform charge density (r -> k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] = density_fft[i]; + work1[n++] = ZEROF; + } + + fft1->compute(work1,work1,1); + + // global energy and virial contribution + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + if (eflag_global || vflag_global) { + if (vflag_global) { + n = 0; + for (i = 0; i < nfft; i++) { + eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; + if (eflag_global) energy += eng; + n += 2; + } + } else { + n = 0; + for (i = 0; i < nfft; i++) { + energy += + s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + n += 2; + } + } + } + + // scale by 1/total-grid-pts to get rho(k) + // multiply by Green's function to get V(k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] *= scaleinv * greensfn[i]; + work1[n++] *= scaleinv * greensfn[i]; + } + + // extra FFTs for per-atom energy/virial + + if (evflag_atom) poisson_peratom(); + + // triclinic system + + if (triclinic) { + poisson_ik_triclinic(); + return; + } + + // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) + // FFT leaves data in 3d brick decomposition + // copy it into inner portion of vdx,vdy,vdz arrays + + // x direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkx[i]*work1[n+1]; + work2[n+1] = -fkx[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdx_brick[k][j][i] = work2[n]; + n += 2; + } + + // y direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fky[j]*work1[n+1]; + work2[n+1] = -fky[j]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdy_brick[k][j][i] = work2[n]; + n += 2; + } + + // z direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkz[k]*work1[n+1]; + work2[n+1] = -fkz[k]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdz_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for ik for a triclinic system +------------------------------------------------------------------------- */ + +void PPPM::poisson_ik_triclinic() +{ + int i,j,k,n; + + // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) + // FFT leaves data in 3d brick decomposition + // copy it into inner portion of vdx,vdy,vdz arrays + + // x direction gradient + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = fkx[i]*work1[n+1]; + work2[n+1] = -fkx[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdx_brick[k][j][i] = work2[n]; + n += 2; + } + + // y direction gradient + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = fky[i]*work1[n+1]; + work2[n+1] = -fky[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdy_brick[k][j][i] = work2[n]; + n += 2; + } + + // z direction gradient + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = fkz[i]*work1[n+1]; + work2[n+1] = -fkz[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdz_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for ad +------------------------------------------------------------------------- */ + +void PPPM::poisson_ad() +{ + int i,j,k,n; + double eng; + + // transform charge density (r -> k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] = density_fft[i]; + work1[n++] = ZEROF; + } + + fft1->compute(work1,work1,1); + + // global energy and virial contribution + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + if (eflag_global || vflag_global) { + if (vflag_global) { + n = 0; + for (i = 0; i < nfft; i++) { + eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; + if (eflag_global) energy += eng; + n += 2; + } + } else { + n = 0; + for (i = 0; i < nfft; i++) { + energy += + s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + n += 2; + } + } + } + + // scale by 1/total-grid-pts to get rho(k) + // multiply by Green's function to get V(k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] *= scaleinv * greensfn[i]; + work1[n++] *= scaleinv * greensfn[i]; + } + + // extra FFTs for per-atom energy/virial + + if (vflag_atom) poisson_peratom(); + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]; + work2[n+1] = work1[n+1]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + u_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for per-atom energy/virial +------------------------------------------------------------------------- */ + +void PPPM::poisson_peratom() +{ + int i,j,k,n; + + // energy + + if (eflag_atom && differentiation_flag != 1) { + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]; + work2[n+1] = work1[n+1]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + u_brick[k][j][i] = work2[n]; + n += 2; + } + } + + // 6 components of virial in v0 thru v5 + + if (!vflag_atom) return; + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][0]; + work2[n+1] = work1[n+1]*vg[i][0]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v0_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][1]; + work2[n+1] = work1[n+1]*vg[i][1]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v1_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][2]; + work2[n+1] = work1[n+1]*vg[i][2]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v2_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][3]; + work2[n+1] = work1[n+1]*vg[i][3]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v3_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][4]; + work2[n+1] = work1[n+1]*vg[i][4]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v4_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][5]; + work2[n+1] = work1[n+1]*vg[i][5]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v5_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles +------------------------------------------------------------------------- */ + +void PPPM::fieldforce() +{ + if (differentiation_flag == 1) fieldforce_ad(); + else fieldforce_ik(); +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ik +------------------------------------------------------------------------- */ + +void PPPM::fieldforce_ik() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR ekx,eky,ekz; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ekx -= x0*vdx_brick[mz][my][mx]; + eky -= x0*vdy_brick[mz][my][mx]; + ekz -= x0*vdz_brick[mz][my][mx]; + } + } + } + + // convert E-field to force + + const double qfactor = force->qqrd2e * scale * q[i]; + f[i][0] += qfactor*ekx; + f[i][1] += qfactor*eky; + if (slabflag != 2) f[i][2] += qfactor*ekz; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ad +------------------------------------------------------------------------- */ + +void PPPM::fieldforce_ad() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz; + FFT_SCALAR ekx,eky,ekz; + double s1,s2,s3; + double sf = 0.0; + double *prd; + + prd = domain->prd; + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + + double hx_inv = nx_pppm/xprd; + double hy_inv = ny_pppm/yprd; + double hz_inv = nz_pppm/zprd; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + compute_drho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; + } + } + } + ekx *= hx_inv; + eky *= hy_inv; + ekz *= hz_inv; + + // convert E-field to force and substract self forces + + const double qfactor = force->qqrd2e * scale; + + s1 = x[i][0]*hx_inv; + s2 = x[i][1]*hy_inv; + s3 = x[i][2]*hz_inv; + sf = sf_coeff[0]*sin(2*MY_PI*s1); + sf += sf_coeff[1]*sin(4*MY_PI*s1); + sf *= 2*q[i]*q[i]; + f[i][0] += qfactor*(ekx*q[i] - sf); + + sf = sf_coeff[2]*sin(2*MY_PI*s2); + sf += sf_coeff[3]*sin(4*MY_PI*s2); + sf *= 2*q[i]*q[i]; + f[i][1] += qfactor*(eky*q[i] - sf); + + + sf = sf_coeff[4]*sin(2*MY_PI*s3); + sf += sf_coeff[5]*sin(4*MY_PI*s3); + sf *= 2*q[i]*q[i]; + if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get per-atom energy/virial +------------------------------------------------------------------------- */ + +void PPPM::fieldforce_peratom() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR u,v0,v1,v2,v3,v4,v5; + + // loop over my charges, interpolate from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + if (eflag_atom) u += x0*u_brick[mz][my][mx]; + if (vflag_atom) { + v0 += x0*v0_brick[mz][my][mx]; + v1 += x0*v1_brick[mz][my][mx]; + v2 += x0*v2_brick[mz][my][mx]; + v3 += x0*v3_brick[mz][my][mx]; + v4 += x0*v4_brick[mz][my][mx]; + v5 += x0*v5_brick[mz][my][mx]; + } + } + } + } + + if (eflag_atom) eatom[i] += q[i]*u; + if (vflag_atom) { + vatom[i][0] += q[i]*v0; + vatom[i][1] += q[i]*v1; + vatom[i][2] += q[i]*v2; + vatom[i][3] += q[i]*v3; + vatom[i][4] += q[i]*v4; + vatom[i][5] += q[i]*v5; + } + } +} + +/* ---------------------------------------------------------------------- + pack own values to buf to send to another proc +------------------------------------------------------------------------- */ + +void PPPM::pack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + int n = 0; + + if (flag == FORWARD_IK) { + FFT_SCALAR *xsrc = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *ysrc = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *zsrc = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + buf[n++] = xsrc[list[i]]; + buf[n++] = ysrc[list[i]]; + buf[n++] = zsrc[list[i]]; + } + } else if (flag == FORWARD_AD) { + FFT_SCALAR *src = &u_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + buf[i] = src[list[i]]; + } else if (flag == FORWARD_IK_PERATOM) { + FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + if (eflag_atom) buf[n++] = esrc[list[i]]; + if (vflag_atom) { + buf[n++] = v0src[list[i]]; + buf[n++] = v1src[list[i]]; + buf[n++] = v2src[list[i]]; + buf[n++] = v3src[list[i]]; + buf[n++] = v4src[list[i]]; + buf[n++] = v5src[list[i]]; + } + } + } else if (flag == FORWARD_AD_PERATOM) { + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + buf[n++] = v0src[list[i]]; + buf[n++] = v1src[list[i]]; + buf[n++] = v2src[list[i]]; + buf[n++] = v3src[list[i]]; + buf[n++] = v4src[list[i]]; + buf[n++] = v5src[list[i]]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack another proc's own values from buf and set own ghost values +------------------------------------------------------------------------- */ + +void PPPM::unpack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + int n = 0; + + if (flag == FORWARD_IK) { + FFT_SCALAR *xdest = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *ydest = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *zdest = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + xdest[list[i]] = buf[n++]; + ydest[list[i]] = buf[n++]; + zdest[list[i]] = buf[n++]; + } + } else if (flag == FORWARD_AD) { + FFT_SCALAR *dest = &u_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + dest[list[i]] = buf[i]; + } else if (flag == FORWARD_IK_PERATOM) { + FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + if (eflag_atom) esrc[list[i]] = buf[n++]; + if (vflag_atom) { + v0src[list[i]] = buf[n++]; + v1src[list[i]] = buf[n++]; + v2src[list[i]] = buf[n++]; + v3src[list[i]] = buf[n++]; + v4src[list[i]] = buf[n++]; + v5src[list[i]] = buf[n++]; + } + } + } else if (flag == FORWARD_AD_PERATOM) { + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + v0src[list[i]] = buf[n++]; + v1src[list[i]] = buf[n++]; + v2src[list[i]] = buf[n++]; + v3src[list[i]] = buf[n++]; + v4src[list[i]] = buf[n++]; + v5src[list[i]] = buf[n++]; + } + } +} + +/* ---------------------------------------------------------------------- + pack ghost values into buf to send to another proc +------------------------------------------------------------------------- */ + +void PPPM::pack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + if (flag == REVERSE_RHO) { + FFT_SCALAR *src = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + buf[i] = src[list[i]]; + } +} + +/* ---------------------------------------------------------------------- + unpack another proc's ghost values from buf and add to own values +------------------------------------------------------------------------- */ + +void PPPM::unpack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + if (flag == REVERSE_RHO) { + FFT_SCALAR *dest = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + dest[list[i]] += buf[i]; + } +} + +/* ---------------------------------------------------------------------- + map nprocs to NX by NY grid as PX by PY procs - return optimal px,py +------------------------------------------------------------------------- */ + +void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py) +{ + // loop thru all possible factorizations of nprocs + // surf = surface area of largest proc sub-domain + // innermost if test minimizes surface area and surface/volume ratio + + int bestsurf = 2 * (nx + ny); + int bestboxx = 0; + int bestboxy = 0; + + int boxx,boxy,surf,ipx,ipy; + + ipx = 1; + while (ipx <= nprocs) { + if (nprocs % ipx == 0) { + ipy = nprocs/ipx; + boxx = nx/ipx; + if (nx % ipx) boxx++; + boxy = ny/ipy; + if (ny % ipy) boxy++; + surf = boxx + boxy; + if (surf < bestsurf || + (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) { + bestsurf = surf; + bestboxx = boxx; + bestboxy = boxy; + *px = ipx; + *py = ipy; + } + } + ipx++; + } +} + +/* ---------------------------------------------------------------------- + charge assignment into rho1d + dx,dy,dz = distance of particle from "lower left" grid point +------------------------------------------------------------------------- */ + +void PPPM::compute_rho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, + const FFT_SCALAR &dz) +{ + int k,l; + FFT_SCALAR r1,r2,r3; + + for (k = (1-order)/2; k <= order/2; k++) { + r1 = r2 = r3 = ZEROF; + + for (l = order-1; l >= 0; l--) { + r1 = rho_coeff[l][k] + r1*dx; + r2 = rho_coeff[l][k] + r2*dy; + r3 = rho_coeff[l][k] + r3*dz; + } + rho1d[0][k] = r1; + rho1d[1][k] = r2; + rho1d[2][k] = r3; + } +} + +/* ---------------------------------------------------------------------- + charge assignment into drho1d + dx,dy,dz = distance of particle from "lower left" grid point +------------------------------------------------------------------------- */ + +void PPPM::compute_drho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, + const FFT_SCALAR &dz) +{ + int k,l; + FFT_SCALAR r1,r2,r3; + + for (k = (1-order)/2; k <= order/2; k++) { + r1 = r2 = r3 = ZEROF; + + for (l = order-2; l >= 0; l--) { + r1 = drho_coeff[l][k] + r1*dx; + r2 = drho_coeff[l][k] + r2*dy; + r3 = drho_coeff[l][k] + r3*dz; + } + drho1d[0][k] = r1; + drho1d[1][k] = r2; + drho1d[2][k] = r3; + } +} + +/* ---------------------------------------------------------------------- + generate coeffients for the weight function of order n + + (n-1) + Wn(x) = Sum wn(k,x) , Sum is over every other integer + k=-(n-1) + For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1 + k is odd integers if n is even and even integers if n is odd + --- + | n-1 + | Sum a(l,j)*(x-k/2)**l if abs(x-k/2) < 1/2 + wn(k,x) = < l=0 + | + | 0 otherwise + --- + a coeffients are packed into the array rho_coeff to eliminate zeros + rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k) +------------------------------------------------------------------------- */ + +void PPPM::compute_rho_coeff() +{ + int j,k,l,m; + FFT_SCALAR s; + + FFT_SCALAR **a; + memory->create2d_offset(a,order,-order,order,"pppm:a"); + + for (k = -order; k <= order; k++) + for (l = 0; l < order; l++) + a[l][k] = 0.0; + + a[0][0] = 1.0; + for (j = 1; j < order; j++) { + for (k = -j; k <= j; k += 2) { + s = 0.0; + for (l = 0; l < j; l++) { + a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1); +#ifdef FFT_SINGLE + s += powf(0.5,(float) l+1) * + (a[l][k-1] + powf(-1.0,(float) l) * a[l][k+1]) / (l+1); +#else + s += pow(0.5,(double) l+1) * + (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1); +#endif + } + a[0][k] = s; + } + } + + m = (1-order)/2; + for (k = -(order-1); k < order; k += 2) { + for (l = 0; l < order; l++) + rho_coeff[l][m] = a[l][k]; + for (l = 1; l < order; l++) + drho_coeff[l-1][m] = l*a[l][k]; + m++; + } + + memory->destroy2d_offset(a,-order); +} + +/* ---------------------------------------------------------------------- + Slab-geometry correction term to dampen inter-slab interactions between + periodically repeating slabs. Yields good approximation to 2D Ewald if + adequate empty space is left between repeating slabs (J. Chem. Phys. + 111, 3155). Slabs defined here to be parallel to the xy plane. +------------------------------------------------------------------------- */ + +void PPPM::slabcorr() +{ + // compute local contribution to global dipole moment + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + double dipole = 0.0; + for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; + + // sum local contributions to get global dipole moment + + double dipole_all; + MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); + + // compute corrections + + const double e_slabcorr = MY_2PI*dipole_all*dipole_all/volume; + const double qscale = force->qqrd2e * scale; + + if (eflag_global) energy += qscale * e_slabcorr; + + // per-atom energy + + if (eflag_atom) { + double efact = MY_2PI*dipole_all/volume; + for (int i = 0; i < nlocal; i++) eatom[i] += qscale * q[i]*x[i][2]*efact; + } + + // add on force corrections + + double ffact = -4.0*MY_PI*dipole_all/volume; + double **f = atom->f; + + for (int i = 0; i < nlocal; i++) f[i][2] += qscale * q[i]*ffact; +} + +/* ---------------------------------------------------------------------- + perform and time the 1d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPM::timing_1d(int n, double &time1d) +{ + double time1,time2; + + for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->timing1d(work1,nfft_both,1); + fft2->timing1d(work1,nfft_both,-1); + if (differentiation_flag != 1) { + fft2->timing1d(work1,nfft_both,-1); + fft2->timing1d(work1,nfft_both,-1); + } + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time1d = time2 - time1; + + if (differentiation_flag) return 2; + return 4; +} + +/* ---------------------------------------------------------------------- + perform and time the 3d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPM::timing_3d(int n, double &time3d) +{ + double time1,time2; + + for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->compute(work1,work1,1); + fft2->compute(work1,work1,-1); + if (differentiation_flag != 1) { + fft2->compute(work1,work1,-1); + fft2->compute(work1,work1,-1); + } + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time3d = time2 - time1; + + if (differentiation_flag) return 2; + return 4; +} + +/* ---------------------------------------------------------------------- + memory usage of local arrays +------------------------------------------------------------------------- */ + +double PPPM::memory_usage() +{ + double bytes = nmax*3 * sizeof(double); + int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + if (differentiation_flag == 1) { + bytes += 2 * nbrick * sizeof(FFT_SCALAR); + } else { + bytes += 4 * nbrick * sizeof(FFT_SCALAR); + } + if (triclinic) bytes += 3 * nfft_both * sizeof(double); + bytes += 6 * nfft_both * sizeof(double); + bytes += nfft_both * sizeof(double); + bytes += nfft_both*5 * sizeof(FFT_SCALAR); + + if (peratom_allocate_flag) + bytes += 6 * nbrick * sizeof(FFT_SCALAR); + + if (group_allocate_flag) { + bytes += 2 * nbrick * sizeof(FFT_SCALAR); + bytes += 2 * nfft_both * sizeof(FFT_SCALAR);; + } + + bytes += cg->memory_usage(); + + return bytes; +} + +/* ---------------------------------------------------------------------- + group-group interactions + ------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + compute the PPPM total long-range force and energy for groups A and B + ------------------------------------------------------------------------- */ + +void PPPM::compute_group_group(int groupbit_A, int groupbit_B, int BA_flag) +{ + if (slabflag) + error->all(FLERR,"Cannot (yet) use K-space slab " + "correction with compute group/group"); + + if (differentiation_flag) + error->all(FLERR,"Cannot (yet) use 'kspace_modify " + "diff ad' with compute group/group"); + + if (!group_allocate_flag) allocate_groups(); + + // convert atoms from box to lamda coords + + if (triclinic == 0) boxlo = domain->boxlo; + else { + boxlo = domain->boxlo_lamda; + domain->x2lamda(atom->nlocal); + } + + e2group = 0; //energy + f2group[0] = 0; //force in x-direction + f2group[1] = 0; //force in y-direction + f2group[2] = 0; //force in z-direction + + // map my particle charge onto my local 3d density grid + + make_rho_groups(groupbit_A,groupbit_B,BA_flag); + + // all procs communicate density values from their ghost cells + // to fully sum contribution in their 3d bricks + // remap from 3d decomposition to FFT decomposition + + // temporarily store and switch pointers so we can + // use brick2fft() for groups A and B (without + // writing an additional function) + + FFT_SCALAR ***density_brick_real = density_brick; + FFT_SCALAR *density_fft_real = density_fft; + + // group A + + density_brick = density_A_brick; + density_fft = density_A_fft; + + cg->reverse_comm(this,REVERSE_RHO); + brick2fft(); + + // group B + + density_brick = density_B_brick; + density_fft = density_B_fft; + + cg->reverse_comm(this,REVERSE_RHO); + brick2fft(); + + // switch back pointers + + density_brick = density_brick_real; + density_fft = density_fft_real; + + // compute potential gradient on my FFT grid and + // portion of group-group energy/force on this proc's FFT grid + + poisson_groups(BA_flag); + + const double qscale = force->qqrd2e * scale; + + // total group A <--> group B energy + // self and boundary correction terms are in compute_group_group.cpp + + double e2group_all; + MPI_Allreduce(&e2group,&e2group_all,1,MPI_DOUBLE,MPI_SUM,world); + e2group = e2group_all; + + e2group *= qscale*0.5*volume; + + // total group A <--> group B force + + double f2group_all[3]; + MPI_Allreduce(f2group,f2group_all,3,MPI_DOUBLE,MPI_SUM,world); + + for (int i = 0; i < 3; i++) f2group[i] = qscale*volume*f2group_all[i]; + + // convert atoms back from lamda to box coords + + if (triclinic) domain->lamda2x(atom->nlocal); +} + +/* ---------------------------------------------------------------------- + allocate group-group memory that depends on # of K-vectors and order + ------------------------------------------------------------------------- */ + +void PPPM::allocate_groups() +{ + group_allocate_flag = 1; + + memory->create3d_offset(density_A_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_A_brick"); + memory->create3d_offset(density_B_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_B_brick"); + memory->create(density_A_fft,nfft_both,"pppm:density_A_fft"); + memory->create(density_B_fft,nfft_both,"pppm:density_B_fft"); +} + +/* ---------------------------------------------------------------------- + deallocate group-group memory that depends on # of K-vectors and order + ------------------------------------------------------------------------- */ + +void PPPM::deallocate_groups() +{ + group_allocate_flag = 0; + + memory->destroy3d_offset(density_A_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(density_B_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy(density_A_fft); + memory->destroy(density_B_fft); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid for group-group interactions + ------------------------------------------------------------------------- */ + +void PPPM::make_rho_groups(int groupbit_A, int groupbit_B, int BA_flag) +{ + int l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + + // clear 3d density arrays + + memset(&(density_A_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + memset(&(density_B_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + int *mask = atom->mask; + + for (int i = 0; i < nlocal; i++) { + + if ((mask[i] & groupbit_A) && (mask[i] & groupbit_B)) + if (BA_flag) continue; + + if ((mask[i] & groupbit_A) || (mask[i] & groupbit_B)) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + + // group A + + if (mask[i] & groupbit_A) + density_A_brick[mz][my][mx] += x0*rho1d[0][l]; + + // group B + + if (mask[i] & groupbit_B) + density_B_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for group-group interactions + ------------------------------------------------------------------------- */ + +void PPPM::poisson_groups(int BA_flag) +{ + int i,j,k,n; + + // reuse memory (already declared) + + FFT_SCALAR *work_A = work1; + FFT_SCALAR *work_B = work2; + + // transform charge density (r -> k) + + // group A + + n = 0; + for (i = 0; i < nfft; i++) { + work_A[n++] = density_A_fft[i]; + work_A[n++] = ZEROF; + } + + fft1->compute(work_A,work_A,1); + + // group B + + n = 0; + for (i = 0; i < nfft; i++) { + work_B[n++] = density_B_fft[i]; + work_B[n++] = ZEROF; + } + + fft1->compute(work_B,work_B,1); + + // group-group energy and force contribution, + // keep everything in reciprocal space so + // no inverse FFTs needed + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + // energy + + n = 0; + for (i = 0; i < nfft; i++) { + e2group += s2 * greensfn[i] * + (work_A[n]*work_B[n] + work_A[n+1]*work_B[n+1]); + n += 2; + } + + if (BA_flag) return; + + + // multiply by Green's function and s2 + // (only for work_A so it is not squared below) + + n = 0; + for (i = 0; i < nfft; i++) { + work_A[n++] *= s2 * greensfn[i]; + work_A[n++] *= s2 * greensfn[i]; + } + + // triclinic system + + if (triclinic) { + poisson_groups_triclinic(); + return; + } + + double partial_group; + + // force, x direction + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[0] += fkx[i] * partial_group; + n += 2; + } + + // force, y direction + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[1] += fky[j] * partial_group; + n += 2; + } + + // force, z direction + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[2] += fkz[k] * partial_group; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for group-group interactions + for a triclinic system + ------------------------------------------------------------------------- */ + +void PPPM::poisson_groups_triclinic() +{ + int i,j,k,n; + + // reuse memory (already declared) + + FFT_SCALAR *work_A = work1; + FFT_SCALAR *work_B = work2; + + double partial_group; + + // force, x direction + + n = 0; + for (i = 0; i < nfft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[0] += fkx[i] * partial_group; + n += 2; + } + + // force, y direction + + n = 0; + for (i = 0; i < nfft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[1] += fky[i] * partial_group; + n += 2; + } + + // force, z direction + + n = 0; + for (i = 0; i < nfft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[2] += fkz[i] * partial_group; + n += 2; + } +} diff --git a/src/KSPACE/pppm.h b/src/KSPACE/pppm.h index 7da6d80560..133d425b6d 100644 --- a/src/KSPACE/pppm.h +++ b/src/KSPACE/pppm.h @@ -1,335 +1,335 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 KSPACE_CLASS - -KSpaceStyle(pppm,PPPM) - -#else - -#ifndef LMP_PPPM_H -#define LMP_PPPM_H - -#include "lmptype.h" -#include "mpi.h" - -#ifdef FFT_SINGLE -typedef float FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_FLOAT -#else -typedef double FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_DOUBLE -#endif - -#include "kspace.h" - -namespace LAMMPS_NS { - -class PPPM : public KSpace { - public: - PPPM(class LAMMPS *, int, char **); - virtual ~PPPM(); - virtual void init(); - virtual void setup(); - void setup_grid(); - virtual void compute(int, int); - virtual int timing_1d(int, double &); - virtual int timing_3d(int, double &); - virtual double memory_usage(); - - virtual void compute_group_group(int, int, int); - - protected: - int me,nprocs; - int nfactors; - int *factors; - double qsum,qsqsum,q2; - double cutoff; - double volume; - double delxinv,delyinv,delzinv,delvolinv; - double h_x,h_y,h_z; - double shift,shiftone; - int peratom_allocate_flag; - - int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; - int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; - int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; - int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; - int nlower,nupper; - int ngrid,nfft,nfft_both; - - FFT_SCALAR ***density_brick; - FFT_SCALAR ***vdx_brick,***vdy_brick,***vdz_brick; - FFT_SCALAR ***u_brick; - FFT_SCALAR ***v0_brick,***v1_brick,***v2_brick; - FFT_SCALAR ***v3_brick,***v4_brick,***v5_brick; - double *greensfn; - double **vg; - double *fkx,*fky,*fkz; - FFT_SCALAR *density_fft; - FFT_SCALAR *work1,*work2; - - double *gf_b; - FFT_SCALAR **rho1d,**rho_coeff,**drho1d,**drho_coeff; - double *sf_precoeff1, *sf_precoeff2, *sf_precoeff3; - double *sf_precoeff4, *sf_precoeff5, *sf_precoeff6; - double sf_coeff[6]; // coefficients for calculating ad self-forces - double **acons; - - // group-group interactions - - int group_allocate_flag; - FFT_SCALAR ***density_A_brick,***density_B_brick; - FFT_SCALAR *density_A_fft,*density_B_fft; - - class FFT3d *fft1,*fft2; - class Remap *remap; - class CommGrid *cg; - class CommGrid *cg_peratom; - - int **part2grid; // storage for particle -> grid mapping - int nmax; - - double *boxlo; - // TIP4P settings - int typeH,typeO; // atom types of TIP4P water H and O atoms - double qdist; // distance from O site to negative charge - double alpha; // geometric factor - - void set_grid_global(); - void set_grid_local(); - void adjust_gewald(); - double newton_raphson_f(); - double derivf(); - double final_accuracy(); - - virtual void allocate(); - virtual void allocate_peratom(); - virtual void deallocate(); - virtual void deallocate_peratom(); - int factorable(int); - double compute_df_kspace(); - double estimate_ik_error(double, double, bigint); - double compute_qopt(); - void compute_gf_denom(); - virtual void compute_gf_ik(); - virtual void compute_gf_ad(); - void compute_sf_precoeff(); - - virtual void particle_map(); - virtual void make_rho(); - virtual void brick2fft(); - - virtual void poisson(); - virtual void poisson_ik(); - virtual void poisson_ad(); - - virtual void fieldforce(); - virtual void fieldforce_ik(); - virtual void fieldforce_ad(); - - virtual void poisson_peratom(); - virtual void fieldforce_peratom(); - void procs2grid2d(int,int,int,int *, int*); - void compute_rho1d(const FFT_SCALAR &, const FFT_SCALAR &, - const FFT_SCALAR &); - void compute_drho1d(const FFT_SCALAR &, const FFT_SCALAR &, - const FFT_SCALAR &); - void compute_rho_coeff(); - void slabcorr(); - - // grid communication - - virtual void pack_forward(int, FFT_SCALAR *, int, int *); - virtual void unpack_forward(int, FFT_SCALAR *, int, int *); - virtual void pack_reverse(int, FFT_SCALAR *, int, int *); - virtual void unpack_reverse(int, FFT_SCALAR *, int, int *); - - // triclinic - - int triclinic; // domain settings, orthog or triclinic - void setup_triclinic(); - void compute_gf_ik_triclinic(); - void poisson_ik_triclinic(); - void poisson_groups_triclinic(); - - // group-group interactions - - virtual void allocate_groups(); - virtual void deallocate_groups(); - virtual void make_rho_groups(int, int, int); - virtual void poisson_groups(int); - -/* ---------------------------------------------------------------------- - denominator for Hockney-Eastwood Green's function - of x,y,z = sin(kx*deltax/2), etc - - inf n-1 - S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l - j=-inf l=0 - - = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) - gf_b = denominator expansion coeffs -------------------------------------------------------------------------- */ - - inline double gf_denom(const double &x, const double &y, - const double &z) const { - double sx,sy,sz; - sz = sy = sx = 0.0; - for (int l = order-1; l >= 0; l--) { - sx = gf_b[l] + sx*x; - sy = gf_b[l] + sy*y; - sz = gf_b[l] + sz*z; - } - double s = sx*sy*sz; - return s*s; - }; -}; - -} - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Illegal ... command - -Self-explanatory. Check the input script syntax and compare to the -documentation for the command. You can use -echo screen as a -command-line option when running LAMMPS to see the offending line. - -E: Cannot (yet) use PPPM with triclinic box and 'kspace_modify diff ad' - -This feature is not yet supported. - -E: Cannot (yet) use PPPM with triclinic box and slab correction - -This feature is not yet supported. - -E: Cannot use PPPM with 2d simulation - -The kspace style pppm cannot be used in 2d simulations. You can use -2d PPPM in a 3d simulation; see the kspace_modify command. - -E: Kspace style requires atom attribute q - -The atom style defined does not have these attributes. - -E: Cannot use nonperiodic boundaries with PPPM - -For kspace style pppm, all 3 dimensions must have periodic boundaries -unless you use the kspace_modify command to define a 2d slab with a -non-periodic z dimension. - -E: Incorrect boundaries with slab PPPM - -Must have periodic x,y dimensions and non-periodic z dimension to use -2d slab option with PPPM. - -E: PPPM order cannot be < 2 or > than %d - -This is a limitation of the PPPM implementation in LAMMPS. - -E: KSpace style is incompatible with Pair style - -Setting a kspace style requires that a pair style with a long-range -Coulombic or dispersion component be used. - -E: Bond and angle potentials must be defined for TIP4P - -Cannot use TIP4P pair potential unless bond and angle potentials -are defined. - -E: Bad TIP4P angle type for PPPM/TIP4P - -Specified angle type is not valid. - -E: Bad TIP4P bond type for PPPM/TIP4P - -Specified bond type is not valid. - -E: Cannot (yet) use PPPM with triclinic box and TIP4P - -This feature is not yet supported. - -E: Cannot use kspace solver on system with no charge - -No atoms in system have a non-zero charge. - -W: System is not charge neutral, net charge = %g - -The total charge on all atoms on the system is not 0.0, which -is not valid for the long-range Coulombic solvers. - -W: Reducing PPPM order b/c stencil extends beyond nearest neighbor processor - -This may lead to a larger grid than desired. See the kspace_modify overlap -command to prevent changing of the PPPM order. - -E: PPPM order < minimum allowed order - -The default minimum order is 2. This can be reset by the -kspace_modify minorder command. - -E: PPPM grid stencil extends beyond nearest neighbor processor - -This is not allowed if the kspace_modify overlap setting is no. - -E: KSpace accuracy must be > 0 - -The kspace accuracy designated in the input must be greater than zero. - -E: Could not compute grid size - -The code is unable to compute a grid size consistent with the desired -accuracy. This error should not occur for typical problems. Please -send an email to the developers. - -E: PPPM grid is too large - -The global PPPM grid is larger than OFFSET in one or more dimensions. -OFFSET is currently set to 4096. You likely need to decrease the -requested accuracy. - -E: Could not compute g_ewald - -The Newton-Raphson solver failed to converge to a good value for -g_ewald. This error should not occur for typical problems. Please -send an email to the developers. - -E: Out of range atoms - cannot compute PPPM - -One or more atoms are attempting to map their charge to a PPPM grid -point that is not owned by a processor. This is likely for one of two -reasons, both of them bad. First, it may mean that an atom near the -boundary of a processor's sub-domain has moved more than 1/2 the -"neighbor skin distance"_neighbor.html without neighbor lists being -rebuilt and atoms being migrated to new processors. This also means -you may be missing pairwise interactions that need to be computed. -The solution is to change the re-neighboring criteria via the -"neigh_modify"_neigh_modify command. The safest settings are "delay 0 -every 1 check yes". Second, it may mean that an atom has moved far -outside a processor's sub-domain or even the entire simulation box. -This indicates bad physics, e.g. due to highly overlapping atoms, too -large a timestep, etc. - -E: Cannot (yet) use K-space slab correction with compute group/group - -This option is not yet supported. - -E: Cannot (yet) use 'kspace_modify diff ad' with compute group/group - -This option is not yet supported. - -*/ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 KSPACE_CLASS + +KSpaceStyle(pppm,PPPM) + +#else + +#ifndef LMP_PPPM_H +#define LMP_PPPM_H + +#include "lmptype.h" +#include "mpi.h" + +#ifdef FFT_SINGLE +typedef float FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_FLOAT +#else +typedef double FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_DOUBLE +#endif + +#include "kspace.h" + +namespace LAMMPS_NS { + +class PPPM : public KSpace { + public: + PPPM(class LAMMPS *, int, char **); + virtual ~PPPM(); + virtual void init(); + virtual void setup(); + void setup_grid(); + virtual void compute(int, int); + virtual int timing_1d(int, double &); + virtual int timing_3d(int, double &); + virtual double memory_usage(); + + virtual void compute_group_group(int, int, int); + + protected: + int me,nprocs; + int nfactors; + int *factors; + double qsum,qsqsum,q2; + double cutoff; + double volume; + double delxinv,delyinv,delzinv,delvolinv; + double h_x,h_y,h_z; + double shift,shiftone; + int peratom_allocate_flag; + + int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; + int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; + int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; + int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; + int nlower,nupper; + int ngrid,nfft,nfft_both; + + FFT_SCALAR ***density_brick; + FFT_SCALAR ***vdx_brick,***vdy_brick,***vdz_brick; + FFT_SCALAR ***u_brick; + FFT_SCALAR ***v0_brick,***v1_brick,***v2_brick; + FFT_SCALAR ***v3_brick,***v4_brick,***v5_brick; + double *greensfn; + double **vg; + double *fkx,*fky,*fkz; + FFT_SCALAR *density_fft; + FFT_SCALAR *work1,*work2; + + double *gf_b; + FFT_SCALAR **rho1d,**rho_coeff,**drho1d,**drho_coeff; + double *sf_precoeff1, *sf_precoeff2, *sf_precoeff3; + double *sf_precoeff4, *sf_precoeff5, *sf_precoeff6; + double sf_coeff[6]; // coefficients for calculating ad self-forces + double **acons; + + // group-group interactions + + int group_allocate_flag; + FFT_SCALAR ***density_A_brick,***density_B_brick; + FFT_SCALAR *density_A_fft,*density_B_fft; + + class FFT3d *fft1,*fft2; + class Remap *remap; + class CommGrid *cg; + class CommGrid *cg_peratom; + + int **part2grid; // storage for particle -> grid mapping + int nmax; + + double *boxlo; + // TIP4P settings + int typeH,typeO; // atom types of TIP4P water H and O atoms + double qdist; // distance from O site to negative charge + double alpha; // geometric factor + + void set_grid_global(); + void set_grid_local(); + void adjust_gewald(); + double newton_raphson_f(); + double derivf(); + double final_accuracy(); + + virtual void allocate(); + virtual void allocate_peratom(); + virtual void deallocate(); + virtual void deallocate_peratom(); + int factorable(int); + double compute_df_kspace(); + double estimate_ik_error(double, double, bigint); + virtual double compute_qopt(); + virtual void compute_gf_denom(); + virtual void compute_gf_ik(); + virtual void compute_gf_ad(); + void compute_sf_precoeff(); + + virtual void particle_map(); + virtual void make_rho(); + virtual void brick2fft(); + + virtual void poisson(); + virtual void poisson_ik(); + virtual void poisson_ad(); + + virtual void fieldforce(); + virtual void fieldforce_ik(); + virtual void fieldforce_ad(); + + virtual void poisson_peratom(); + virtual void fieldforce_peratom(); + void procs2grid2d(int,int,int,int *, int*); + void compute_rho1d(const FFT_SCALAR &, const FFT_SCALAR &, + const FFT_SCALAR &); + void compute_drho1d(const FFT_SCALAR &, const FFT_SCALAR &, + const FFT_SCALAR &); + void compute_rho_coeff(); + void slabcorr(); + + // grid communication + + virtual void pack_forward(int, FFT_SCALAR *, int, int *); + virtual void unpack_forward(int, FFT_SCALAR *, int, int *); + virtual void pack_reverse(int, FFT_SCALAR *, int, int *); + virtual void unpack_reverse(int, FFT_SCALAR *, int, int *); + + // triclinic + + int triclinic; // domain settings, orthog or triclinic + void setup_triclinic(); + void compute_gf_ik_triclinic(); + void poisson_ik_triclinic(); + void poisson_groups_triclinic(); + + // group-group interactions + + virtual void allocate_groups(); + virtual void deallocate_groups(); + virtual void make_rho_groups(int, int, int); + virtual void poisson_groups(int); + +/* ---------------------------------------------------------------------- + denominator for Hockney-Eastwood Green's function + of x,y,z = sin(kx*deltax/2), etc + + inf n-1 + S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l + j=-inf l=0 + + = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) + gf_b = denominator expansion coeffs +------------------------------------------------------------------------- */ + + inline double gf_denom(const double &x, const double &y, + const double &z) const { + double sx,sy,sz; + sz = sy = sx = 0.0; + for (int l = order-1; l >= 0; l--) { + sx = gf_b[l] + sx*x; + sy = gf_b[l] + sy*y; + sz = gf_b[l] + sz*z; + } + double s = sx*sy*sz; + return s*s; + }; +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Cannot (yet) use PPPM with triclinic box and 'kspace_modify diff ad' + +This feature is not yet supported. + +E: Cannot (yet) use PPPM with triclinic box and slab correction + +This feature is not yet supported. + +E: Cannot use PPPM with 2d simulation + +The kspace style pppm cannot be used in 2d simulations. You can use +2d PPPM in a 3d simulation; see the kspace_modify command. + +E: Kspace style requires atom attribute q + +The atom style defined does not have these attributes. + +E: Cannot use nonperiodic boundaries with PPPM + +For kspace style pppm, all 3 dimensions must have periodic boundaries +unless you use the kspace_modify command to define a 2d slab with a +non-periodic z dimension. + +E: Incorrect boundaries with slab PPPM + +Must have periodic x,y dimensions and non-periodic z dimension to use +2d slab option with PPPM. + +E: PPPM order cannot be < 2 or > than %d + +This is a limitation of the PPPM implementation in LAMMPS. + +E: KSpace style is incompatible with Pair style + +Setting a kspace style requires that a pair style with a long-range +Coulombic or dispersion component be used. + +E: Bond and angle potentials must be defined for TIP4P + +Cannot use TIP4P pair potential unless bond and angle potentials +are defined. + +E: Bad TIP4P angle type for PPPM/TIP4P + +Specified angle type is not valid. + +E: Bad TIP4P bond type for PPPM/TIP4P + +Specified bond type is not valid. + +E: Cannot (yet) use PPPM with triclinic box and TIP4P + +This feature is not yet supported. + +E: Cannot use kspace solver on system with no charge + +No atoms in system have a non-zero charge. + +W: System is not charge neutral, net charge = %g + +The total charge on all atoms on the system is not 0.0, which +is not valid for the long-range Coulombic solvers. + +W: Reducing PPPM order b/c stencil extends beyond nearest neighbor processor + +This may lead to a larger grid than desired. See the kspace_modify overlap +command to prevent changing of the PPPM order. + +E: PPPM order < minimum allowed order + +The default minimum order is 2. This can be reset by the +kspace_modify minorder command. + +E: PPPM grid stencil extends beyond nearest neighbor processor + +This is not allowed if the kspace_modify overlap setting is no. + +E: KSpace accuracy must be > 0 + +The kspace accuracy designated in the input must be greater than zero. + +E: Could not compute grid size + +The code is unable to compute a grid size consistent with the desired +accuracy. This error should not occur for typical problems. Please +send an email to the developers. + +E: PPPM grid is too large + +The global PPPM grid is larger than OFFSET in one or more dimensions. +OFFSET is currently set to 4096. You likely need to decrease the +requested accuracy. + +E: Could not compute g_ewald + +The Newton-Raphson solver failed to converge to a good value for +g_ewald. This error should not occur for typical problems. Please +send an email to the developers. + +E: Out of range atoms - cannot compute PPPM + +One or more atoms are attempting to map their charge to a PPPM grid +point that is not owned by a processor. This is likely for one of two +reasons, both of them bad. First, it may mean that an atom near the +boundary of a processor's sub-domain has moved more than 1/2 the +"neighbor skin distance"_neighbor.html without neighbor lists being +rebuilt and atoms being migrated to new processors. This also means +you may be missing pairwise interactions that need to be computed. +The solution is to change the re-neighboring criteria via the +"neigh_modify"_neigh_modify command. The safest settings are "delay 0 +every 1 check yes". Second, it may mean that an atom has moved far +outside a processor's sub-domain or even the entire simulation box. +This indicates bad physics, e.g. due to highly overlapping atoms, too +large a timestep, etc. + +E: Cannot (yet) use K-space slab correction with compute group/group + +This option is not yet supported. + +E: Cannot (yet) use 'kspace_modify diff ad' with compute group/group + +This option is not yet supported. + +*/ diff --git a/src/kspace.cpp b/src/kspace.cpp index ddb90c9e45..ed158df815 100644 --- a/src/kspace.cpp +++ b/src/kspace.cpp @@ -1,446 +1,447 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 "stdlib.h" -#include "string.h" -#include "kspace.h" -#include "atom.h" -#include "comm.h" -#include "force.h" -#include "pair.h" -#include "memory.h" -#include "atom_masks.h" -#include "error.h" -#include "suffix.h" -#include "domain.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -KSpace::KSpace(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) -{ - energy = 0.0; - virial[0] = virial[1] = virial[2] = virial[3] = virial[4] = virial[5] = 0.0; - - triclinic_support = 1; - ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = dipoleflag = 0; - compute_flag = 1; - group_group_enable = 0; - - order = 5; - gridflag = 0; - gewaldflag = 0; - minorder = 2; - overlap_allowed = 1; - fftbench = 1; - - order_6 = 5; - gridflag_6 = 0; - gewaldflag_6 = 0; - - slabflag = 0; - differentiation_flag = 0; - slab_volfactor = 1; - suffix_flag = Suffix::NONE; - adjust_cutoff_flag = 1; - - accuracy_absolute = -1.0; - two_charge_force = force->qqr2e * - (force->qelectron * force->qelectron) / - (force->angstrom * force->angstrom); - - maxeatom = maxvatom = 0; - eatom = NULL; - vatom = NULL; - - datamask = ALL_MASK; - datamask_ext = ALL_MASK; - - memory->create(gcons,7,7,"kspace:gcons"); - gcons[2][0] = 15.0 / 8.0; - gcons[2][1] = -5.0 / 4.0; - gcons[2][2] = 3.0 / 8.0; - gcons[3][0] = 35.0 / 16.0; - gcons[3][1] = -35.0 / 16.0; - gcons[3][2] = 21.0 / 16.0; - gcons[3][3] = -5.0 / 16.0; - gcons[4][0] = 315.0 / 128.0; - gcons[4][1] = -105.0 / 32.0; - gcons[4][2] = 189.0 / 64.0; - gcons[4][3] = -45.0 / 32.0; - gcons[4][4] = 35.0 / 128.0; - gcons[5][0] = 693.0 / 256.0; - gcons[5][1] = -1155.0 / 256.0; - gcons[5][2] = 693.0 / 128.0; - gcons[5][3] = -495.0 / 128.0; - gcons[5][4] = 385.0 / 256.0; - gcons[5][5] = -63.0 / 256.0; - gcons[6][0] = 3003.0 / 1024.0; - gcons[6][1] = -3003.0 / 512.0; - gcons[6][2] = 9009.0 / 1024.0; - gcons[6][3] = -2145.0 / 256.0; - gcons[6][4] = 5005.0 / 1024.0; - gcons[6][5] = -819.0 / 512.0; - gcons[6][6] = 231.0 / 1024.0; - - memory->create(dgcons,7,6,"kspace:dgcons"); - dgcons[2][0] = -5.0 / 2.0; - dgcons[2][1] = 3.0 / 2.0; - dgcons[3][0] = -35.0 / 8.0; - dgcons[3][1] = 21.0 / 4.0; - dgcons[3][2] = -15.0 / 8.0; - dgcons[4][0] = -105.0 / 16.0; - dgcons[4][1] = 189.0 / 16.0; - dgcons[4][2] = -135.0 / 16.0; - dgcons[4][3] = 35.0 / 16.0; - dgcons[5][0] = -1155.0 / 128.0; - dgcons[5][1] = 693.0 / 32.0; - dgcons[5][2] = -1485.0 / 64.0; - dgcons[5][3] = 385.0 / 32.0; - dgcons[5][4] = -315.0 / 128.0; - dgcons[6][0] = -3003.0 / 256.0; - dgcons[6][1] = 9009.0 / 256.0; - dgcons[6][2] = -6435.0 / 128.0; - dgcons[6][3] = 5005.0 / 128.0; - dgcons[6][4] = -4095.0 / 256.0; - dgcons[6][5] = 693.0 / 256.0; -} - -/* ---------------------------------------------------------------------- */ - -KSpace::~KSpace() -{ - memory->destroy(eatom); - memory->destroy(vatom); - memory->destroy(gcons); - memory->destroy(dgcons); -} - -/* ---------------------------------------------------------------------- */ - -void KSpace::triclinic_check() -{ - if (domain->triclinic && triclinic_support != 1) - error->all(FLERR,"KSpace style does not yet support triclinic geometries"); -} - -/* ---------------------------------------------------------------------- */ - -void KSpace::compute_dummy(int eflag, int vflag) -{ - if (eflag || vflag) ev_setup(eflag,vflag); - else evflag = evflag_atom = eflag_global = vflag_global = - eflag_atom = vflag_atom = 0; -} - -/* ---------------------------------------------------------------------- - check that pair style is compatible with long-range solver -------------------------------------------------------------------------- */ - -void KSpace::pair_check() -{ - if (force->pair == NULL) - error->all(FLERR,"KSpace solver requires a pair style"); - if (ewaldflag && force->pair->ewaldflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (pppmflag && force->pair->pppmflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (msmflag && force->pair->msmflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (dispersionflag && force->pair->dispersionflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (tip4pflag && force->pair->tip4pflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (dipoleflag && force->pair->dipoleflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); -} - -/* ---------------------------------------------------------------------- - setup for energy, virial computation - see integrate::ev_set() for values of eflag (0-3) and vflag (0-6) -------------------------------------------------------------------------- */ - -void KSpace::ev_setup(int eflag, int vflag) -{ - int i,n; - - evflag = 1; - - eflag_either = eflag; - eflag_global = eflag % 2; - eflag_atom = eflag / 2; - - vflag_either = vflag; - vflag_global = vflag % 4; - vflag_atom = vflag / 4; - - if (eflag_atom || vflag_atom) evflag_atom = 1; - else evflag_atom = 0; - - // reallocate per-atom arrays if necessary - - if (eflag_atom && atom->nmax > maxeatom) { - maxeatom = atom->nmax; - memory->destroy(eatom); - memory->create(eatom,maxeatom,"kspace:eatom"); - } - if (vflag_atom && atom->nmax > maxvatom) { - maxvatom = atom->nmax; - memory->destroy(vatom); - memory->create(vatom,maxvatom,6,"kspace:vatom"); - } - - // zero accumulators - - if (eflag_global) energy = 0.0; - if (vflag_global) for (i = 0; i < 6; i++) virial[i] = 0.0; - if (eflag_atom) { - n = atom->nlocal; - if (tip4pflag) n += atom->nghost; - for (i = 0; i < n; i++) eatom[i] = 0.0; - } - if (vflag_atom) { - n = atom->nlocal; - if (tip4pflag) n += atom->nghost; - for (i = 0; i < n; i++) { - vatom[i][0] = 0.0; - vatom[i][1] = 0.0; - vatom[i][2] = 0.0; - vatom[i][3] = 0.0; - vatom[i][4] = 0.0; - vatom[i][5] = 0.0; - } - } -} - -/* ---------------------------------------------------------------------- - estimate the accuracy of the short-range coulomb tables -------------------------------------------------------------------------- */ - -double KSpace::estimate_table_accuracy(double q2_over_sqrt, double spr) -{ - double table_accuracy = 0.0; - int nctb = force->pair->ncoultablebits; - if (nctb) { - double empirical_precision[17]; - empirical_precision[6] = 6.99E-03; - empirical_precision[7] = 1.78E-03; - empirical_precision[8] = 4.72E-04; - empirical_precision[9] = 1.17E-04; - empirical_precision[10] = 2.95E-05; - empirical_precision[11] = 7.41E-06; - empirical_precision[12] = 1.76E-06; - empirical_precision[13] = 9.28E-07; - empirical_precision[14] = 7.46E-07; - empirical_precision[15] = 7.32E-07; - empirical_precision[16] = 7.30E-07; - if (nctb <= 6) table_accuracy = empirical_precision[6]; - else if (nctb <= 16) table_accuracy = empirical_precision[nctb]; - else table_accuracy = empirical_precision[16]; - table_accuracy *= q2_over_sqrt; - if ((table_accuracy > spr) && (comm->me == 0)) - error->warning(FLERR,"For better accuracy use 'pair_modify table 0'"); - } - - return table_accuracy; -} - -/* ---------------------------------------------------------------------- - convert box coords vector to transposed triclinic lamda (0-1) coords - vector, lamda = [(H^-1)^T] * v, does not preserve vector magnitude - v and lamda can point to same 3-vector -------------------------------------------------------------------------- */ - -void KSpace::x2lamdaT(double *v, double *lamda) -{ - double *h_inv = domain->h_inv; - double lamda_tmp[3]; - - lamda_tmp[0] = h_inv[0]*v[0]; - lamda_tmp[1] = h_inv[5]*v[0] + h_inv[1]*v[1]; - lamda_tmp[2] = h_inv[4]*v[0] + h_inv[3]*v[1] + h_inv[2]*v[2]; - - lamda[0] = lamda_tmp[0]; - lamda[1] = lamda_tmp[1]; - lamda[2] = lamda_tmp[2]; -} - -/* ---------------------------------------------------------------------- - convert lamda (0-1) coords vector to transposed box coords vector - lamda = (H^T) * v, does not preserve vector magnitude - v and lamda can point to same 3-vector -------------------------------------------------------------------------- */ - -void KSpace::lamda2xT(double *lamda, double *v) -{ - double *h = domain->h; - double v_tmp[3]; - - v_tmp[0] = h[0]*lamda[0]; - v_tmp[1] = h[5]*lamda[0] + h[1]*lamda[1]; - v_tmp[2] = h[4]*lamda[0] + h[3]*lamda[1] + h[2]*lamda[2]; - - v[0] = v_tmp[0]; - v[1] = v_tmp[1]; - v[2] = v_tmp[2]; -} - -/* ---------------------------------------------------------------------- - convert triclinic lamda (0-1) coords vector to box coords vector - v = H * lamda, does not preserve vector magnitude - lamda and v can point to same 3-vector -------------------------------------------------------------------------- */ - -void KSpace::lamda2xvector(double *lamda, double *v) -{ - double *h = domain->h; - - v[0] = h[0]*lamda[0] + h[5]*lamda[1] + h[4]*lamda[2]; - v[1] = h[1]*lamda[1] + h[3]*lamda[2]; - v[2] = h[2]*lamda[2]; -} - -/* ---------------------------------------------------------------------- - convert a sphere in box coords to an ellipsoid in lamda (0-1) - coords and return the tight (axis-aligned) bounding box, does not - preserve vector magnitude - see http://www.loria.fr/~shornus/ellipsoid-bbox.html and - http://yiningkarlli.blogspot.com/2013/02/bounding-boxes-for-ellipsoidsfigure.html -------------------------------------------------------------------------- */ - -void KSpace::kspacebbox(double r, double *b) -{ - double *h = domain->h; - double lx,ly,lz,xy,xz,yz; - lx = h[0]; - ly = h[1]; - lz = h[2]; - yz = h[3]; - xz = h[4]; - xy = h[5]; - - b[0] = r*sqrt(ly*ly*lz*lz + ly*ly*xz*xz - 2.0*ly*xy*xz*yz + lz*lz*xy*xy + - xy*xy*yz*yz)/(lx*ly*lz); - b[1] = r*sqrt(lz*lz + yz*yz)/(ly*lz); - b[2] = r/lz; -} - -/* ---------------------------------------------------------------------- - modify parameters of the KSpace style -------------------------------------------------------------------------- */ - -void KSpace::modify_params(int narg, char **arg) -{ - int iarg = 0; - while (iarg < narg) { - if (strcmp(arg[iarg],"mesh") == 0) { - if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); - nx_pppm = nx_msm_max = atoi(arg[iarg+1]); - ny_pppm = ny_msm_max = atoi(arg[iarg+2]); - nz_pppm = nz_msm_max = atoi(arg[iarg+3]); - if (nx_pppm == 0 && ny_pppm == 0 && nz_pppm == 0) gridflag = 0; - else gridflag = 1; - iarg += 4; - } else if (strcmp(arg[iarg],"mesh/disp") == 0) { - if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); - nx_pppm_6 = atoi(arg[iarg+1]); - ny_pppm_6 = atoi(arg[iarg+2]); - nz_pppm_6 = atoi(arg[iarg+3]); - if (nx_pppm_6 == 0 || ny_pppm_6 == 0 || nz_pppm_6 == 0) gridflag_6 = 0; - else gridflag_6 = 1; - iarg += 4; - } else if (strcmp(arg[iarg],"order") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - order = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"order/disp") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - order_6 = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"minorder") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - minorder = atoi(arg[iarg+1]); - if (minorder < 2) error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"overlap") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) overlap_allowed = 1; - else if (strcmp(arg[iarg+1],"no") == 0) overlap_allowed = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"force") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - accuracy_absolute = atof(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"gewald") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - g_ewald = atof(arg[iarg+1]); - if (g_ewald == 0.0) gewaldflag = 0; - else gewaldflag = 1; - iarg += 2; - } else if (strcmp(arg[iarg],"gewald/disp") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - g_ewald_6 = atof(arg[iarg+1]); - if (g_ewald_6 == 0.0) gewaldflag_6 = 0; - else gewaldflag_6 = 1; - iarg += 2; - } else if (strcmp(arg[iarg],"slab") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"nozforce") == 0) { - slabflag = 2; - } else { - slabflag = 1; - slab_volfactor = atof(arg[iarg+1]); - if (slab_volfactor <= 1.0) - error->all(FLERR,"Bad kspace_modify slab parameter"); - if (slab_volfactor < 2.0 && comm->me == 0) - error->warning(FLERR,"Kspace_modify slab param < 2.0 may " - "cause unphysical behavior"); - } - iarg += 2; - } else if (strcmp(arg[iarg],"compute") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) compute_flag = 1; - else if (strcmp(arg[iarg+1],"no") == 0) compute_flag = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"fftbench") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) fftbench = 1; - else if (strcmp(arg[iarg+1],"no") == 0) fftbench = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"diff") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"ad") == 0) differentiation_flag = 1; - else if (strcmp(arg[iarg+1],"ik") == 0) differentiation_flag = 0; - else error->all(FLERR, "Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"cutoff/adjust") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) adjust_cutoff_flag = 1; - else if (strcmp(arg[iarg+1],"no") == 0) adjust_cutoff_flag = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else error->all(FLERR,"Illegal kspace_modify command"); - } -} - -/* ---------------------------------------------------------------------- */ - -void *KSpace::extract(const char *str) -{ - if (strcmp(str,"scale") == 0) return (void *) &scale; - return NULL; -} +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 "stdlib.h" +#include "string.h" +#include "kspace.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "pair.h" +#include "memory.h" +#include "atom_masks.h" +#include "error.h" +#include "suffix.h" +#include "domain.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +KSpace::KSpace(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) +{ + energy = 0.0; + virial[0] = virial[1] = virial[2] = virial[3] = virial[4] = virial[5] = 0.0; + + triclinic_support = 1; + ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = dipoleflag = 0; + compute_flag = 1; + group_group_enable = 0; + stagger_flag = 0; + + order = 5; + gridflag = 0; + gewaldflag = 0; + minorder = 2; + overlap_allowed = 1; + fftbench = 1; + + order_6 = 5; + gridflag_6 = 0; + gewaldflag_6 = 0; + + slabflag = 0; + differentiation_flag = 0; + slab_volfactor = 1; + suffix_flag = Suffix::NONE; + adjust_cutoff_flag = 1; + + accuracy_absolute = -1.0; + two_charge_force = force->qqr2e * + (force->qelectron * force->qelectron) / + (force->angstrom * force->angstrom); + + maxeatom = maxvatom = 0; + eatom = NULL; + vatom = NULL; + + datamask = ALL_MASK; + datamask_ext = ALL_MASK; + + memory->create(gcons,7,7,"kspace:gcons"); + gcons[2][0] = 15.0 / 8.0; + gcons[2][1] = -5.0 / 4.0; + gcons[2][2] = 3.0 / 8.0; + gcons[3][0] = 35.0 / 16.0; + gcons[3][1] = -35.0 / 16.0; + gcons[3][2] = 21.0 / 16.0; + gcons[3][3] = -5.0 / 16.0; + gcons[4][0] = 315.0 / 128.0; + gcons[4][1] = -105.0 / 32.0; + gcons[4][2] = 189.0 / 64.0; + gcons[4][3] = -45.0 / 32.0; + gcons[4][4] = 35.0 / 128.0; + gcons[5][0] = 693.0 / 256.0; + gcons[5][1] = -1155.0 / 256.0; + gcons[5][2] = 693.0 / 128.0; + gcons[5][3] = -495.0 / 128.0; + gcons[5][4] = 385.0 / 256.0; + gcons[5][5] = -63.0 / 256.0; + gcons[6][0] = 3003.0 / 1024.0; + gcons[6][1] = -3003.0 / 512.0; + gcons[6][2] = 9009.0 / 1024.0; + gcons[6][3] = -2145.0 / 256.0; + gcons[6][4] = 5005.0 / 1024.0; + gcons[6][5] = -819.0 / 512.0; + gcons[6][6] = 231.0 / 1024.0; + + memory->create(dgcons,7,6,"kspace:dgcons"); + dgcons[2][0] = -5.0 / 2.0; + dgcons[2][1] = 3.0 / 2.0; + dgcons[3][0] = -35.0 / 8.0; + dgcons[3][1] = 21.0 / 4.0; + dgcons[3][2] = -15.0 / 8.0; + dgcons[4][0] = -105.0 / 16.0; + dgcons[4][1] = 189.0 / 16.0; + dgcons[4][2] = -135.0 / 16.0; + dgcons[4][3] = 35.0 / 16.0; + dgcons[5][0] = -1155.0 / 128.0; + dgcons[5][1] = 693.0 / 32.0; + dgcons[5][2] = -1485.0 / 64.0; + dgcons[5][3] = 385.0 / 32.0; + dgcons[5][4] = -315.0 / 128.0; + dgcons[6][0] = -3003.0 / 256.0; + dgcons[6][1] = 9009.0 / 256.0; + dgcons[6][2] = -6435.0 / 128.0; + dgcons[6][3] = 5005.0 / 128.0; + dgcons[6][4] = -4095.0 / 256.0; + dgcons[6][5] = 693.0 / 256.0; +} + +/* ---------------------------------------------------------------------- */ + +KSpace::~KSpace() +{ + memory->destroy(eatom); + memory->destroy(vatom); + memory->destroy(gcons); + memory->destroy(dgcons); +} + +/* ---------------------------------------------------------------------- */ + +void KSpace::triclinic_check() +{ + if (domain->triclinic && triclinic_support != 1) + error->all(FLERR,"KSpace style does not yet support triclinic geometries"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpace::compute_dummy(int eflag, int vflag) +{ + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = evflag_atom = eflag_global = vflag_global = + eflag_atom = vflag_atom = 0; +} + +/* ---------------------------------------------------------------------- + check that pair style is compatible with long-range solver +------------------------------------------------------------------------- */ + +void KSpace::pair_check() +{ + if (force->pair == NULL) + error->all(FLERR,"KSpace solver requires a pair style"); + if (ewaldflag && force->pair->ewaldflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (pppmflag && force->pair->pppmflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (msmflag && force->pair->msmflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (dispersionflag && force->pair->dispersionflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (tip4pflag && force->pair->tip4pflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (dipoleflag && force->pair->dipoleflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); +} + +/* ---------------------------------------------------------------------- + setup for energy, virial computation + see integrate::ev_set() for values of eflag (0-3) and vflag (0-6) +------------------------------------------------------------------------- */ + +void KSpace::ev_setup(int eflag, int vflag) +{ + int i,n; + + evflag = 1; + + eflag_either = eflag; + eflag_global = eflag % 2; + eflag_atom = eflag / 2; + + vflag_either = vflag; + vflag_global = vflag % 4; + vflag_atom = vflag / 4; + + if (eflag_atom || vflag_atom) evflag_atom = 1; + else evflag_atom = 0; + + // reallocate per-atom arrays if necessary + + if (eflag_atom && atom->nmax > maxeatom) { + maxeatom = atom->nmax; + memory->destroy(eatom); + memory->create(eatom,maxeatom,"kspace:eatom"); + } + if (vflag_atom && atom->nmax > maxvatom) { + maxvatom = atom->nmax; + memory->destroy(vatom); + memory->create(vatom,maxvatom,6,"kspace:vatom"); + } + + // zero accumulators + + if (eflag_global) energy = 0.0; + if (vflag_global) for (i = 0; i < 6; i++) virial[i] = 0.0; + if (eflag_atom) { + n = atom->nlocal; + if (tip4pflag) n += atom->nghost; + for (i = 0; i < n; i++) eatom[i] = 0.0; + } + if (vflag_atom) { + n = atom->nlocal; + if (tip4pflag) n += atom->nghost; + for (i = 0; i < n; i++) { + vatom[i][0] = 0.0; + vatom[i][1] = 0.0; + vatom[i][2] = 0.0; + vatom[i][3] = 0.0; + vatom[i][4] = 0.0; + vatom[i][5] = 0.0; + } + } +} + +/* ---------------------------------------------------------------------- + estimate the accuracy of the short-range coulomb tables +------------------------------------------------------------------------- */ + +double KSpace::estimate_table_accuracy(double q2_over_sqrt, double spr) +{ + double table_accuracy = 0.0; + int nctb = force->pair->ncoultablebits; + if (nctb) { + double empirical_precision[17]; + empirical_precision[6] = 6.99E-03; + empirical_precision[7] = 1.78E-03; + empirical_precision[8] = 4.72E-04; + empirical_precision[9] = 1.17E-04; + empirical_precision[10] = 2.95E-05; + empirical_precision[11] = 7.41E-06; + empirical_precision[12] = 1.76E-06; + empirical_precision[13] = 9.28E-07; + empirical_precision[14] = 7.46E-07; + empirical_precision[15] = 7.32E-07; + empirical_precision[16] = 7.30E-07; + if (nctb <= 6) table_accuracy = empirical_precision[6]; + else if (nctb <= 16) table_accuracy = empirical_precision[nctb]; + else table_accuracy = empirical_precision[16]; + table_accuracy *= q2_over_sqrt; + if ((table_accuracy > spr) && (comm->me == 0)) + error->warning(FLERR,"For better accuracy use 'pair_modify table 0'"); + } + + return table_accuracy; +} + +/* ---------------------------------------------------------------------- + convert box coords vector to transposed triclinic lamda (0-1) coords + vector, lamda = [(H^-1)^T] * v, does not preserve vector magnitude + v and lamda can point to same 3-vector +------------------------------------------------------------------------- */ + +void KSpace::x2lamdaT(double *v, double *lamda) +{ + double *h_inv = domain->h_inv; + double lamda_tmp[3]; + + lamda_tmp[0] = h_inv[0]*v[0]; + lamda_tmp[1] = h_inv[5]*v[0] + h_inv[1]*v[1]; + lamda_tmp[2] = h_inv[4]*v[0] + h_inv[3]*v[1] + h_inv[2]*v[2]; + + lamda[0] = lamda_tmp[0]; + lamda[1] = lamda_tmp[1]; + lamda[2] = lamda_tmp[2]; +} + +/* ---------------------------------------------------------------------- + convert lamda (0-1) coords vector to transposed box coords vector + lamda = (H^T) * v, does not preserve vector magnitude + v and lamda can point to same 3-vector +------------------------------------------------------------------------- */ + +void KSpace::lamda2xT(double *lamda, double *v) +{ + double *h = domain->h; + double v_tmp[3]; + + v_tmp[0] = h[0]*lamda[0]; + v_tmp[1] = h[5]*lamda[0] + h[1]*lamda[1]; + v_tmp[2] = h[4]*lamda[0] + h[3]*lamda[1] + h[2]*lamda[2]; + + v[0] = v_tmp[0]; + v[1] = v_tmp[1]; + v[2] = v_tmp[2]; +} + +/* ---------------------------------------------------------------------- + convert triclinic lamda (0-1) coords vector to box coords vector + v = H * lamda, does not preserve vector magnitude + lamda and v can point to same 3-vector +------------------------------------------------------------------------- */ + +void KSpace::lamda2xvector(double *lamda, double *v) +{ + double *h = domain->h; + + v[0] = h[0]*lamda[0] + h[5]*lamda[1] + h[4]*lamda[2]; + v[1] = h[1]*lamda[1] + h[3]*lamda[2]; + v[2] = h[2]*lamda[2]; +} + +/* ---------------------------------------------------------------------- + convert a sphere in box coords to an ellipsoid in lamda (0-1) + coords and return the tight (axis-aligned) bounding box, does not + preserve vector magnitude + see http://www.loria.fr/~shornus/ellipsoid-bbox.html and + http://yiningkarlli.blogspot.com/2013/02/bounding-boxes-for-ellipsoidsfigure.html +------------------------------------------------------------------------- */ + +void KSpace::kspacebbox(double r, double *b) +{ + double *h = domain->h; + double lx,ly,lz,xy,xz,yz; + lx = h[0]; + ly = h[1]; + lz = h[2]; + yz = h[3]; + xz = h[4]; + xy = h[5]; + + b[0] = r*sqrt(ly*ly*lz*lz + ly*ly*xz*xz - 2.0*ly*xy*xz*yz + lz*lz*xy*xy + + xy*xy*yz*yz)/(lx*ly*lz); + b[1] = r*sqrt(lz*lz + yz*yz)/(ly*lz); + b[2] = r/lz; +} + +/* ---------------------------------------------------------------------- + modify parameters of the KSpace style +------------------------------------------------------------------------- */ + +void KSpace::modify_params(int narg, char **arg) +{ + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"mesh") == 0) { + if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); + nx_pppm = nx_msm_max = atoi(arg[iarg+1]); + ny_pppm = ny_msm_max = atoi(arg[iarg+2]); + nz_pppm = nz_msm_max = atoi(arg[iarg+3]); + if (nx_pppm == 0 && ny_pppm == 0 && nz_pppm == 0) gridflag = 0; + else gridflag = 1; + iarg += 4; + } else if (strcmp(arg[iarg],"mesh/disp") == 0) { + if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); + nx_pppm_6 = atoi(arg[iarg+1]); + ny_pppm_6 = atoi(arg[iarg+2]); + nz_pppm_6 = atoi(arg[iarg+3]); + if (nx_pppm_6 == 0 || ny_pppm_6 == 0 || nz_pppm_6 == 0) gridflag_6 = 0; + else gridflag_6 = 1; + iarg += 4; + } else if (strcmp(arg[iarg],"order") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + order = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"order/disp") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + order_6 = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"minorder") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + minorder = atoi(arg[iarg+1]); + if (minorder < 2) error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"overlap") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) overlap_allowed = 1; + else if (strcmp(arg[iarg+1],"no") == 0) overlap_allowed = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"force") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + accuracy_absolute = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"gewald") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + g_ewald = atof(arg[iarg+1]); + if (g_ewald == 0.0) gewaldflag = 0; + else gewaldflag = 1; + iarg += 2; + } else if (strcmp(arg[iarg],"gewald/disp") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + g_ewald_6 = atof(arg[iarg+1]); + if (g_ewald_6 == 0.0) gewaldflag_6 = 0; + else gewaldflag_6 = 1; + iarg += 2; + } else if (strcmp(arg[iarg],"slab") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"nozforce") == 0) { + slabflag = 2; + } else { + slabflag = 1; + slab_volfactor = atof(arg[iarg+1]); + if (slab_volfactor <= 1.0) + error->all(FLERR,"Bad kspace_modify slab parameter"); + if (slab_volfactor < 2.0 && comm->me == 0) + error->warning(FLERR,"Kspace_modify slab param < 2.0 may " + "cause unphysical behavior"); + } + iarg += 2; + } else if (strcmp(arg[iarg],"compute") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) compute_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) compute_flag = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"fftbench") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) fftbench = 1; + else if (strcmp(arg[iarg+1],"no") == 0) fftbench = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"diff") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"ad") == 0) differentiation_flag = 1; + else if (strcmp(arg[iarg+1],"ik") == 0) differentiation_flag = 0; + else error->all(FLERR, "Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"cutoff/adjust") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) adjust_cutoff_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) adjust_cutoff_flag = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else error->all(FLERR,"Illegal kspace_modify command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void *KSpace::extract(const char *str) +{ + if (strcmp(str,"scale") == 0) return (void *) &scale; + return NULL; +} diff --git a/src/kspace.h b/src/kspace.h index 2f64e203df..f2c914ee95 100644 --- a/src/kspace.h +++ b/src/kspace.h @@ -1,198 +1,200 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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. -------------------------------------------------------------------------- */ - -#ifndef LMP_KSPACE_H -#define LMP_KSPACE_H - -#include "pointers.h" - -#ifdef FFT_SINGLE -typedef float FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_FLOAT -#else -typedef double FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_DOUBLE -#endif - -namespace LAMMPS_NS { - -class KSpace : protected Pointers { - friend class ThrOMP; - friend class FixOMP; - public: - double energy; // accumulated energies - double energy_1,energy_6; - double virial[6]; // accumlated virial - double *eatom,**vatom; // accumulated per-atom energy/virial - double e2group; // accumulated group-group energy - double f2group[3]; // accumulated group-group force - int triclinic_support; // 1 if supports triclinic geometries - - int ewaldflag; // 1 if a Ewald solver - int pppmflag; // 1 if a PPPM solver - int msmflag; // 1 if a MSM solver - int dispersionflag; // 1 if a LJ/dispersion solver - int tip4pflag; // 1 if a TIP4P solver - int dipoleflag; // 1 if a dipole solver - int differentiation_flag; - int slabflag; - double slab_volfactor; - - int order,order_6; - double accuracy; // accuracy of KSpace solver (force units) - double accuracy_absolute; // user-specifed accuracy in force units - double accuracy_relative; // user-specified dimensionless accuracy - // accurary = acc_rel * two_charge_force - double two_charge_force; // force in user units of two point - // charges separated by 1 Angstrom - - double g_ewald,g_ewald_6; - int nx_pppm,ny_pppm,nz_pppm; // global FFT grid for Coulombics - int nx_pppm_6,ny_pppm_6,nz_pppm_6; // global FFT grid for dispersion - int nx_msm_max,ny_msm_max,nz_msm_max; - - int group_group_enable; // 1 if style supports group/group calculation - - unsigned int datamask; - unsigned int datamask_ext; - - int compute_flag; // 0 if skip compute() - int fftbench; // 0 if skip FFT timing - - KSpace(class LAMMPS *, int, char **); - virtual ~KSpace(); - void triclinic_check(); - void modify_params(int, char **); - void *extract(const char *); - void compute_dummy(int, int); - - // triclinic - - void x2lamdaT(double *, double *); - void lamda2xT(double *, double *); - void lamda2xvector(double *, double *); - void kspacebbox(double, double *); - - // general child-class methods - - virtual void init() = 0; - virtual void setup() = 0; - virtual void setup_grid() {}; - virtual void compute(int, int) = 0; - virtual void compute_group_group(int, int, int) {}; - - virtual void pack_forward(int, FFT_SCALAR *, int, int *) {}; - virtual void unpack_forward(int, FFT_SCALAR *, int, int *) {}; - virtual void pack_reverse(int, FFT_SCALAR *, int, int *) {}; - virtual void unpack_reverse(int, FFT_SCALAR *, int, int *) {}; - - virtual int timing(int, double &, double &) {return 0;} - virtual int timing_1d(int, double &) {return 0;} - virtual int timing_3d(int, double &) {return 0;} - virtual double memory_usage() {return 0.0;} - -/* ---------------------------------------------------------------------- - compute gamma for MSM and pair styles - see Eq 4 from Parallel Computing 35 (2009) 164–177 -------------------------------------------------------------------------- */ - - double gamma(const double &rho) const { - if (rho <= 1.0) { - const int split_order = order/2; - const double rho2 = rho*rho; - double g = gcons[split_order][0]; - double rho_n = rho2; - for (int n=1; n<=split_order; n++) { - g += gcons[split_order][n]*rho_n; - rho_n *= rho2; - } - return g; - } else - return (1.0/rho); - } - -/* ---------------------------------------------------------------------- - compute the derivative of gamma for MSM and pair styles - see Eq 4 from Parallel Computing 35 (2009) 164-177 -------------------------------------------------------------------------- */ - - double dgamma(const double &rho) const { - if (rho <= 1.0) { - const int split_order = order/2; - const double rho2 = rho*rho; - double dg = dgcons[split_order][0]*rho; - double rho_n = rho*rho2; - for (int n=1; n= 2.0. - -W: Kspace_modify slab param < 2.0 may cause unphysical behavior - -The kspace_modify slab parameter should be larger to insure periodic -grids padded with empty space do not overlap. - -*/ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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. +------------------------------------------------------------------------- */ + +#ifndef LMP_KSPACE_H +#define LMP_KSPACE_H + +#include "pointers.h" + +#ifdef FFT_SINGLE +typedef float FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_FLOAT +#else +typedef double FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_DOUBLE +#endif + +namespace LAMMPS_NS { + +class KSpace : protected Pointers { + friend class ThrOMP; + friend class FixOMP; + public: + double energy; // accumulated energies + double energy_1,energy_6; + double virial[6]; // accumlated virial + double *eatom,**vatom; // accumulated per-atom energy/virial + double e2group; // accumulated group-group energy + double f2group[3]; // accumulated group-group force + int triclinic_support; // 1 if supports triclinic geometries + + int ewaldflag; // 1 if a Ewald solver + int pppmflag; // 1 if a PPPM solver + int msmflag; // 1 if a MSM solver + int dispersionflag; // 1 if a LJ/dispersion solver + int tip4pflag; // 1 if a TIP4P solver + int dipoleflag; // 1 if a dipole solver + int differentiation_flag; + int slabflag; + double slab_volfactor; + + int order,order_6; + double accuracy; // accuracy of KSpace solver (force units) + double accuracy_absolute; // user-specifed accuracy in force units + double accuracy_relative; // user-specified dimensionless accuracy + // accurary = acc_rel * two_charge_force + double two_charge_force; // force in user units of two point + // charges separated by 1 Angstrom + + double g_ewald,g_ewald_6; + int nx_pppm,ny_pppm,nz_pppm; // global FFT grid for Coulombics + int nx_pppm_6,ny_pppm_6,nz_pppm_6; // global FFT grid for dispersion + int nx_msm_max,ny_msm_max,nz_msm_max; + + int group_group_enable; // 1 if style supports group/group calculation + + unsigned int datamask; + unsigned int datamask_ext; + + int compute_flag; // 0 if skip compute() + int fftbench; // 0 if skip FFT timing + + int stagger_flag; // 1 if using staggered PPPM grids + + KSpace(class LAMMPS *, int, char **); + virtual ~KSpace(); + void triclinic_check(); + void modify_params(int, char **); + void *extract(const char *); + void compute_dummy(int, int); + + // triclinic + + void x2lamdaT(double *, double *); + void lamda2xT(double *, double *); + void lamda2xvector(double *, double *); + void kspacebbox(double, double *); + + // general child-class methods + + virtual void init() = 0; + virtual void setup() = 0; + virtual void setup_grid() {}; + virtual void compute(int, int) = 0; + virtual void compute_group_group(int, int, int) {}; + + virtual void pack_forward(int, FFT_SCALAR *, int, int *) {}; + virtual void unpack_forward(int, FFT_SCALAR *, int, int *) {}; + virtual void pack_reverse(int, FFT_SCALAR *, int, int *) {}; + virtual void unpack_reverse(int, FFT_SCALAR *, int, int *) {}; + + virtual int timing(int, double &, double &) {return 0;} + virtual int timing_1d(int, double &) {return 0;} + virtual int timing_3d(int, double &) {return 0;} + virtual double memory_usage() {return 0.0;} + +/* ---------------------------------------------------------------------- + compute gamma for MSM and pair styles + see Eq 4 from Parallel Computing 35 (2009) 164–177 +------------------------------------------------------------------------- */ + + double gamma(const double &rho) const { + if (rho <= 1.0) { + const int split_order = order/2; + const double rho2 = rho*rho; + double g = gcons[split_order][0]; + double rho_n = rho2; + for (int n=1; n<=split_order; n++) { + g += gcons[split_order][n]*rho_n; + rho_n *= rho2; + } + return g; + } else + return (1.0/rho); + } + +/* ---------------------------------------------------------------------- + compute the derivative of gamma for MSM and pair styles + see Eq 4 from Parallel Computing 35 (2009) 164-177 +------------------------------------------------------------------------- */ + + double dgamma(const double &rho) const { + if (rho <= 1.0) { + const int split_order = order/2; + const double rho2 = rho*rho; + double dg = dgcons[split_order][0]*rho; + double rho_n = rho*rho2; + for (int n=1; n= 2.0. + +W: Kspace_modify slab param < 2.0 may cause unphysical behavior + +The kspace_modify slab parameter should be larger to insure periodic +grids padded with empty space do not overlap. + +*/ From c0afb45368f7a6bedc0d293d0dad4e606837ddfe Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:50:40 +0000 Subject: [PATCH 29/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10131 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm_stagger.cpp | 996 ++++++++++++++++++++++++++++++++++++ src/KSPACE/pppm_stagger.h | 109 ++++ 2 files changed, 1105 insertions(+) create mode 100755 src/KSPACE/pppm_stagger.cpp create mode 100755 src/KSPACE/pppm_stagger.h diff --git a/src/KSPACE/pppm_stagger.cpp b/src/KSPACE/pppm_stagger.cpp new file mode 100755 index 0000000000..08bbd6e7dd --- /dev/null +++ b/src/KSPACE/pppm_stagger.cpp @@ -0,0 +1,996 @@ +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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: Stan Moore (Sandia) +------------------------------------------------------------------------- */ + +#include "lmptype.h" +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" +#include "pppm_stagger.h" +#include "atom.h" +#include "commgrid.h" +#include "force.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#include "math_const.h" +#include "math_special.h" + +using namespace LAMMPS_NS; +using namespace MathConst; +using namespace MathSpecial; + +#define OFFSET 16384 +#define EPS_HOC 1.0e-7 + +enum{REVERSE_RHO}; +enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; + +#ifdef FFT_SINGLE +#define ZEROF 0.0f +#define ONEF 1.0f +#else +#define ZEROF 0.0 +#define ONEF 1.0 +#endif + +/* ---------------------------------------------------------------------- */ + +PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : PPPM(lmp, narg, arg) +{ + if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm/stagger command"); + stagger_flag = 1; + group_group_enable = 0; + + memory->create(gf_b2,8,7,"pppm_stagger:gf_b2"); + gf_b2[1][0] = 1.0; + gf_b2[2][0] = 5.0 / 6.0; + gf_b2[2][1] = 1.0 / 6.0; + gf_b2[3][0] = 61.0 / 120.0; + gf_b2[3][1] = 29.0 / 60.0; + gf_b2[3][2] = 1.0 / 120.0; + gf_b2[4][0] = 277.0 / 1008.0; + gf_b2[4][1] = 1037.0 / 1680.0; + gf_b2[4][2] = 181.0 / 1680.00; + gf_b2[4][3] = 1.0 / 5040.0; + gf_b2[5][0] = 50521.0 / 362880.0; + gf_b2[5][1] = 7367.0 / 12960.0; + gf_b2[5][2] = 16861.0 / 60480.0; + gf_b2[5][3] = 1229.0 / 90720.0; + gf_b2[5][4] = 1.0 / 362880.0; + gf_b2[6][0] = 540553.0 / 7983360.0; + gf_b2[6][1] = 17460701.0 / 39916800.0; + gf_b2[6][2] = 8444893.0 / 19958400.0; + gf_b2[6][3] = 1409633.0 / 19958400.0; + gf_b2[6][4] = 44281.0 / 39916800.0; + gf_b2[6][5] = 1.0 / 39916800.0; + gf_b2[7][0] = 199360981.0 / 6227020800.0; + gf_b2[7][1] = 103867703.0 / 345945600.0; + gf_b2[7][2] = 66714163.0 / 138378240.0; + gf_b2[7][3] = 54085121.0 / 311351040.0; + gf_b2[7][4] = 1640063.0 / 138378240.0; + gf_b2[7][5] = 671.0 / 10483200.0; + gf_b2[7][6] = 1.0 / 6227020800.0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +PPPMStagger::~PPPMStagger() +{ + memory->destroy(gf_b2); +} + +/* ---------------------------------------------------------------------- + called once before run +------------------------------------------------------------------------- */ + +void PPPMStagger::init() +{ + // error check + + if (domain->triclinic) + error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger with triclinic systems"); + + PPPM::init(); +} + +/* ---------------------------------------------------------------------- + compute the PPPM long-range force, energy, virial +------------------------------------------------------------------------- */ + +void PPPMStagger::compute(int eflag, int vflag) +{ + int i,j; + + // set energy/virial flags + // invoke allocate_peratom() if needed for first time + + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = evflag_atom = eflag_global = vflag_global = + eflag_atom = vflag_atom = 0; + + if (evflag_atom && !peratom_allocate_flag) { + allocate_peratom(); + cg_peratom->ghost_notify(); + cg_peratom->setup(); + } + + // convert atoms from box to lamda coords + + if (triclinic == 0) boxlo = domain->boxlo; + else { + boxlo = domain->boxlo_lamda; + domain->x2lamda(atom->nlocal); + } + + // extend size of per-atom arrays if necessary + + if (atom->nlocal > nmax) { + memory->destroy(part2grid); + nmax = atom->nmax; + memory->create(part2grid,nmax,3,"pppm:part2grid"); + } + + nstagger = 2; + + stagger = 0.0; + for (int n=0; nreverse_comm(this,REVERSE_RHO); + brick2fft(); + + // compute potential gradient on my FFT grid and + // portion of e_long on this proc's FFT grid + // return gradients (electric fields) in 3d brick decomposition + // also performs per-atom calculations via poisson_peratom() + + poisson(); + + // all procs communicate E-field values + // to fill ghost cells surrounding their 3d bricks + + if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); + else cg->forward_comm(this,FORWARD_IK); + + // extra per-atom energy/virial communication + + if (evflag_atom) { + if (differentiation_flag == 1 && vflag_atom) + cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); + else if (differentiation_flag == 0) + cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); + } + + // calculate the force on my particles + + fieldforce(); + + // extra per-atom energy/virial communication + + if (evflag_atom) fieldforce_peratom(); + + stagger += 1.0/float(nstagger); + } + + // sum global energy across procs and add in volume-dependent term + + const double qscale = force->qqrd2e * scale; + + if (eflag_global) { + double energy_all; + MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); + energy = energy_all; + + energy *= 0.5*volume/float(nstagger); + energy -= g_ewald*qsqsum/MY_PIS + + MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qscale; + } + + // sum global virial across procs + + if (vflag_global) { + double virial_all[6]; + MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]/float(nstagger); + } + + // per-atom energy/virial + // energy includes self-energy correction + // notal accounts for TIP4P tallying eatom/vatom for ghost atoms + + if (evflag_atom) { + double *q = atom->q; + int nlocal = atom->nlocal; + int ntotal = nlocal; + if (tip4pflag) ntotal += atom->nghost; + + if (eflag_atom) { + for (i = 0; i < nlocal; i++) { + eatom[i] *= 0.5; + eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / + (g_ewald*g_ewald*volume); + eatom[i] *= qscale; + } + for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; + } + + if (vflag_atom) { + for (i = 0; i < ntotal; i++) + for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; + } + } + + // 2d slab correction + + if (slabflag == 1) slabcorr(); + + // convert atoms back from lamda to box coords + + if (triclinic) domain->lamda2x(atom->nlocal); +} + +/* ---------------------------------------------------------------------- + compute qopt +------------------------------------------------------------------------- */ + +double PPPMStagger::compute_qopt() +{ + if (differentiation_flag == 1) + return compute_qopt_ad(); + + double qopt = 0.0; + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,sum2,dot1,dot2; + double numerator,denominator; + double u1,u2,u3,sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = 2; + const int nby = 2; + const int nbz = 2; + + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + cny = cos(0.5*unitky*lper*yprd/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); + sum1 = 0.0; + sum2 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx + qy*qy + qz*qz; + u1 = sx*sy*sz; + u2 = wx*wy*wz; + u3 = numerator*u1*u2*dot1; + sum1 += u1*u1*MY_4PI*MY_4PI/dot2; + sum2 += u3*u3/dot2; + } + } + } + qopt += sum1 - sum2/denominator; + } + } + } + } + double qopt_all; + MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); + return qopt_all; +} + +/* ---------------------------------------------------------------------- + compute qopt_ad +------------------------------------------------------------------------- */ + +double PPPMStagger::compute_qopt_ad() +{ + double qopt = 0.0; + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,sum2,sum3,sum4,sum5,sum6,dot2; + double u1,u2,sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = 2; + const int nby = 2; + const int nbz = 2; + + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + cny = cos(0.5*unitky*lper*yprd/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + sum1 = 0.0; + sum2 = 0.0; + sum3 = 0.0; + sum4 = 0.0; + sum5 = 0.0; + sum6 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot2 = qx*qx + qy*qy + qz*qz; + u1 = sx*sy*sz; + u2 = wx*wy*wz; + sum1 += u1*u1/dot2*MY_4PI*MY_4PI; + sum2 += u1*u1*u2*u2*MY_4PI*MY_4PI; + sum3 += u2; + sum4 += dot2*u2; + sum5 += u2*powint(-1.0,nx+ny+nz); + sum6 += dot2*u2*powint(-1.0,nx+ny+nz); + } + } + } + qopt += sum1 - sum2/(0.5*(sum3*sum4 + sum5*sum6)); + } + } + } + } + double qopt_all; + MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); + return qopt_all; +} + +/* ---------------------------------------------------------------------- + pre-compute Green's function denominator expansion coeffs, Gamma(2n) +------------------------------------------------------------------------- */ + +void PPPMStagger::compute_gf_denom() +{ + if (gf_b) memory->destroy(gf_b); + memory->create(gf_b,order,"pppm:gf_b"); + + int k,l,m; + + for (l = 1; l < order; l++) gf_b[l] = 0.0; + gf_b[0] = 1.0; + + for (m = 1; m < order; m++) { + for (l = m; l > 0; l--) + gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); + gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); + } + + bigint ifact = 1; + for (k = 1; k < 2*order; k++) ifact *= k; + double gaminv = 1.0/ifact; + for (l = 0; l < order; l++) gf_b[l] *= gaminv; +} + +/* ---------------------------------------------------------------------- + pre-compute modified (Hockney-Eastwood) Coulomb Green's function +------------------------------------------------------------------------- */ + +void PPPMStagger::compute_gf_ik() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + double sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + cny = cos(0.5*unitky*lper*yprd/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); + sum1 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + compute optimized Green's function for energy calculation +------------------------------------------------------------------------- */ + +void PPPMStagger::compute_gf_ad() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz,sqk; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double numerator,denominator; + int k,l,m,n,kper,lper,mper; + + const int twoorder = 2*order; + + for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + qz = unitkz*mper; + snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); + cnz = cos(0.5*qz*zprd_slab/nz_pppm); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + qy = unitky*lper; + sny = square(sin(0.5*qy*yprd/ny_pppm)); + cny = cos(0.5*qy*yprd/ny_pppm); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + qx = unitkx*kper; + snx = square(sin(0.5*qx*xprd/nx_pppm)); + cnx = cos(0.5*qx*xprd/nx_pppm); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + sqk = qx*qx + qy*qy + qz*qz; + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); + greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } else { + greensfn[n] = 0.0; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } + } + } + } + + // compute the coefficients for the self-force correction + + double prex, prey, prez; + prex = prey = prez = MY_PI/volume; + prex *= nx_pppm/xprd; + prey *= ny_pppm/yprd; + prez *= nz_pppm/zprd_slab; + sf_coeff[0] *= prex; + sf_coeff[1] *= prex*2; + sf_coeff[2] *= prey; + sf_coeff[3] *= prey*2; + sf_coeff[4] *= prez; + sf_coeff[5] *= prez*2; + + // communicate values with other procs + + double tmp[6]; + MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); + for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; +} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPMStagger::particle_map() +{ + int nx,ny,nz; + + double **x = atom->x; + int nlocal = atom->nlocal; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift + stagger) - OFFSET; + ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift + stagger) - OFFSET; + nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift + stagger) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) + flag = 1; + } + + if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPMStagger::make_rho() +{ + int l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + + // clear 3d density array + + memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ik +------------------------------------------------------------------------- */ + +void PPPMStagger::fieldforce_ik() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR ekx,eky,ekz; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ekx -= x0*vdx_brick[mz][my][mx]; + eky -= x0*vdy_brick[mz][my][mx]; + ekz -= x0*vdz_brick[mz][my][mx]; + } + } + } + + // convert E-field to force + + const double qfactor = force->qqrd2e * scale * q[i] / float(nstagger); + f[i][0] += qfactor*ekx; + f[i][1] += qfactor*eky; + if (slabflag != 2) f[i][2] += qfactor*ekz; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ad +------------------------------------------------------------------------- */ + +void PPPMStagger::fieldforce_ad() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz; + FFT_SCALAR ekx,eky,ekz; + double s1,s2,s3; + double sf = 0.0; + double *prd; + + prd = domain->prd; + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + + double hx_inv = nx_pppm/xprd; + double hy_inv = ny_pppm/yprd; + double hz_inv = nz_pppm/zprd; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + compute_drho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; + } + } + } + ekx *= hx_inv; + eky *= hy_inv; + ekz *= hz_inv; + + // convert E-field to force and substract self forces + + const double qfactor = force->qqrd2e * scale / float(nstagger); + + s1 = x[i][0]*hx_inv + stagger; + s2 = x[i][1]*hy_inv + stagger; + s3 = x[i][2]*hz_inv + stagger; + sf = sf_coeff[0]*sin(2*MY_PI*s1); + sf += sf_coeff[1]*sin(4*MY_PI*s1); + sf *= 2*q[i]*q[i]; + f[i][0] += qfactor*(ekx*q[i] - sf); + + sf = sf_coeff[2]*sin(2*MY_PI*s2); + sf += sf_coeff[3]*sin(4*MY_PI*s2); + sf *= 2*q[i]*q[i]; + f[i][1] += qfactor*(eky*q[i] - sf); + + + sf = sf_coeff[4]*sin(2*MY_PI*s3); + sf += sf_coeff[5]*sin(4*MY_PI*s3); + sf *= 2*q[i]*q[i]; + if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get per-atom energy/virial +------------------------------------------------------------------------- */ + +void PPPMStagger::fieldforce_peratom() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR u,v0,v1,v2,v3,v4,v5; + + // loop over my charges, interpolate from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + + u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + if (eflag_atom) u += x0*u_brick[mz][my][mx]; + if (vflag_atom) { + v0 += x0*v0_brick[mz][my][mx]; + v1 += x0*v1_brick[mz][my][mx]; + v2 += x0*v2_brick[mz][my][mx]; + v3 += x0*v3_brick[mz][my][mx]; + v4 += x0*v4_brick[mz][my][mx]; + v5 += x0*v5_brick[mz][my][mx]; + } + } + } + } + + if (eflag_atom) eatom[i] += q[i]*u/float(nstagger); + if (vflag_atom) { + vatom[i][0] += q[i]*v0/float(nstagger); + vatom[i][1] += q[i]*v1/float(nstagger); + vatom[i][2] += q[i]*v2/float(nstagger); + vatom[i][3] += q[i]*v3/float(nstagger); + vatom[i][4] += q[i]*v4/float(nstagger); + vatom[i][5] += q[i]*v5/float(nstagger); + } + } +} + +/* ---------------------------------------------------------------------- + perform and time the 1d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPMStagger::timing_1d(int n, double &time1d) +{ + PPPM::timing_1d(n,time1d); + time1d *= 2.0; + + if (differentiation_flag) return 2; + return 4; +} + +/* ---------------------------------------------------------------------- + perform and time the 3d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPMStagger::timing_3d(int n, double &time3d) +{ + PPPM::timing_3d(n,time3d); + time3d *= 2.0; + + if (differentiation_flag) return 2; + return 4; +} diff --git a/src/KSPACE/pppm_stagger.h b/src/KSPACE/pppm_stagger.h new file mode 100755 index 0000000000..cb80cf4766 --- /dev/null +++ b/src/KSPACE/pppm_stagger.h @@ -0,0 +1,109 @@ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 KSPACE_CLASS + +KSpaceStyle(pppm/stagger,PPPMStagger) + +#else + +#ifndef LMP_PPPM_STAGGER_H +#define LMP_PPPM_STAGGER_H + +#include "pppm.h" + +namespace LAMMPS_NS { + +class PPPMStagger : public PPPM { + public: + PPPMStagger(class LAMMPS *, int, char **); + virtual ~PPPMStagger(); + virtual void init(); + virtual void compute(int, int); + virtual int timing_1d(int, double &); + virtual int timing_3d(int, double &); + + protected: + int nstagger; + double stagger; + double **gf_b2; + + virtual double compute_qopt(); + double compute_qopt_ad(); + virtual void compute_gf_denom(); + virtual void compute_gf_ik(); + virtual void compute_gf_ad(); + + virtual void particle_map(); + virtual void make_rho(); + virtual void fieldforce_ik(); + virtual void fieldforce_ad(); + virtual void fieldforce_peratom(); + + + inline double gf_denom2(const double &x, const double &y, + const double &z) const { + double sx,sy,sz; + double x2 = x*x; + double y2 = y*y; + double z2 = z*z; + double xl = x; + double yl = y; + double zl = z; + for (int l = 0; l < order; l++) { + sx += gf_b2[order][l]*xl; + sy += gf_b2[order][l]*yl; + sz += gf_b2[order][l]*zl; + xl *= x2; + yl *= y2; + zl *= z2; + } + double s = sx*sy*sz; + return s*s; + }; +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Cannot (yet) use kspace_style pppm/stagger with triclinic systems + +This feature is not yet supported. + +E: Out of range atoms - cannot compute PPPM + +One or more atoms are attempting to map their charge to a PPPM grid +point that is not owned by a processor. This is likely for one of two +reasons, both of them bad. First, it may mean that an atom near the +boundary of a processor's sub-domain has moved more than 1/2 the +"neighbor skin distance"_neighbor.html without neighbor lists being +rebuilt and atoms being migrated to new processors. This also means +you may be missing pairwise interactions that need to be computed. +The solution is to change the re-neighboring criteria via the +"neigh_modify"_neigh_modify command. The safest settings are "delay 0 +every 1 check yes". Second, it may mean that an atom has moved far +outside a processor's sub-domain or even the entire simulation box. +This indicates bad physics, e.g. due to highly overlapping atoms, too +large a timestep, etc. + +*/ From cb90b71a13ead0256d8932596903d521502ad798 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:51:34 +0000 Subject: [PATCH 30/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10132 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm_stagger.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/KSPACE/pppm_stagger.cpp b/src/KSPACE/pppm_stagger.cpp index 08bbd6e7dd..1142102252 100755 --- a/src/KSPACE/pppm_stagger.cpp +++ b/src/KSPACE/pppm_stagger.cpp @@ -52,7 +52,8 @@ enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; /* ---------------------------------------------------------------------- */ -PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : PPPM(lmp, narg, arg) +PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : + PPPM(lmp, narg, arg) { if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm/stagger command"); stagger_flag = 1; @@ -107,7 +108,8 @@ void PPPMStagger::init() // error check if (domain->triclinic) - error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger with triclinic systems"); + error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger " + "with triclinic systems"); PPPM::init(); } From 45f0fb457d366e575c3a055dd38ba5b3eb1333ae Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:51:44 +0000 Subject: [PATCH 31/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10133 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm_stagger.cpp | 1996 +++++++++++++++++------------------ src/KSPACE/pppm_stagger.h | 218 ++-- 2 files changed, 1107 insertions(+), 1107 deletions(-) diff --git a/src/KSPACE/pppm_stagger.cpp b/src/KSPACE/pppm_stagger.cpp index 1142102252..996aa8e27d 100755 --- a/src/KSPACE/pppm_stagger.cpp +++ b/src/KSPACE/pppm_stagger.cpp @@ -1,998 +1,998 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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: Stan Moore (Sandia) -------------------------------------------------------------------------- */ - -#include "lmptype.h" -#include "mpi.h" -#include "string.h" -#include "stdio.h" -#include "stdlib.h" -#include "math.h" -#include "pppm_stagger.h" -#include "atom.h" -#include "commgrid.h" -#include "force.h" -#include "domain.h" -#include "memory.h" -#include "error.h" - -#include "math_const.h" -#include "math_special.h" - -using namespace LAMMPS_NS; -using namespace MathConst; -using namespace MathSpecial; - -#define OFFSET 16384 -#define EPS_HOC 1.0e-7 - -enum{REVERSE_RHO}; -enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; - -#ifdef FFT_SINGLE -#define ZEROF 0.0f -#define ONEF 1.0f -#else -#define ZEROF 0.0 -#define ONEF 1.0 -#endif - -/* ---------------------------------------------------------------------- */ - -PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : - PPPM(lmp, narg, arg) -{ - if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm/stagger command"); - stagger_flag = 1; - group_group_enable = 0; - - memory->create(gf_b2,8,7,"pppm_stagger:gf_b2"); - gf_b2[1][0] = 1.0; - gf_b2[2][0] = 5.0 / 6.0; - gf_b2[2][1] = 1.0 / 6.0; - gf_b2[3][0] = 61.0 / 120.0; - gf_b2[3][1] = 29.0 / 60.0; - gf_b2[3][2] = 1.0 / 120.0; - gf_b2[4][0] = 277.0 / 1008.0; - gf_b2[4][1] = 1037.0 / 1680.0; - gf_b2[4][2] = 181.0 / 1680.00; - gf_b2[4][3] = 1.0 / 5040.0; - gf_b2[5][0] = 50521.0 / 362880.0; - gf_b2[5][1] = 7367.0 / 12960.0; - gf_b2[5][2] = 16861.0 / 60480.0; - gf_b2[5][3] = 1229.0 / 90720.0; - gf_b2[5][4] = 1.0 / 362880.0; - gf_b2[6][0] = 540553.0 / 7983360.0; - gf_b2[6][1] = 17460701.0 / 39916800.0; - gf_b2[6][2] = 8444893.0 / 19958400.0; - gf_b2[6][3] = 1409633.0 / 19958400.0; - gf_b2[6][4] = 44281.0 / 39916800.0; - gf_b2[6][5] = 1.0 / 39916800.0; - gf_b2[7][0] = 199360981.0 / 6227020800.0; - gf_b2[7][1] = 103867703.0 / 345945600.0; - gf_b2[7][2] = 66714163.0 / 138378240.0; - gf_b2[7][3] = 54085121.0 / 311351040.0; - gf_b2[7][4] = 1640063.0 / 138378240.0; - gf_b2[7][5] = 671.0 / 10483200.0; - gf_b2[7][6] = 1.0 / 6227020800.0; -} - -/* ---------------------------------------------------------------------- - free all memory -------------------------------------------------------------------------- */ - -PPPMStagger::~PPPMStagger() -{ - memory->destroy(gf_b2); -} - -/* ---------------------------------------------------------------------- - called once before run -------------------------------------------------------------------------- */ - -void PPPMStagger::init() -{ - // error check - - if (domain->triclinic) - error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger " - "with triclinic systems"); - - PPPM::init(); -} - -/* ---------------------------------------------------------------------- - compute the PPPM long-range force, energy, virial -------------------------------------------------------------------------- */ - -void PPPMStagger::compute(int eflag, int vflag) -{ - int i,j; - - // set energy/virial flags - // invoke allocate_peratom() if needed for first time - - if (eflag || vflag) ev_setup(eflag,vflag); - else evflag = evflag_atom = eflag_global = vflag_global = - eflag_atom = vflag_atom = 0; - - if (evflag_atom && !peratom_allocate_flag) { - allocate_peratom(); - cg_peratom->ghost_notify(); - cg_peratom->setup(); - } - - // convert atoms from box to lamda coords - - if (triclinic == 0) boxlo = domain->boxlo; - else { - boxlo = domain->boxlo_lamda; - domain->x2lamda(atom->nlocal); - } - - // extend size of per-atom arrays if necessary - - if (atom->nlocal > nmax) { - memory->destroy(part2grid); - nmax = atom->nmax; - memory->create(part2grid,nmax,3,"pppm:part2grid"); - } - - nstagger = 2; - - stagger = 0.0; - for (int n=0; nreverse_comm(this,REVERSE_RHO); - brick2fft(); - - // compute potential gradient on my FFT grid and - // portion of e_long on this proc's FFT grid - // return gradients (electric fields) in 3d brick decomposition - // also performs per-atom calculations via poisson_peratom() - - poisson(); - - // all procs communicate E-field values - // to fill ghost cells surrounding their 3d bricks - - if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); - else cg->forward_comm(this,FORWARD_IK); - - // extra per-atom energy/virial communication - - if (evflag_atom) { - if (differentiation_flag == 1 && vflag_atom) - cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); - else if (differentiation_flag == 0) - cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); - } - - // calculate the force on my particles - - fieldforce(); - - // extra per-atom energy/virial communication - - if (evflag_atom) fieldforce_peratom(); - - stagger += 1.0/float(nstagger); - } - - // sum global energy across procs and add in volume-dependent term - - const double qscale = force->qqrd2e * scale; - - if (eflag_global) { - double energy_all; - MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); - energy = energy_all; - - energy *= 0.5*volume/float(nstagger); - energy -= g_ewald*qsqsum/MY_PIS + - MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); - energy *= qscale; - } - - // sum global virial across procs - - if (vflag_global) { - double virial_all[6]; - MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); - for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]/float(nstagger); - } - - // per-atom energy/virial - // energy includes self-energy correction - // notal accounts for TIP4P tallying eatom/vatom for ghost atoms - - if (evflag_atom) { - double *q = atom->q; - int nlocal = atom->nlocal; - int ntotal = nlocal; - if (tip4pflag) ntotal += atom->nghost; - - if (eflag_atom) { - for (i = 0; i < nlocal; i++) { - eatom[i] *= 0.5; - eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / - (g_ewald*g_ewald*volume); - eatom[i] *= qscale; - } - for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; - } - - if (vflag_atom) { - for (i = 0; i < ntotal; i++) - for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; - } - } - - // 2d slab correction - - if (slabflag == 1) slabcorr(); - - // convert atoms back from lamda to box coords - - if (triclinic) domain->lamda2x(atom->nlocal); -} - -/* ---------------------------------------------------------------------- - compute qopt -------------------------------------------------------------------------- */ - -double PPPMStagger::compute_qopt() -{ - if (differentiation_flag == 1) - return compute_qopt_ad(); - - double qopt = 0.0; - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz; - double cnx,cny,cnz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,sum2,dot1,dot2; - double numerator,denominator; - double u1,u2,u3,sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - const int nbx = 2; - const int nby = 2; - const int nbz = 2; - - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); - cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); - cny = cos(0.5*unitky*lper*yprd/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); - cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - numerator = MY_4PI/sqk; - denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); - sum1 = 0.0; - sum2 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; - dot2 = qx*qx + qy*qy + qz*qz; - u1 = sx*sy*sz; - u2 = wx*wy*wz; - u3 = numerator*u1*u2*dot1; - sum1 += u1*u1*MY_4PI*MY_4PI/dot2; - sum2 += u3*u3/dot2; - } - } - } - qopt += sum1 - sum2/denominator; - } - } - } - } - double qopt_all; - MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); - return qopt_all; -} - -/* ---------------------------------------------------------------------- - compute qopt_ad -------------------------------------------------------------------------- */ - -double PPPMStagger::compute_qopt_ad() -{ - double qopt = 0.0; - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz; - double cnx,cny,cnz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,sum2,sum3,sum4,sum5,sum6,dot2; - double u1,u2,sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - const int nbx = 2; - const int nby = 2; - const int nbz = 2; - - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); - cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); - cny = cos(0.5*unitky*lper*yprd/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); - cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - sum1 = 0.0; - sum2 = 0.0; - sum3 = 0.0; - sum4 = 0.0; - sum5 = 0.0; - sum6 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - dot2 = qx*qx + qy*qy + qz*qz; - u1 = sx*sy*sz; - u2 = wx*wy*wz; - sum1 += u1*u1/dot2*MY_4PI*MY_4PI; - sum2 += u1*u1*u2*u2*MY_4PI*MY_4PI; - sum3 += u2; - sum4 += dot2*u2; - sum5 += u2*powint(-1.0,nx+ny+nz); - sum6 += dot2*u2*powint(-1.0,nx+ny+nz); - } - } - } - qopt += sum1 - sum2/(0.5*(sum3*sum4 + sum5*sum6)); - } - } - } - } - double qopt_all; - MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); - return qopt_all; -} - -/* ---------------------------------------------------------------------- - pre-compute Green's function denominator expansion coeffs, Gamma(2n) -------------------------------------------------------------------------- */ - -void PPPMStagger::compute_gf_denom() -{ - if (gf_b) memory->destroy(gf_b); - memory->create(gf_b,order,"pppm:gf_b"); - - int k,l,m; - - for (l = 1; l < order; l++) gf_b[l] = 0.0; - gf_b[0] = 1.0; - - for (m = 1; m < order; m++) { - for (l = m; l > 0; l--) - gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); - gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); - } - - bigint ifact = 1; - for (k = 1; k < 2*order; k++) ifact *= k; - double gaminv = 1.0/ifact; - for (l = 0; l < order; l++) gf_b[l] *= gaminv; -} - -/* ---------------------------------------------------------------------- - pre-compute modified (Hockney-Eastwood) Coulomb Green's function -------------------------------------------------------------------------- */ - -void PPPMStagger::compute_gf_ik() -{ - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz; - double cnx,cny,cnz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,dot1,dot2; - double numerator,denominator; - double sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); - cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); - cny = cos(0.5*unitky*lper*yprd/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); - cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - numerator = MY_4PI/sqk; - denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); - sum1 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; - dot2 = qx*qx+qy*qy+qz*qz; - sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; - } - } - } - greensfn[n++] = numerator*sum1/denominator; - } else greensfn[n++] = 0.0; - } - } - } -} - -/* ---------------------------------------------------------------------- - compute optimized Green's function for energy calculation -------------------------------------------------------------------------- */ - -void PPPMStagger::compute_gf_ad() -{ - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz,sqk; - double cnx,cny,cnz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double numerator,denominator; - int k,l,m,n,kper,lper,mper; - - const int twoorder = 2*order; - - for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - qz = unitkz*mper; - snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); - cnz = cos(0.5*qz*zprd_slab/nz_pppm); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - qy = unitky*lper; - sny = square(sin(0.5*qy*yprd/ny_pppm)); - cny = cos(0.5*qy*yprd/ny_pppm); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - qx = unitkx*kper; - snx = square(sin(0.5*qx*xprd/nx_pppm)); - cnx = cos(0.5*qx*xprd/nx_pppm); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - sqk = qx*qx + qy*qy + qz*qz; - - if (sqk != 0.0) { - numerator = MY_4PI/sqk; - denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); - greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; - sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; - sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; - sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; - sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; - sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; - sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; - n++; - } else { - greensfn[n] = 0.0; - sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; - sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; - sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; - sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; - sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; - sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; - n++; - } - } - } - } - - // compute the coefficients for the self-force correction - - double prex, prey, prez; - prex = prey = prez = MY_PI/volume; - prex *= nx_pppm/xprd; - prey *= ny_pppm/yprd; - prez *= nz_pppm/zprd_slab; - sf_coeff[0] *= prex; - sf_coeff[1] *= prex*2; - sf_coeff[2] *= prey; - sf_coeff[3] *= prey*2; - sf_coeff[4] *= prez; - sf_coeff[5] *= prez*2; - - // communicate values with other procs - - double tmp[6]; - MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); - for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; -} - -/* ---------------------------------------------------------------------- - find center grid pt for each of my particles - check that full stencil for the particle will fit in my 3d brick - store central grid pt indices in part2grid array -------------------------------------------------------------------------- */ - -void PPPMStagger::particle_map() -{ - int nx,ny,nz; - - double **x = atom->x; - int nlocal = atom->nlocal; - - int flag = 0; - for (int i = 0; i < nlocal; i++) { - - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // current particle coord can be outside global and local box - // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 - - nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift + stagger) - OFFSET; - ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift + stagger) - OFFSET; - nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift + stagger) - OFFSET; - - part2grid[i][0] = nx; - part2grid[i][1] = ny; - part2grid[i][2] = nz; - - // check that entire stencil around nx,ny,nz will fit in my 3d brick - - if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || - ny+nlower < nylo_out || ny+nupper > nyhi_out || - nz+nlower < nzlo_out || nz+nupper > nzhi_out) - flag = 1; - } - - if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); -} - -/* ---------------------------------------------------------------------- - create discretized "density" on section of global grid due to my particles - density(x,y,z) = charge "density" at grid points of my 3d brick - (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) - in global grid -------------------------------------------------------------------------- */ - -void PPPMStagger::make_rho() -{ - int l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - - // clear 3d density array - - memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - // loop over my charges, add their contribution to nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - - for (int i = 0; i < nlocal; i++) { - - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; - - compute_rho1d(dx,dy,dz); - - z0 = delvolinv * q[i]; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - y0 = z0*rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - x0 = y0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - density_brick[mz][my][mx] += x0*rho1d[0][l]; - } - } - } - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles for ik -------------------------------------------------------------------------- */ - -void PPPMStagger::fieldforce_ik() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - FFT_SCALAR ekx,eky,ekz; - - // loop over my charges, interpolate electric field from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - // ek = 3 components of E-field on particle - - double *q = atom->q; - double **x = atom->x; - double **f = atom->f; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; - - compute_rho1d(dx,dy,dz); - - ekx = eky = ekz = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - z0 = rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - y0 = z0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - x0 = y0*rho1d[0][l]; - ekx -= x0*vdx_brick[mz][my][mx]; - eky -= x0*vdy_brick[mz][my][mx]; - ekz -= x0*vdz_brick[mz][my][mx]; - } - } - } - - // convert E-field to force - - const double qfactor = force->qqrd2e * scale * q[i] / float(nstagger); - f[i][0] += qfactor*ekx; - f[i][1] += qfactor*eky; - if (slabflag != 2) f[i][2] += qfactor*ekz; - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles for ad -------------------------------------------------------------------------- */ - -void PPPMStagger::fieldforce_ad() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz; - FFT_SCALAR ekx,eky,ekz; - double s1,s2,s3; - double sf = 0.0; - double *prd; - - prd = domain->prd; - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - - double hx_inv = nx_pppm/xprd; - double hy_inv = ny_pppm/yprd; - double hz_inv = nz_pppm/zprd; - - // loop over my charges, interpolate electric field from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - // ek = 3 components of E-field on particle - - double *q = atom->q; - double **x = atom->x; - double **f = atom->f; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; - - compute_rho1d(dx,dy,dz); - compute_drho1d(dx,dy,dz); - - ekx = eky = ekz = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; - eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; - ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; - } - } - } - ekx *= hx_inv; - eky *= hy_inv; - ekz *= hz_inv; - - // convert E-field to force and substract self forces - - const double qfactor = force->qqrd2e * scale / float(nstagger); - - s1 = x[i][0]*hx_inv + stagger; - s2 = x[i][1]*hy_inv + stagger; - s3 = x[i][2]*hz_inv + stagger; - sf = sf_coeff[0]*sin(2*MY_PI*s1); - sf += sf_coeff[1]*sin(4*MY_PI*s1); - sf *= 2*q[i]*q[i]; - f[i][0] += qfactor*(ekx*q[i] - sf); - - sf = sf_coeff[2]*sin(2*MY_PI*s2); - sf += sf_coeff[3]*sin(4*MY_PI*s2); - sf *= 2*q[i]*q[i]; - f[i][1] += qfactor*(eky*q[i] - sf); - - - sf = sf_coeff[4]*sin(2*MY_PI*s3); - sf += sf_coeff[5]*sin(4*MY_PI*s3); - sf *= 2*q[i]*q[i]; - if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get per-atom energy/virial -------------------------------------------------------------------------- */ - -void PPPMStagger::fieldforce_peratom() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - FFT_SCALAR u,v0,v1,v2,v3,v4,v5; - - // loop over my charges, interpolate from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; - - compute_rho1d(dx,dy,dz); - - u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - z0 = rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - y0 = z0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - x0 = y0*rho1d[0][l]; - if (eflag_atom) u += x0*u_brick[mz][my][mx]; - if (vflag_atom) { - v0 += x0*v0_brick[mz][my][mx]; - v1 += x0*v1_brick[mz][my][mx]; - v2 += x0*v2_brick[mz][my][mx]; - v3 += x0*v3_brick[mz][my][mx]; - v4 += x0*v4_brick[mz][my][mx]; - v5 += x0*v5_brick[mz][my][mx]; - } - } - } - } - - if (eflag_atom) eatom[i] += q[i]*u/float(nstagger); - if (vflag_atom) { - vatom[i][0] += q[i]*v0/float(nstagger); - vatom[i][1] += q[i]*v1/float(nstagger); - vatom[i][2] += q[i]*v2/float(nstagger); - vatom[i][3] += q[i]*v3/float(nstagger); - vatom[i][4] += q[i]*v4/float(nstagger); - vatom[i][5] += q[i]*v5/float(nstagger); - } - } -} - -/* ---------------------------------------------------------------------- - perform and time the 1d FFTs required for N timesteps -------------------------------------------------------------------------- */ - -int PPPMStagger::timing_1d(int n, double &time1d) -{ - PPPM::timing_1d(n,time1d); - time1d *= 2.0; - - if (differentiation_flag) return 2; - return 4; -} - -/* ---------------------------------------------------------------------- - perform and time the 3d FFTs required for N timesteps -------------------------------------------------------------------------- */ - -int PPPMStagger::timing_3d(int n, double &time3d) -{ - PPPM::timing_3d(n,time3d); - time3d *= 2.0; - - if (differentiation_flag) return 2; - return 4; -} +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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: Stan Moore (Sandia) +------------------------------------------------------------------------- */ + +#include "lmptype.h" +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" +#include "pppm_stagger.h" +#include "atom.h" +#include "commgrid.h" +#include "force.h" +#include "domain.h" +#include "memory.h" +#include "error.h" + +#include "math_const.h" +#include "math_special.h" + +using namespace LAMMPS_NS; +using namespace MathConst; +using namespace MathSpecial; + +#define OFFSET 16384 +#define EPS_HOC 1.0e-7 + +enum{REVERSE_RHO}; +enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; + +#ifdef FFT_SINGLE +#define ZEROF 0.0f +#define ONEF 1.0f +#else +#define ZEROF 0.0 +#define ONEF 1.0 +#endif + +/* ---------------------------------------------------------------------- */ + +PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : + PPPM(lmp, narg, arg) +{ + if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm/stagger command"); + stagger_flag = 1; + group_group_enable = 0; + + memory->create(gf_b2,8,7,"pppm_stagger:gf_b2"); + gf_b2[1][0] = 1.0; + gf_b2[2][0] = 5.0 / 6.0; + gf_b2[2][1] = 1.0 / 6.0; + gf_b2[3][0] = 61.0 / 120.0; + gf_b2[3][1] = 29.0 / 60.0; + gf_b2[3][2] = 1.0 / 120.0; + gf_b2[4][0] = 277.0 / 1008.0; + gf_b2[4][1] = 1037.0 / 1680.0; + gf_b2[4][2] = 181.0 / 1680.00; + gf_b2[4][3] = 1.0 / 5040.0; + gf_b2[5][0] = 50521.0 / 362880.0; + gf_b2[5][1] = 7367.0 / 12960.0; + gf_b2[5][2] = 16861.0 / 60480.0; + gf_b2[5][3] = 1229.0 / 90720.0; + gf_b2[5][4] = 1.0 / 362880.0; + gf_b2[6][0] = 540553.0 / 7983360.0; + gf_b2[6][1] = 17460701.0 / 39916800.0; + gf_b2[6][2] = 8444893.0 / 19958400.0; + gf_b2[6][3] = 1409633.0 / 19958400.0; + gf_b2[6][4] = 44281.0 / 39916800.0; + gf_b2[6][5] = 1.0 / 39916800.0; + gf_b2[7][0] = 199360981.0 / 6227020800.0; + gf_b2[7][1] = 103867703.0 / 345945600.0; + gf_b2[7][2] = 66714163.0 / 138378240.0; + gf_b2[7][3] = 54085121.0 / 311351040.0; + gf_b2[7][4] = 1640063.0 / 138378240.0; + gf_b2[7][5] = 671.0 / 10483200.0; + gf_b2[7][6] = 1.0 / 6227020800.0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +PPPMStagger::~PPPMStagger() +{ + memory->destroy(gf_b2); +} + +/* ---------------------------------------------------------------------- + called once before run +------------------------------------------------------------------------- */ + +void PPPMStagger::init() +{ + // error check + + if (domain->triclinic) + error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger " + "with triclinic systems"); + + PPPM::init(); +} + +/* ---------------------------------------------------------------------- + compute the PPPM long-range force, energy, virial +------------------------------------------------------------------------- */ + +void PPPMStagger::compute(int eflag, int vflag) +{ + int i,j; + + // set energy/virial flags + // invoke allocate_peratom() if needed for first time + + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = evflag_atom = eflag_global = vflag_global = + eflag_atom = vflag_atom = 0; + + if (evflag_atom && !peratom_allocate_flag) { + allocate_peratom(); + cg_peratom->ghost_notify(); + cg_peratom->setup(); + } + + // convert atoms from box to lamda coords + + if (triclinic == 0) boxlo = domain->boxlo; + else { + boxlo = domain->boxlo_lamda; + domain->x2lamda(atom->nlocal); + } + + // extend size of per-atom arrays if necessary + + if (atom->nlocal > nmax) { + memory->destroy(part2grid); + nmax = atom->nmax; + memory->create(part2grid,nmax,3,"pppm:part2grid"); + } + + nstagger = 2; + + stagger = 0.0; + for (int n=0; nreverse_comm(this,REVERSE_RHO); + brick2fft(); + + // compute potential gradient on my FFT grid and + // portion of e_long on this proc's FFT grid + // return gradients (electric fields) in 3d brick decomposition + // also performs per-atom calculations via poisson_peratom() + + poisson(); + + // all procs communicate E-field values + // to fill ghost cells surrounding their 3d bricks + + if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); + else cg->forward_comm(this,FORWARD_IK); + + // extra per-atom energy/virial communication + + if (evflag_atom) { + if (differentiation_flag == 1 && vflag_atom) + cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); + else if (differentiation_flag == 0) + cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); + } + + // calculate the force on my particles + + fieldforce(); + + // extra per-atom energy/virial communication + + if (evflag_atom) fieldforce_peratom(); + + stagger += 1.0/float(nstagger); + } + + // sum global energy across procs and add in volume-dependent term + + const double qscale = force->qqrd2e * scale; + + if (eflag_global) { + double energy_all; + MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); + energy = energy_all; + + energy *= 0.5*volume/float(nstagger); + energy -= g_ewald*qsqsum/MY_PIS + + MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qscale; + } + + // sum global virial across procs + + if (vflag_global) { + double virial_all[6]; + MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]/float(nstagger); + } + + // per-atom energy/virial + // energy includes self-energy correction + // notal accounts for TIP4P tallying eatom/vatom for ghost atoms + + if (evflag_atom) { + double *q = atom->q; + int nlocal = atom->nlocal; + int ntotal = nlocal; + if (tip4pflag) ntotal += atom->nghost; + + if (eflag_atom) { + for (i = 0; i < nlocal; i++) { + eatom[i] *= 0.5; + eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / + (g_ewald*g_ewald*volume); + eatom[i] *= qscale; + } + for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; + } + + if (vflag_atom) { + for (i = 0; i < ntotal; i++) + for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; + } + } + + // 2d slab correction + + if (slabflag == 1) slabcorr(); + + // convert atoms back from lamda to box coords + + if (triclinic) domain->lamda2x(atom->nlocal); +} + +/* ---------------------------------------------------------------------- + compute qopt +------------------------------------------------------------------------- */ + +double PPPMStagger::compute_qopt() +{ + if (differentiation_flag == 1) + return compute_qopt_ad(); + + double qopt = 0.0; + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,sum2,dot1,dot2; + double numerator,denominator; + double u1,u2,u3,sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = 2; + const int nby = 2; + const int nbz = 2; + + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + cny = cos(0.5*unitky*lper*yprd/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); + sum1 = 0.0; + sum2 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx + qy*qy + qz*qz; + u1 = sx*sy*sz; + u2 = wx*wy*wz; + u3 = numerator*u1*u2*dot1; + sum1 += u1*u1*MY_4PI*MY_4PI/dot2; + sum2 += u3*u3/dot2; + } + } + } + qopt += sum1 - sum2/denominator; + } + } + } + } + double qopt_all; + MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); + return qopt_all; +} + +/* ---------------------------------------------------------------------- + compute qopt_ad +------------------------------------------------------------------------- */ + +double PPPMStagger::compute_qopt_ad() +{ + double qopt = 0.0; + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,sum2,sum3,sum4,sum5,sum6,dot2; + double u1,u2,sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = 2; + const int nby = 2; + const int nbz = 2; + + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + cny = cos(0.5*unitky*lper*yprd/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + sum1 = 0.0; + sum2 = 0.0; + sum3 = 0.0; + sum4 = 0.0; + sum5 = 0.0; + sum6 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot2 = qx*qx + qy*qy + qz*qz; + u1 = sx*sy*sz; + u2 = wx*wy*wz; + sum1 += u1*u1/dot2*MY_4PI*MY_4PI; + sum2 += u1*u1*u2*u2*MY_4PI*MY_4PI; + sum3 += u2; + sum4 += dot2*u2; + sum5 += u2*powint(-1.0,nx+ny+nz); + sum6 += dot2*u2*powint(-1.0,nx+ny+nz); + } + } + } + qopt += sum1 - sum2/(0.5*(sum3*sum4 + sum5*sum6)); + } + } + } + } + double qopt_all; + MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); + return qopt_all; +} + +/* ---------------------------------------------------------------------- + pre-compute Green's function denominator expansion coeffs, Gamma(2n) +------------------------------------------------------------------------- */ + +void PPPMStagger::compute_gf_denom() +{ + if (gf_b) memory->destroy(gf_b); + memory->create(gf_b,order,"pppm:gf_b"); + + int k,l,m; + + for (l = 1; l < order; l++) gf_b[l] = 0.0; + gf_b[0] = 1.0; + + for (m = 1; m < order; m++) { + for (l = m; l > 0; l--) + gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); + gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); + } + + bigint ifact = 1; + for (k = 1; k < 2*order; k++) ifact *= k; + double gaminv = 1.0/ifact; + for (l = 0; l < order; l++) gf_b[l] *= gaminv; +} + +/* ---------------------------------------------------------------------- + pre-compute modified (Hockney-Eastwood) Coulomb Green's function +------------------------------------------------------------------------- */ + +void PPPMStagger::compute_gf_ik() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + double sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + cnz = cos(0.5*unitkz*mper*zprd_slab/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + cny = cos(0.5*unitky*lper*yprd/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + cnx = cos(0.5*unitkx*kper*xprd/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); + sum1 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + compute optimized Green's function for energy calculation +------------------------------------------------------------------------- */ + +void PPPMStagger::compute_gf_ad() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz,sqk; + double cnx,cny,cnz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double numerator,denominator; + int k,l,m,n,kper,lper,mper; + + const int twoorder = 2*order; + + for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + qz = unitkz*mper; + snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); + cnz = cos(0.5*qz*zprd_slab/nz_pppm); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + qy = unitky*lper; + sny = square(sin(0.5*qy*yprd/ny_pppm)); + cny = cos(0.5*qy*yprd/ny_pppm); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + qx = unitkx*kper; + snx = square(sin(0.5*qx*xprd/nx_pppm)); + cnx = cos(0.5*qx*xprd/nx_pppm); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + sqk = qx*qx + qy*qy + qz*qz; + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = 0.5*(gf_denom(snx,sny,snz) + gf_denom2(cnx,cny,cnz)); + greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } else { + greensfn[n] = 0.0; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } + } + } + } + + // compute the coefficients for the self-force correction + + double prex, prey, prez; + prex = prey = prez = MY_PI/volume; + prex *= nx_pppm/xprd; + prey *= ny_pppm/yprd; + prez *= nz_pppm/zprd_slab; + sf_coeff[0] *= prex; + sf_coeff[1] *= prex*2; + sf_coeff[2] *= prey; + sf_coeff[3] *= prey*2; + sf_coeff[4] *= prez; + sf_coeff[5] *= prez*2; + + // communicate values with other procs + + double tmp[6]; + MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); + for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; +} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPMStagger::particle_map() +{ + int nx,ny,nz; + + double **x = atom->x; + int nlocal = atom->nlocal; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift + stagger) - OFFSET; + ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift + stagger) - OFFSET; + nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift + stagger) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) + flag = 1; + } + + if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPMStagger::make_rho() +{ + int l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + + // clear 3d density array + + memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ik +------------------------------------------------------------------------- */ + +void PPPMStagger::fieldforce_ik() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR ekx,eky,ekz; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ekx -= x0*vdx_brick[mz][my][mx]; + eky -= x0*vdy_brick[mz][my][mx]; + ekz -= x0*vdz_brick[mz][my][mx]; + } + } + } + + // convert E-field to force + + const double qfactor = force->qqrd2e * scale * q[i] / float(nstagger); + f[i][0] += qfactor*ekx; + f[i][1] += qfactor*eky; + if (slabflag != 2) f[i][2] += qfactor*ekz; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ad +------------------------------------------------------------------------- */ + +void PPPMStagger::fieldforce_ad() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz; + FFT_SCALAR ekx,eky,ekz; + double s1,s2,s3; + double sf = 0.0; + double *prd; + + prd = domain->prd; + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + + double hx_inv = nx_pppm/xprd; + double hy_inv = ny_pppm/yprd; + double hz_inv = nz_pppm/zprd; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + compute_drho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; + } + } + } + ekx *= hx_inv; + eky *= hy_inv; + ekz *= hz_inv; + + // convert E-field to force and substract self forces + + const double qfactor = force->qqrd2e * scale / float(nstagger); + + s1 = x[i][0]*hx_inv + stagger; + s2 = x[i][1]*hy_inv + stagger; + s3 = x[i][2]*hz_inv + stagger; + sf = sf_coeff[0]*sin(2*MY_PI*s1); + sf += sf_coeff[1]*sin(4*MY_PI*s1); + sf *= 2*q[i]*q[i]; + f[i][0] += qfactor*(ekx*q[i] - sf); + + sf = sf_coeff[2]*sin(2*MY_PI*s2); + sf += sf_coeff[3]*sin(4*MY_PI*s2); + sf *= 2*q[i]*q[i]; + f[i][1] += qfactor*(eky*q[i] - sf); + + + sf = sf_coeff[4]*sin(2*MY_PI*s3); + sf += sf_coeff[5]*sin(4*MY_PI*s3); + sf *= 2*q[i]*q[i]; + if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get per-atom energy/virial +------------------------------------------------------------------------- */ + +void PPPMStagger::fieldforce_peratom() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR u,v0,v1,v2,v3,v4,v5; + + // loop over my charges, interpolate from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv - stagger; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv - stagger; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv - stagger; + + compute_rho1d(dx,dy,dz); + + u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + if (eflag_atom) u += x0*u_brick[mz][my][mx]; + if (vflag_atom) { + v0 += x0*v0_brick[mz][my][mx]; + v1 += x0*v1_brick[mz][my][mx]; + v2 += x0*v2_brick[mz][my][mx]; + v3 += x0*v3_brick[mz][my][mx]; + v4 += x0*v4_brick[mz][my][mx]; + v5 += x0*v5_brick[mz][my][mx]; + } + } + } + } + + if (eflag_atom) eatom[i] += q[i]*u/float(nstagger); + if (vflag_atom) { + vatom[i][0] += q[i]*v0/float(nstagger); + vatom[i][1] += q[i]*v1/float(nstagger); + vatom[i][2] += q[i]*v2/float(nstagger); + vatom[i][3] += q[i]*v3/float(nstagger); + vatom[i][4] += q[i]*v4/float(nstagger); + vatom[i][5] += q[i]*v5/float(nstagger); + } + } +} + +/* ---------------------------------------------------------------------- + perform and time the 1d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPMStagger::timing_1d(int n, double &time1d) +{ + PPPM::timing_1d(n,time1d); + time1d *= 2.0; + + if (differentiation_flag) return 2; + return 4; +} + +/* ---------------------------------------------------------------------- + perform and time the 3d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPMStagger::timing_3d(int n, double &time3d) +{ + PPPM::timing_3d(n,time3d); + time3d *= 2.0; + + if (differentiation_flag) return 2; + return 4; +} diff --git a/src/KSPACE/pppm_stagger.h b/src/KSPACE/pppm_stagger.h index cb80cf4766..b1ed7779dc 100755 --- a/src/KSPACE/pppm_stagger.h +++ b/src/KSPACE/pppm_stagger.h @@ -1,109 +1,109 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 KSPACE_CLASS - -KSpaceStyle(pppm/stagger,PPPMStagger) - -#else - -#ifndef LMP_PPPM_STAGGER_H -#define LMP_PPPM_STAGGER_H - -#include "pppm.h" - -namespace LAMMPS_NS { - -class PPPMStagger : public PPPM { - public: - PPPMStagger(class LAMMPS *, int, char **); - virtual ~PPPMStagger(); - virtual void init(); - virtual void compute(int, int); - virtual int timing_1d(int, double &); - virtual int timing_3d(int, double &); - - protected: - int nstagger; - double stagger; - double **gf_b2; - - virtual double compute_qopt(); - double compute_qopt_ad(); - virtual void compute_gf_denom(); - virtual void compute_gf_ik(); - virtual void compute_gf_ad(); - - virtual void particle_map(); - virtual void make_rho(); - virtual void fieldforce_ik(); - virtual void fieldforce_ad(); - virtual void fieldforce_peratom(); - - - inline double gf_denom2(const double &x, const double &y, - const double &z) const { - double sx,sy,sz; - double x2 = x*x; - double y2 = y*y; - double z2 = z*z; - double xl = x; - double yl = y; - double zl = z; - for (int l = 0; l < order; l++) { - sx += gf_b2[order][l]*xl; - sy += gf_b2[order][l]*yl; - sz += gf_b2[order][l]*zl; - xl *= x2; - yl *= y2; - zl *= z2; - } - double s = sx*sy*sz; - return s*s; - }; -}; - -} - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Illegal ... command - -Self-explanatory. Check the input script syntax and compare to the -documentation for the command. You can use -echo screen as a -command-line option when running LAMMPS to see the offending line. - -E: Cannot (yet) use kspace_style pppm/stagger with triclinic systems - -This feature is not yet supported. - -E: Out of range atoms - cannot compute PPPM - -One or more atoms are attempting to map their charge to a PPPM grid -point that is not owned by a processor. This is likely for one of two -reasons, both of them bad. First, it may mean that an atom near the -boundary of a processor's sub-domain has moved more than 1/2 the -"neighbor skin distance"_neighbor.html without neighbor lists being -rebuilt and atoms being migrated to new processors. This also means -you may be missing pairwise interactions that need to be computed. -The solution is to change the re-neighboring criteria via the -"neigh_modify"_neigh_modify command. The safest settings are "delay 0 -every 1 check yes". Second, it may mean that an atom has moved far -outside a processor's sub-domain or even the entire simulation box. -This indicates bad physics, e.g. due to highly overlapping atoms, too -large a timestep, etc. - -*/ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 KSPACE_CLASS + +KSpaceStyle(pppm/stagger,PPPMStagger) + +#else + +#ifndef LMP_PPPM_STAGGER_H +#define LMP_PPPM_STAGGER_H + +#include "pppm.h" + +namespace LAMMPS_NS { + +class PPPMStagger : public PPPM { + public: + PPPMStagger(class LAMMPS *, int, char **); + virtual ~PPPMStagger(); + virtual void init(); + virtual void compute(int, int); + virtual int timing_1d(int, double &); + virtual int timing_3d(int, double &); + + protected: + int nstagger; + double stagger; + double **gf_b2; + + virtual double compute_qopt(); + double compute_qopt_ad(); + virtual void compute_gf_denom(); + virtual void compute_gf_ik(); + virtual void compute_gf_ad(); + + virtual void particle_map(); + virtual void make_rho(); + virtual void fieldforce_ik(); + virtual void fieldforce_ad(); + virtual void fieldforce_peratom(); + + + inline double gf_denom2(const double &x, const double &y, + const double &z) const { + double sx,sy,sz; + double x2 = x*x; + double y2 = y*y; + double z2 = z*z; + double xl = x; + double yl = y; + double zl = z; + for (int l = 0; l < order; l++) { + sx += gf_b2[order][l]*xl; + sy += gf_b2[order][l]*yl; + sz += gf_b2[order][l]*zl; + xl *= x2; + yl *= y2; + zl *= z2; + } + double s = sx*sy*sz; + return s*s; + }; +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Cannot (yet) use kspace_style pppm/stagger with triclinic systems + +This feature is not yet supported. + +E: Out of range atoms - cannot compute PPPM + +One or more atoms are attempting to map their charge to a PPPM grid +point that is not owned by a processor. This is likely for one of two +reasons, both of them bad. First, it may mean that an atom near the +boundary of a processor's sub-domain has moved more than 1/2 the +"neighbor skin distance"_neighbor.html without neighbor lists being +rebuilt and atoms being migrated to new processors. This also means +you may be missing pairwise interactions that need to be computed. +The solution is to change the re-neighboring criteria via the +"neigh_modify"_neigh_modify command. The safest settings are "delay 0 +every 1 check yes". Second, it may mean that an atom has moved far +outside a processor's sub-domain or even the entire simulation box. +This indicates bad physics, e.g. due to highly overlapping atoms, too +large a timestep, etc. + +*/ From c0acc33584aeed47dff95d55fbe38f97e5067d81 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:51:59 +0000 Subject: [PATCH 32/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10134 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm.cpp | 6792 +++++++++++++++++++++---------------------- src/KSPACE/pppm.h | 670 ++--- 2 files changed, 3731 insertions(+), 3731 deletions(-) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 44e03aeb35..b33371fd84 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -1,3396 +1,3396 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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: Roy Pollock (LLNL), Paul Crozier (SNL) - per-atom energy/virial & group/group energy/force added by Stan Moore (BYU) - analytic diff (2 FFT) option added by Rolf Isele-Holder (Aachen University) - triclinic added by Stan Moore (SNL) -------------------------------------------------------------------------- */ - -#include "lmptype.h" -#include "mpi.h" -#include "string.h" -#include "stdio.h" -#include "stdlib.h" -#include "math.h" -#include "pppm.h" -#include "atom.h" -#include "comm.h" -#include "commgrid.h" -#include "neighbor.h" -#include "force.h" -#include "pair.h" -#include "bond.h" -#include "angle.h" -#include "domain.h" -#include "fft3d_wrap.h" -#include "remap_wrap.h" -#include "memory.h" -#include "error.h" - -#include "math_const.h" -#include "math_special.h" - -using namespace LAMMPS_NS; -using namespace MathConst; -using namespace MathSpecial; - -#define MAXORDER 7 -#define OFFSET 16384 -#define SMALL 0.00001 -#define LARGE 10000.0 -#define EPS_HOC 1.0e-7 - -enum{REVERSE_RHO}; -enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; - -#ifdef FFT_SINGLE -#define ZEROF 0.0f -#define ONEF 1.0f -#else -#define ZEROF 0.0 -#define ONEF 1.0 -#endif - -/* ---------------------------------------------------------------------- */ - -PPPM::PPPM(LAMMPS *lmp, int narg, char **arg) : KSpace(lmp, narg, arg) -{ - if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm command"); - - pppmflag = 1; - group_group_enable = 1; - - accuracy_relative = atof(arg[0]); - - nfactors = 3; - factors = new int[nfactors]; - factors[0] = 2; - factors[1] = 3; - factors[2] = 5; - - MPI_Comm_rank(world,&me); - MPI_Comm_size(world,&nprocs); - - density_brick = vdx_brick = vdy_brick = vdz_brick = NULL; - density_fft = NULL; - u_brick = NULL; - v0_brick = v1_brick = v2_brick = v3_brick = v4_brick = v5_brick = NULL; - greensfn = NULL; - work1 = work2 = NULL; - vg = NULL; - fkx = fky = fkz = NULL; - - sf_precoeff1 = sf_precoeff2 = sf_precoeff3 = - sf_precoeff4 = sf_precoeff5 = sf_precoeff6 = NULL; - - density_A_brick = density_B_brick = NULL; - density_A_fft = density_B_fft = NULL; - - gf_b = NULL; - rho1d = rho_coeff = drho1d = drho_coeff = NULL; - - fft1 = fft2 = NULL; - remap = NULL; - cg = NULL; - cg_peratom = NULL; - - nmax = 0; - part2grid = NULL; - - peratom_allocate_flag = 0; - group_allocate_flag = 0; - - // define acons coefficients for estimation of kspace errors - // see JCP 109, pg 7698 for derivation of coefficients - // higher order coefficients may be computed if needed - - memory->create(acons,8,7,"pppm:acons"); - acons[1][0] = 2.0 / 3.0; - acons[2][0] = 1.0 / 50.0; - acons[2][1] = 5.0 / 294.0; - acons[3][0] = 1.0 / 588.0; - acons[3][1] = 7.0 / 1440.0; - acons[3][2] = 21.0 / 3872.0; - acons[4][0] = 1.0 / 4320.0; - acons[4][1] = 3.0 / 1936.0; - acons[4][2] = 7601.0 / 2271360.0; - acons[4][3] = 143.0 / 28800.0; - acons[5][0] = 1.0 / 23232.0; - acons[5][1] = 7601.0 / 13628160.0; - acons[5][2] = 143.0 / 69120.0; - acons[5][3] = 517231.0 / 106536960.0; - acons[5][4] = 106640677.0 / 11737571328.0; - acons[6][0] = 691.0 / 68140800.0; - acons[6][1] = 13.0 / 57600.0; - acons[6][2] = 47021.0 / 35512320.0; - acons[6][3] = 9694607.0 / 2095994880.0; - acons[6][4] = 733191589.0 / 59609088000.0; - acons[6][5] = 326190917.0 / 11700633600.0; - acons[7][0] = 1.0 / 345600.0; - acons[7][1] = 3617.0 / 35512320.0; - acons[7][2] = 745739.0 / 838397952.0; - acons[7][3] = 56399353.0 / 12773376000.0; - acons[7][4] = 25091609.0 / 1560084480.0; - acons[7][5] = 1755948832039.0 / 36229939200000.0; - acons[7][6] = 4887769399.0 / 37838389248.0; -} - -/* ---------------------------------------------------------------------- - free all memory -------------------------------------------------------------------------- */ - -PPPM::~PPPM() -{ - delete [] factors; - deallocate(); - if (peratom_allocate_flag) deallocate_peratom(); - if (group_allocate_flag) deallocate_groups(); - memory->destroy(part2grid); - memory->destroy(acons); -} - -/* ---------------------------------------------------------------------- - called once before run -------------------------------------------------------------------------- */ - -void PPPM::init() -{ - if (me == 0) { - if (screen) fprintf(screen,"PPPM initialization ...\n"); - if (logfile) fprintf(logfile,"PPPM initialization ...\n"); - } - - // error check - - triclinic_check(); - if (domain->triclinic && differentiation_flag == 1) - error->all(FLERR,"Cannot (yet) use PPPM with triclinic box " - "and kspace_modify diff a'"); - if (domain->triclinic && slabflag) - error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and " - "slab correction"); - if (domain->dimension == 2) error->all(FLERR, - "Cannot use PPPM with 2d simulation"); - - if (!atom->q_flag) error->all(FLERR,"Kspace style requires atom attribute q"); - - if (slabflag == 0 && domain->nonperiodic > 0) - error->all(FLERR,"Cannot use nonperiodic boundaries with PPPM"); - if (slabflag) { - if (domain->xperiodic != 1 || domain->yperiodic != 1 || - domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) - error->all(FLERR,"Incorrect boundaries with slab PPPM"); - } - - if (order < 2 || order > MAXORDER) { - char str[128]; - sprintf(str,"PPPM order cannot be < 2 or > than %d",MAXORDER); - error->all(FLERR,str); - } - - // extract short-range Coulombic cutoff from pair style - - triclinic = domain->triclinic; - scale = 1.0; - - pair_check(); - - int itmp = 0; - double *p_cutoff = (double *) force->pair->extract("cut_coul",itmp); - if (p_cutoff == NULL) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - cutoff = *p_cutoff; - - // if kspace is TIP4P, extract TIP4P params from pair style - // bond/angle are not yet init(), so insure equilibrium request is valid - - qdist = 0.0; - - if (tip4pflag) { - double *p_qdist = (double *) force->pair->extract("qdist",itmp); - int *p_typeO = (int *) force->pair->extract("typeO",itmp); - int *p_typeH = (int *) force->pair->extract("typeH",itmp); - int *p_typeA = (int *) force->pair->extract("typeA",itmp); - int *p_typeB = (int *) force->pair->extract("typeB",itmp); - if (!p_qdist || !p_typeO || !p_typeH || !p_typeA || !p_typeB) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - qdist = *p_qdist; - typeO = *p_typeO; - typeH = *p_typeH; - int typeA = *p_typeA; - int typeB = *p_typeB; - - if (force->angle == NULL || force->bond == NULL) - error->all(FLERR,"Bond and angle potentials must be defined for TIP4P"); - if (typeA < 1 || typeA > atom->nangletypes || - force->angle->setflag[typeA] == 0) - error->all(FLERR,"Bad TIP4P angle type for PPPM/TIP4P"); - if (typeB < 1 || typeB > atom->nbondtypes || - force->bond->setflag[typeB] == 0) - error->all(FLERR,"Bad TIP4P bond type for PPPM/TIP4P"); - double theta = force->angle->equilibrium_angle(typeA); - double blen = force->bond->equilibrium_distance(typeB); - alpha = qdist / (cos(0.5*theta) * blen); - if (domain->triclinic) - error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and TIP4P"); - } - - // compute qsum & qsqsum and warn if not charge-neutral - - qsum = qsqsum = 0.0; - for (int i = 0; i < atom->nlocal; i++) { - qsum += atom->q[i]; - qsqsum += atom->q[i]*atom->q[i]; - } - - double tmp; - MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); - qsum = tmp; - MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); - qsqsum = tmp; - q2 = qsqsum * force->qqrd2e / force->dielectric; - - if (qsqsum == 0.0) - error->all(FLERR,"Cannot use kspace solver on system with no charge"); - if (fabs(qsum) > SMALL && me == 0) { - char str[128]; - sprintf(str,"System is not charge neutral, net charge = %g",qsum); - error->warning(FLERR,str); - } - - // set accuracy (force units) from accuracy_relative or accuracy_absolute - - if (accuracy_absolute >= 0.0) accuracy = accuracy_absolute; - else accuracy = accuracy_relative * two_charge_force; - - // free all arrays previously allocated - - deallocate(); - if (peratom_allocate_flag) deallocate_peratom(); - if (group_allocate_flag) deallocate_groups(); - - // setup FFT grid resolution and g_ewald - // normally one iteration thru while loop is all that is required - // if grid stencil does not extend beyond neighbor proc - // or overlap is allowed, then done - // else reduce order and try again - - int (*procneigh)[2] = comm->procneigh; - - CommGrid *cgtmp = NULL; - int iteration = 0; - - while (order >= minorder) { - if (iteration && me == 0) - error->warning(FLERR,"Reducing PPPM order b/c stencil extends " - "beyond nearest neighbor processor"); - - if (stagger_flag && !differentiation_flag) compute_gf_denom(); - set_grid_global(); - set_grid_local(); - if (overlap_allowed) break; - - cgtmp = new CommGrid(lmp,world,1,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); - cgtmp->ghost_notify(); - if (!cgtmp->ghost_overlap()) break; - delete cgtmp; - - order--; - iteration++; - } - - if (order < minorder) error->all(FLERR,"PPPM order < minimum allowed order"); - if (!overlap_allowed && cgtmp->ghost_overlap()) - error->all(FLERR,"PPPM grid stencil extends " - "beyond nearest neighbor processor"); - if (cgtmp) delete cgtmp; - - // adjust g_ewald - - if (!gewaldflag) adjust_gewald(); - - // calculate the final accuracy - - double estimated_accuracy = final_accuracy(); - - // print stats - - int ngrid_max,nfft_both_max; - MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world); - MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world); - - if (me == 0) { - -#ifdef FFT_SINGLE - const char fft_prec[] = "single"; -#else - const char fft_prec[] = "double"; -#endif - - if (screen) { - fprintf(screen," G vector (1/distance)= %g\n",g_ewald); - fprintf(screen," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); - fprintf(screen," stencil order = %d\n",order); - fprintf(screen," estimated absolute RMS force accuracy = %g\n", - estimated_accuracy); - fprintf(screen," estimated relative force accuracy = %g\n", - estimated_accuracy/two_charge_force); - fprintf(screen," using %s precision FFTs\n",fft_prec); - fprintf(screen," 3d grid and FFT values/proc = %d %d\n", - ngrid_max,nfft_both_max); - } - if (logfile) { - fprintf(logfile," G vector (1/distance) = %g\n",g_ewald); - fprintf(logfile," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); - fprintf(logfile," stencil order = %d\n",order); - fprintf(logfile," estimated absolute RMS force accuracy = %g\n", - estimated_accuracy); - fprintf(logfile," estimated relative force accuracy = %g\n", - estimated_accuracy/two_charge_force); - fprintf(logfile," using %s precision FFTs\n",fft_prec); - fprintf(logfile," 3d grid and FFT values/proc = %d %d\n", - ngrid_max,nfft_both_max); - } - } - - // allocate K-space dependent memory - // don't invoke allocate peratom() or group(), will be allocated when needed - - allocate(); - cg->ghost_notify(); - cg->setup(); - - // pre-compute Green's function denomiator expansion - // pre-compute 1d charge distribution coefficients - - compute_gf_denom(); - if (differentiation_flag == 1) compute_sf_precoeff(); - compute_rho_coeff(); -} - -/* ---------------------------------------------------------------------- - adjust PPPM coeffs, called initially and whenever volume has changed -------------------------------------------------------------------------- */ - -void PPPM::setup() -{ - if (triclinic) { - setup_triclinic(); - return; - } - - int i,j,k,n; - double *prd; - - // volume-dependent factors - // adjust z dimension for 2d slab PPPM - // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 - - if (triclinic == 0) prd = domain->prd; - else prd = domain->prd_lamda; - - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - double zprd_slab = zprd*slab_volfactor; - volume = xprd * yprd * zprd_slab; - - delxinv = nx_pppm/xprd; - delyinv = ny_pppm/yprd; - delzinv = nz_pppm/zprd_slab; - - delvolinv = delxinv*delyinv*delzinv; - - double unitkx = (MY_2PI/xprd); - double unitky = (MY_2PI/yprd); - double unitkz = (MY_2PI/zprd_slab); - - // fkx,fky,fkz for my FFT grid pts - - double per; - - for (i = nxlo_fft; i <= nxhi_fft; i++) { - per = i - nx_pppm*(2*i/nx_pppm); - fkx[i] = unitkx*per; - } - - for (i = nylo_fft; i <= nyhi_fft; i++) { - per = i - ny_pppm*(2*i/ny_pppm); - fky[i] = unitky*per; - } - - for (i = nzlo_fft; i <= nzhi_fft; i++) { - per = i - nz_pppm*(2*i/nz_pppm); - fkz[i] = unitkz*per; - } - - // virial coefficients - - double sqk,vterm; - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) { - for (j = nylo_fft; j <= nyhi_fft; j++) { - for (i = nxlo_fft; i <= nxhi_fft; i++) { - sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k]; - if (sqk == 0.0) { - vg[n][0] = 0.0; - vg[n][1] = 0.0; - vg[n][2] = 0.0; - vg[n][3] = 0.0; - vg[n][4] = 0.0; - vg[n][5] = 0.0; - } else { - vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); - vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i]; - vg[n][1] = 1.0 + vterm*fky[j]*fky[j]; - vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k]; - vg[n][3] = vterm*fkx[i]*fky[j]; - vg[n][4] = vterm*fkx[i]*fkz[k]; - vg[n][5] = vterm*fky[j]*fkz[k]; - } - n++; - } - } - } - - if (differentiation_flag == 1) compute_gf_ad(); - else compute_gf_ik(); -} - -/* ---------------------------------------------------------------------- - adjust PPPM coeffs, called initially and whenever volume has changed - for a triclinic system -------------------------------------------------------------------------- */ - -void PPPM::setup_triclinic() -{ - int i,j,k,n; - double *prd; - - // volume-dependent factors - // adjust z dimension for 2d slab PPPM - // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 - - prd = domain->prd; - - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - double zprd_slab = zprd*slab_volfactor; - volume = xprd * yprd * zprd_slab; - - // use lamda (0-1) coordinates - - delxinv = nx_pppm; - delyinv = ny_pppm; - delzinv = nz_pppm; - delvolinv = delxinv*delyinv*delzinv/volume; - - // fkx,fky,fkz for my FFT grid pts - - double per_i,per_j,per_k; - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) { - per_k = k - nz_pppm*(2*k/nz_pppm); - for (j = nylo_fft; j <= nyhi_fft; j++) { - per_j = j - ny_pppm*(2*j/ny_pppm); - for (i = nxlo_fft; i <= nxhi_fft; i++) { - per_i = i - nx_pppm*(2*i/nx_pppm); - - double unitk_lamda[3]; - unitk_lamda[0] = 2.0*MY_PI*per_i; - unitk_lamda[1] = 2.0*MY_PI*per_j; - unitk_lamda[2] = 2.0*MY_PI*per_k; - x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); - fkx[n] = unitk_lamda[0]; - fky[n] = unitk_lamda[1]; - fkz[n] = unitk_lamda[2]; - n++; - } - } - } - - // virial coefficients - - double sqk,vterm; - - for (n = 0; n < nfft; n++) { - sqk = fkx[n]*fkx[n] + fky[n]*fky[n] + fkz[n]*fkz[n]; - if (sqk == 0.0) { - vg[n][0] = 0.0; - vg[n][1] = 0.0; - vg[n][2] = 0.0; - vg[n][3] = 0.0; - vg[n][4] = 0.0; - vg[n][5] = 0.0; - } else { - vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); - vg[n][0] = 1.0 + vterm*fkx[n]*fkx[n]; - vg[n][1] = 1.0 + vterm*fky[n]*fky[n]; - vg[n][2] = 1.0 + vterm*fkz[n]*fkz[n]; - vg[n][3] = vterm*fkx[n]*fky[n]; - vg[n][4] = vterm*fkx[n]*fkz[n]; - vg[n][5] = vterm*fky[n]*fkz[n]; - } - } - - compute_gf_ik_triclinic(); -} - -/* ---------------------------------------------------------------------- - reset local grid arrays and communication stencils - called by fix balance b/c it changed sizes of processor sub-domains -------------------------------------------------------------------------- */ - -void PPPM::setup_grid() -{ - // free all arrays previously allocated - - deallocate(); - if (peratom_allocate_flag) deallocate_peratom(); - if (group_allocate_flag) deallocate_groups(); - - // reset portion of global grid that each proc owns - - set_grid_local(); - - // reallocate K-space dependent memory - // check if grid communication is now overlapping if not allowed - // don't invoke allocate peratom() or group(), will be allocated when needed - - allocate(); - - cg->ghost_notify(); - if (overlap_allowed == 0 && cg->ghost_overlap()) - error->all(FLERR,"PPPM grid stencil extends " - "beyond nearest neighbor processor"); - cg->setup(); - - // pre-compute Green's function denomiator expansion - // pre-compute 1d charge distribution coefficients - - compute_gf_denom(); - if (differentiation_flag == 1) compute_sf_precoeff(); - compute_rho_coeff(); - - // pre-compute volume-dependent coeffs - - setup(); -} - -/* ---------------------------------------------------------------------- - compute the PPPM long-range force, energy, virial -------------------------------------------------------------------------- */ - -void PPPM::compute(int eflag, int vflag) -{ - int i,j; - - // set energy/virial flags - // invoke allocate_peratom() if needed for first time - - if (eflag || vflag) ev_setup(eflag,vflag); - else evflag = evflag_atom = eflag_global = vflag_global = - eflag_atom = vflag_atom = 0; - - if (evflag_atom && !peratom_allocate_flag) { - allocate_peratom(); - cg_peratom->ghost_notify(); - cg_peratom->setup(); - } - - // convert atoms from box to lamda coords - - if (triclinic == 0) boxlo = domain->boxlo; - else { - boxlo = domain->boxlo_lamda; - domain->x2lamda(atom->nlocal); - } - - // extend size of per-atom arrays if necessary - - if (atom->nlocal > nmax) { - memory->destroy(part2grid); - nmax = atom->nmax; - memory->create(part2grid,nmax,3,"pppm:part2grid"); - } - - // find grid points for all my particles - // map my particle charge onto my local 3d density grid - - particle_map(); - make_rho(); - - // all procs communicate density values from their ghost cells - // to fully sum contribution in their 3d bricks - // remap from 3d decomposition to FFT decomposition - - cg->reverse_comm(this,REVERSE_RHO); - brick2fft(); - - // compute potential gradient on my FFT grid and - // portion of e_long on this proc's FFT grid - // return gradients (electric fields) in 3d brick decomposition - // also performs per-atom calculations via poisson_peratom() - - poisson(); - - // all procs communicate E-field values - // to fill ghost cells surrounding their 3d bricks - - if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); - else cg->forward_comm(this,FORWARD_IK); - - // extra per-atom energy/virial communication - - if (evflag_atom) { - if (differentiation_flag == 1 && vflag_atom) - cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); - else if (differentiation_flag == 0) - cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); - } - - // calculate the force on my particles - - fieldforce(); - - // extra per-atom energy/virial communication - - if (evflag_atom) fieldforce_peratom(); - - // sum global energy across procs and add in volume-dependent term - - const double qscale = force->qqrd2e * scale; - - if (eflag_global) { - double energy_all; - MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); - energy = energy_all; - - energy *= 0.5*volume; - energy -= g_ewald*qsqsum/MY_PIS + - MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); - energy *= qscale; - } - - // sum global virial across procs - - if (vflag_global) { - double virial_all[6]; - MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); - for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]; - } - - // per-atom energy/virial - // energy includes self-energy correction - // notal accounts for TIP4P tallying eatom/vatom for ghost atoms - - if (evflag_atom) { - double *q = atom->q; - int nlocal = atom->nlocal; - int ntotal = nlocal; - if (tip4pflag) ntotal += atom->nghost; - - if (eflag_atom) { - for (i = 0; i < nlocal; i++) { - eatom[i] *= 0.5; - eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / - (g_ewald*g_ewald*volume); - eatom[i] *= qscale; - } - for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; - } - - if (vflag_atom) { - for (i = 0; i < ntotal; i++) - for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; - } - } - - // 2d slab correction - - if (slabflag == 1) slabcorr(); - - // convert atoms back from lamda to box coords - - if (triclinic) domain->lamda2x(atom->nlocal); -} - -/* ---------------------------------------------------------------------- - allocate memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::allocate() -{ - memory->create3d_offset(density_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:density_brick"); - - memory->create(density_fft,nfft_both,"pppm:density_fft"); - memory->create(greensfn,nfft_both,"pppm:greensfn"); - memory->create(work1,2*nfft_both,"pppm:work1"); - memory->create(work2,2*nfft_both,"pppm:work2"); - memory->create(vg,nfft_both,6,"pppm:vg"); - - if (triclinic == 0) { - memory->create1d_offset(fkx,nxlo_fft,nxhi_fft,"pppm:fkx"); - memory->create1d_offset(fky,nylo_fft,nyhi_fft,"pppm:fky"); - memory->create1d_offset(fkz,nzlo_fft,nzhi_fft,"pppm:fkz"); - } else { - memory->create(fkx,nfft_both,"pppm:fkx"); - memory->create(fky,nfft_both,"pppm:fky"); - memory->create(fkz,nfft_both,"pppm:fkz"); - } - - if (differentiation_flag == 1) { - memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:u_brick"); - - memory->create(sf_precoeff1,nfft_both,"pppm:sf_precoeff1"); - memory->create(sf_precoeff2,nfft_both,"pppm:sf_precoeff2"); - memory->create(sf_precoeff3,nfft_both,"pppm:sf_precoeff3"); - memory->create(sf_precoeff4,nfft_both,"pppm:sf_precoeff4"); - memory->create(sf_precoeff5,nfft_both,"pppm:sf_precoeff5"); - memory->create(sf_precoeff6,nfft_both,"pppm:sf_precoeff6"); - - } else { - memory->create3d_offset(vdx_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:vdx_brick"); - memory->create3d_offset(vdy_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:vdy_brick"); - memory->create3d_offset(vdz_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:vdz_brick"); - } - - // summation coeffs - - if (!stagger_flag) memory->create(gf_b,order,"pppm:gf_b"); - memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); - memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); - memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); - memory->create2d_offset(drho_coeff,order,(1-order)/2,order/2, - "pppm:drho_coeff"); - - // create 2 FFTs and a Remap - // 1st FFT keeps data in FFT decompostion - // 2nd FFT returns data in 3d brick decomposition - // remap takes data from 3d brick to FFT decomposition - - int tmp; - - fft1 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - 0,0,&tmp); - - fft2 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - 0,0,&tmp); - - remap = new Remap(lmp,world, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, - 1,0,0,FFT_PRECISION); - - // create ghost grid object for rho and electric field communication - - int (*procneigh)[2] = comm->procneigh; - - if (differentiation_flag == 1) - cg = new CommGrid(lmp,world,1,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); - else - cg = new CommGrid(lmp,world,3,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); -} - -/* ---------------------------------------------------------------------- - deallocate memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::deallocate() -{ - memory->destroy3d_offset(density_brick,nzlo_out,nylo_out,nxlo_out); - - if (differentiation_flag == 1) { - memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy(sf_precoeff1); - memory->destroy(sf_precoeff2); - memory->destroy(sf_precoeff3); - memory->destroy(sf_precoeff4); - memory->destroy(sf_precoeff5); - memory->destroy(sf_precoeff6); - } else { - memory->destroy3d_offset(vdx_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(vdy_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(vdz_brick,nzlo_out,nylo_out,nxlo_out); - } - - memory->destroy(density_fft); - memory->destroy(greensfn); - memory->destroy(work1); - memory->destroy(work2); - memory->destroy(vg); - - if (triclinic == 0) { - memory->destroy1d_offset(fkx,nxlo_fft); - memory->destroy1d_offset(fky,nylo_fft); - memory->destroy1d_offset(fkz,nzlo_fft); - } else { - memory->destroy(fkx); - memory->destroy(fky); - memory->destroy(fkz); - } - - memory->destroy(gf_b); - if (stagger_flag) gf_b = NULL; - memory->destroy2d_offset(rho1d,-order/2); - memory->destroy2d_offset(drho1d,-order/2); - memory->destroy2d_offset(rho_coeff,(1-order)/2); - memory->destroy2d_offset(drho_coeff,(1-order)/2); - - delete fft1; - delete fft2; - delete remap; - delete cg; -} - -/* ---------------------------------------------------------------------- - allocate per-atom memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::allocate_peratom() -{ - peratom_allocate_flag = 1; - - if (differentiation_flag != 1) - memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:u_brick"); - - memory->create3d_offset(v0_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v0_brick"); - - memory->create3d_offset(v1_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v1_brick"); - memory->create3d_offset(v2_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v2_brick"); - memory->create3d_offset(v3_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v3_brick"); - memory->create3d_offset(v4_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v4_brick"); - memory->create3d_offset(v5_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:v5_brick"); - - // create ghost grid object for rho and electric field communication - - int (*procneigh)[2] = comm->procneigh; - - if (differentiation_flag == 1) - cg_peratom = - new CommGrid(lmp,world,6,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); - else - cg_peratom = - new CommGrid(lmp,world,7,1, - nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, - nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, - procneigh[0][0],procneigh[0][1],procneigh[1][0], - procneigh[1][1],procneigh[2][0],procneigh[2][1]); -} - -/* ---------------------------------------------------------------------- - deallocate per-atom memory that depends on # of K-vectors and order -------------------------------------------------------------------------- */ - -void PPPM::deallocate_peratom() -{ - peratom_allocate_flag = 0; - - memory->destroy3d_offset(v0_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v1_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v2_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v3_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v4_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(v5_brick,nzlo_out,nylo_out,nxlo_out); - - if (differentiation_flag != 1) - memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); - - delete cg_peratom; -} - -/* ---------------------------------------------------------------------- - set global size of PPPM grid = nx,ny,nz_pppm - used for charge accumulation, FFTs, and electric field interpolation -------------------------------------------------------------------------- */ - -void PPPM::set_grid_global() -{ - // use xprd,yprd,zprd (even if triclinic, and then scale later) - // adjust z dimension for 2d slab PPPM - // 3d PPPM just uses zprd since slab_volfactor = 1.0 - - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - double zprd_slab = zprd*slab_volfactor; - - // make initial g_ewald estimate - // based on desired accuracy and real space cutoff - // fluid-occupied volume used to estimate real-space error - // zprd used rather than zprd_slab - - double h; - bigint natoms = atom->natoms; - - if (!gewaldflag) { - if (accuracy <= 0.0) - error->all(FLERR,"KSpace accuracy must be > 0"); - g_ewald = accuracy*sqrt(natoms*cutoff*xprd*yprd*zprd) / (2.0*q2); - if (g_ewald >= 1.0) g_ewald = (1.35 - 0.15*log(accuracy))/cutoff; - else g_ewald = sqrt(-log(g_ewald)) / cutoff; - } - - // set optimal nx_pppm,ny_pppm,nz_pppm based on order and accuracy - // nz_pppm uses extended zprd_slab instead of zprd - // reduce it until accuracy target is met - - if (!gridflag) { - - if (differentiation_flag == 1 || stagger_flag) { - - h = h_x = h_y = h_z = 4.0/g_ewald; - int count = 0; - while (1) { - - // set grid dimension - nx_pppm = static_cast (xprd/h_x); - ny_pppm = static_cast (yprd/h_y); - nz_pppm = static_cast (zprd_slab/h_z); - - if (nx_pppm <= 1) nx_pppm = 2; - if (ny_pppm <= 1) ny_pppm = 2; - if (nz_pppm <= 1) nz_pppm = 2; - - //set local grid dimension - int npey_fft,npez_fft; - if (nz_pppm >= nprocs) { - npey_fft = 1; - npez_fft = nprocs; - } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); - - int me_y = me % npey_fft; - int me_z = me / npey_fft; - - nxlo_fft = 0; - nxhi_fft = nx_pppm - 1; - nylo_fft = me_y*ny_pppm/npey_fft; - nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; - nzlo_fft = me_z*nz_pppm/npez_fft; - nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; - - double df_kspace = compute_df_kspace(); - - count++; - - // break loop if the accuracy has been reached or - // too many loops have been performed - - if (df_kspace <= accuracy) break; - if (count > 500) error->all(FLERR, "Could not compute grid size"); - h *= 0.95; - h_x = h_y = h_z = h; - } - - } else { - - double err; - h_x = h_y = h_z = 1.0/g_ewald; - - nx_pppm = static_cast (xprd/h_x) + 1; - ny_pppm = static_cast (yprd/h_y) + 1; - nz_pppm = static_cast (zprd_slab/h_z) + 1; - - err = estimate_ik_error(h_x,xprd,natoms); - while (err > accuracy) { - err = estimate_ik_error(h_x,xprd,natoms); - nx_pppm++; - h_x = xprd/nx_pppm; - } - - err = estimate_ik_error(h_y,yprd,natoms); - while (err > accuracy) { - err = estimate_ik_error(h_y,yprd,natoms); - ny_pppm++; - h_y = yprd/ny_pppm; - } - - err = estimate_ik_error(h_z,zprd_slab,natoms); - while (err > accuracy) { - err = estimate_ik_error(h_z,zprd_slab,natoms); - nz_pppm++; - h_z = zprd_slab/nz_pppm; - } - } - - // scale grid for triclinic skew - - if (triclinic) { - double tmp[3]; - tmp[0] = nx_pppm/xprd; - tmp[1] = ny_pppm/yprd; - tmp[2] = nz_pppm/zprd; - lamda2xT(&tmp[0],&tmp[0]); - nx_pppm = static_cast(tmp[0]) + 1; - ny_pppm = static_cast(tmp[1]) + 1; - nz_pppm = static_cast(tmp[2]) + 1; - } - } - - // boost grid size until it is factorable - - while (!factorable(nx_pppm)) nx_pppm++; - while (!factorable(ny_pppm)) ny_pppm++; - while (!factorable(nz_pppm)) nz_pppm++; - - if (triclinic == 0) { - h_x = xprd/nx_pppm; - h_y = yprd/ny_pppm; - h_z = zprd_slab/nz_pppm; - } else { - double tmp[3]; - tmp[0] = nx_pppm; - tmp[1] = ny_pppm; - tmp[2] = nz_pppm; - x2lamdaT(&tmp[0],&tmp[0]); - h_x = 1.0/tmp[0]; - h_y = 1.0/tmp[1]; - h_z = 1.0/tmp[2]; - } - - if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET) - error->all(FLERR,"PPPM grid is too large"); -} - -/* ---------------------------------------------------------------------- - check if all factors of n are in list of factors - return 1 if yes, 0 if no -------------------------------------------------------------------------- */ - -int PPPM::factorable(int n) -{ - int i; - - while (n > 1) { - for (i = 0; i < nfactors; i++) { - if (n % factors[i] == 0) { - n /= factors[i]; - break; - } - } - if (i == nfactors) return 0; - } - - return 1; -} - -/* ---------------------------------------------------------------------- - compute estimated kspace force error -------------------------------------------------------------------------- */ - -double PPPM::compute_df_kspace() -{ - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - double zprd_slab = zprd*slab_volfactor; - bigint natoms = atom->natoms; - double df_kspace = 0.0; - if (differentiation_flag == 1 || stagger_flag) { - double qopt = compute_qopt(); - df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab); - } else { - double lprx = estimate_ik_error(h_x,xprd,natoms); - double lpry = estimate_ik_error(h_y,yprd,natoms); - double lprz = estimate_ik_error(h_z,zprd_slab,natoms); - df_kspace = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); - } - return df_kspace; -} - -/* ---------------------------------------------------------------------- - compute qopt -------------------------------------------------------------------------- */ - -double PPPM::compute_qopt() -{ - double qopt = 0.0; - double *prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - volume = xprd * yprd * zprd_slab; - - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double u1, u2, sqk; - double sum1,sum2,sum3,sum4,dot2; - - int k,l,m,nx,ny,nz; - const int twoorder = 2*order; - - for (m = nzlo_fft; m <= nzhi_fft; m++) { - const int mper = m - nz_pppm*(2*m/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - const int lper = l - ny_pppm*(2*l/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - const int kper = k - nx_pppm*(2*k/nx_pppm); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - - sum1 = 0.0; - sum2 = 0.0; - sum3 = 0.0; - sum4 = 0.0; - for (nx = -2; nx <= 2; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - qx *= qx; - - for (ny = -2; ny <= 2; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - qy *= qy; - - for (nz = -2; nz <= 2; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - qz *= qz; - - dot2 = qx+qy+qz; - u1 = sx*sy*sz; - u2 = wx*wy*wz; - sum1 += u1*u1/dot2*MY_4PI*MY_4PI; - sum2 += u1 * u2 * MY_4PI; - sum3 += u2; - sum4 += dot2*u2; - } - } - } - sum2 *= sum2; - qopt += sum1 - sum2/(sum3*sum4); - } - } - } - } - double qopt_all; - MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); - return qopt_all; -} - -/* ---------------------------------------------------------------------- - estimate kspace force error for ik method -------------------------------------------------------------------------- */ - -double PPPM::estimate_ik_error(double h, double prd, bigint natoms) -{ - double sum = 0.0; - for (int m = 0; m < order; m++) - sum += acons[order][m] * pow(h*g_ewald,2.0*m); - double value = q2 * pow(h*g_ewald,(double)order) * - sqrt(g_ewald*prd*sqrt(MY_2PI)*sum/natoms) / (prd*prd); - - return value; -} - -/* ---------------------------------------------------------------------- - adjust the g_ewald parameter to near its optimal value - using a Newton-Raphson solver -------------------------------------------------------------------------- */ - -void PPPM::adjust_gewald() -{ - double dx; - - for (int i = 0; i < LARGE; i++) { - dx = newton_raphson_f() / derivf(); - g_ewald -= dx; - if (fabs(newton_raphson_f()) < SMALL) return; - } - - char str[128]; - sprintf(str, "Could not compute g_ewald"); - error->all(FLERR, str); -} - -/* ---------------------------------------------------------------------- - Calculate f(x) using Newton-Raphson solver - ------------------------------------------------------------------------- */ - -double PPPM::newton_raphson_f() -{ - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - bigint natoms = atom->natoms; - - double df_rspace = 2.0*q2*exp(-g_ewald*g_ewald*cutoff*cutoff) / - sqrt(natoms*cutoff*xprd*yprd*zprd); - - double df_kspace = compute_df_kspace(); - - return df_rspace - df_kspace; -} - -/* ---------------------------------------------------------------------- - Calculate numerical derivative f'(x) using forward difference - [f(x + h) - f(x)] / h - ------------------------------------------------------------------------- */ - -double PPPM::derivf() -{ - double h = 0.000001; //Derivative step-size - double df,f1,f2,g_ewald_old; - - f1 = newton_raphson_f(); - g_ewald_old = g_ewald; - g_ewald += h; - f2 = newton_raphson_f(); - g_ewald = g_ewald_old; - df = (f2 - f1)/h; - - return df; -} - -/* ---------------------------------------------------------------------- - Calculate the final estimate of the accuracy -------------------------------------------------------------------------- */ - -double PPPM::final_accuracy() -{ - double xprd = domain->xprd; - double yprd = domain->yprd; - double zprd = domain->zprd; - double zprd_slab = zprd*slab_volfactor; - bigint natoms = atom->natoms; - - double df_kspace = compute_df_kspace(); - double q2_over_sqrt = q2 / sqrt(natoms*cutoff*xprd*yprd*zprd); - double df_rspace = 2.0 * q2_over_sqrt * exp(-g_ewald*g_ewald*cutoff*cutoff); - double df_table = estimate_table_accuracy(q2_over_sqrt,df_rspace); - double estimated_accuracy = sqrt(df_kspace*df_kspace + df_rspace*df_rspace + - df_table*df_table); - - return estimated_accuracy; -} - -/* ---------------------------------------------------------------------- - set local subset of PPPM/FFT grid that I own - n xyz lo/hi in = 3d brick that I own (inclusive) - n xyz lo/hi out = 3d brick + ghost cells in 6 directions (inclusive) - n xyz lo/hi fft = FFT columns that I own (all of x dim, 2d decomp in yz) -------------------------------------------------------------------------- */ - -void PPPM::set_grid_local() -{ - // global indices of PPPM grid range from 0 to N-1 - // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of - // global PPPM grid that I own without ghost cells - // for slab PPPM, assign z grid as if it were not extended - - nxlo_in = static_cast (comm->xsplit[comm->myloc[0]] * nx_pppm); - nxhi_in = static_cast (comm->xsplit[comm->myloc[0]+1] * nx_pppm) - 1; - - nylo_in = static_cast (comm->ysplit[comm->myloc[1]] * ny_pppm); - nyhi_in = static_cast (comm->ysplit[comm->myloc[1]+1] * ny_pppm) - 1; - - nzlo_in = static_cast - (comm->zsplit[comm->myloc[2]] * nz_pppm/slab_volfactor); - nzhi_in = static_cast - (comm->zsplit[comm->myloc[2]+1] * nz_pppm/slab_volfactor) - 1; - - // nlower,nupper = stencil size for mapping particles to PPPM grid - - nlower = -(order-1)/2; - nupper = order/2; - - // shift values for particle <-> grid mapping - // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 - - if (order % 2) shift = OFFSET + 0.5; - else shift = OFFSET; - if (order % 2) shiftone = 0.0; - else shiftone = 0.5; - - // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of - // global PPPM grid that my particles can contribute charge to - // effectively nlo_in,nhi_in + ghost cells - // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest - // position a particle in my box can be at - // dist[3] = particle position bound = subbox + skin/2.0 + qdist - // qdist = offset due to TIP4P fictitious charge - // convert to triclinic if necessary - // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping - // for slab PPPM, assign z grid as if it were not extended - - double *prd,*sublo,*subhi; - - if (triclinic == 0) { - prd = domain->prd; - boxlo = domain->boxlo; - sublo = domain->sublo; - subhi = domain->subhi; - } else { - prd = domain->prd_lamda; - boxlo = domain->boxlo_lamda; - sublo = domain->sublo_lamda; - subhi = domain->subhi_lamda; - } - - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - double zprd_slab = zprd*slab_volfactor; - - double dist[3]; - double cuthalf = 0.5*neighbor->skin + qdist; - if (triclinic == 0) dist[0] = dist[1] = dist[2] = cuthalf; - else kspacebbox(cuthalf,&dist[0]); - - int nlo,nhi; - - nlo = static_cast ((sublo[0]-dist[0]-boxlo[0]) * - nx_pppm/xprd + shift) - OFFSET; - nhi = static_cast ((subhi[0]+dist[0]-boxlo[0]) * - nx_pppm/xprd + shift) - OFFSET; - nxlo_out = nlo + nlower; - nxhi_out = nhi + nupper; - - nlo = static_cast ((sublo[1]-dist[1]-boxlo[1]) * - ny_pppm/yprd + shift) - OFFSET; - nhi = static_cast ((subhi[1]+dist[1]-boxlo[1]) * - ny_pppm/yprd + shift) - OFFSET; - nylo_out = nlo + nlower; - nyhi_out = nhi + nupper; - - nlo = static_cast ((sublo[2]-dist[2]-boxlo[2]) * - nz_pppm/zprd_slab + shift) - OFFSET; - nhi = static_cast ((subhi[2]+dist[2]-boxlo[2]) * - nz_pppm/zprd_slab + shift) - OFFSET; - nzlo_out = nlo + nlower; - nzhi_out = nhi + nupper; - - if (stagger_flag) { - nxhi_out++; - nyhi_out++; - nzhi_out++; - } - - // for slab PPPM, change the grid boundary for processors at +z end - // to include the empty volume between periodically repeating slabs - // for slab PPPM, want charge data communicated from -z proc to +z proc, - // but not vice versa, also want field data communicated from +z proc to - // -z proc, but not vice versa - // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells) - // also insure no other procs use ghost cells beyond +z limit - - if (slabflag) { - if (comm->myloc[2] == comm->procgrid[2]-1) - nzhi_in = nzhi_out = nz_pppm - 1; - nzhi_out = MIN(nzhi_out,nz_pppm-1); - } - - // decomposition of FFT mesh - // global indices range from 0 to N-1 - // proc owns entire x-dimension, clumps of columns in y,z dimensions - // npey_fft,npez_fft = # of procs in y,z dims - // if nprocs is small enough, proc can own 1 or more entire xy planes, - // else proc owns 2d sub-blocks of yz plane - // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions - // nlo_fft,nhi_fft = lower/upper limit of the section - // of the global FFT mesh that I own - - int npey_fft,npez_fft; - if (nz_pppm >= nprocs) { - npey_fft = 1; - npez_fft = nprocs; - } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); - - int me_y = me % npey_fft; - int me_z = me / npey_fft; - - nxlo_fft = 0; - nxhi_fft = nx_pppm - 1; - nylo_fft = me_y*ny_pppm/npey_fft; - nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; - nzlo_fft = me_z*nz_pppm/npez_fft; - nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; - - // PPPM grid pts owned by this proc, including ghosts - - ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * - (nzhi_out-nzlo_out+1); - - // FFT grids owned by this proc, without ghosts - // nfft = FFT points in FFT decomposition on this proc - // nfft_brick = FFT points in 3d brick-decomposition on this proc - // nfft_both = greater of 2 values - - nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) * - (nzhi_fft-nzlo_fft+1); - int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) * - (nzhi_in-nzlo_in+1); - nfft_both = MAX(nfft,nfft_brick); -} - -/* ---------------------------------------------------------------------- - pre-compute Green's function denominator expansion coeffs, Gamma(2n) -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_denom() -{ - int k,l,m; - - for (l = 1; l < order; l++) gf_b[l] = 0.0; - gf_b[0] = 1.0; - - for (m = 1; m < order; m++) { - for (l = m; l > 0; l--) - gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); - gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); - } - - bigint ifact = 1; - for (k = 1; k < 2*order; k++) ifact *= k; - double gaminv = 1.0/ifact; - for (l = 0; l < order; l++) gf_b[l] *= gaminv; -} - -/* ---------------------------------------------------------------------- - pre-compute modified (Hockney-Eastwood) Coulomb Green's function -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_ik() -{ - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,dot1,dot2; - double numerator,denominator; - double sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * - pow(-log(EPS_HOC),0.25)); - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); - - sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); - - if (sqk != 0.0) { - numerator = 12.5663706/sqk; - denominator = gf_denom(snx,sny,snz); - sum1 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - qx = unitkx*(kper+nx_pppm*nx); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - qy = unitky*(lper+ny_pppm*ny); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - qz = unitkz*(mper+nz_pppm*nz); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; - dot2 = qx*qx+qy*qy+qz*qz; - sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; - } - } - } - greensfn[n++] = numerator*sum1/denominator; - } else greensfn[n++] = 0.0; - } - } - } -} - -/* ---------------------------------------------------------------------- - pre-compute modified (Hockney-Eastwood) Coulomb Green's function - for a triclinic system -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_ik_triclinic() -{ - double snx,sny,snz; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double sum1,dot1,dot2; - double numerator,denominator; - double sqk; - - int k,l,m,n,nx,ny,nz,kper,lper,mper; - - double tmp[3]; - tmp[0] = (g_ewald/(MY_PI*nx_pppm)) * pow(-log(EPS_HOC),0.25); - tmp[1] = (g_ewald/(MY_PI*ny_pppm)) * pow(-log(EPS_HOC),0.25); - tmp[2] = (g_ewald/(MY_PI*nz_pppm)) * pow(-log(EPS_HOC),0.25); - lamda2xT(&tmp[0],&tmp[0]); - const int nbx = static_cast (tmp[0]); - const int nby = static_cast (tmp[1]); - const int nbz = static_cast (tmp[2]); - - const int twoorder = 2*order; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - snz = square(sin(MY_PI*mper/nz_pppm)); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - sny = square(sin(MY_PI*lper/ny_pppm)); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - snx = square(sin(MY_PI*kper/nx_pppm)); - - double unitk_lamda[3]; - unitk_lamda[0] = 2.0*MY_PI*kper; - unitk_lamda[1] = 2.0*MY_PI*lper; - unitk_lamda[2] = 2.0*MY_PI*mper; - x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); - - sqk = square(unitk_lamda[0]) + square(unitk_lamda[1]) + square(unitk_lamda[2]); - - if (sqk != 0.0) { - numerator = 12.5663706/sqk; - denominator = gf_denom(snx,sny,snz); - sum1 = 0.0; - - for (nx = -nbx; nx <= nbx; nx++) { - argx = MY_PI*kper/nx_pppm + MY_PI*nx; - wx = powsinxx(argx,twoorder); - - for (ny = -nby; ny <= nby; ny++) { - argy = MY_PI*lper/ny_pppm + MY_PI*ny; - wy = powsinxx(argy,twoorder); - - for (nz = -nbz; nz <= nbz; nz++) { - argz = MY_PI*mper/nz_pppm + MY_PI*nz; - wz = powsinxx(argz,twoorder); - - double b[3]; - b[0] = 2.0*MY_PI*nx_pppm*nx; - b[1] = 2.0*MY_PI*ny_pppm*ny; - b[2] = 2.0*MY_PI*nz_pppm*nz; - x2lamdaT(&b[0],&b[0]); - - qx = unitk_lamda[0]+b[0]; - sx = exp(-0.25*square(qx/g_ewald)); - - qy = unitk_lamda[1]+b[1]; - sy = exp(-0.25*square(qy/g_ewald)); - - qz = unitk_lamda[2]+b[2]; - sz = exp(-0.25*square(qz/g_ewald)); - - dot1 = unitk_lamda[0]*qx + unitk_lamda[1]*qy + unitk_lamda[2]*qz; - dot2 = qx*qx+qy*qy+qz*qz; - sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; - } - } - } - greensfn[n++] = numerator*sum1/denominator; - } else greensfn[n++] = 0.0; - } - } - } -} - -/* ---------------------------------------------------------------------- - compute optimized Green's function for energy calculation -------------------------------------------------------------------------- */ - -void PPPM::compute_gf_ad() -{ - const double * const prd = domain->prd; - - const double xprd = prd[0]; - const double yprd = prd[1]; - const double zprd = prd[2]; - const double zprd_slab = zprd*slab_volfactor; - const double unitkx = (MY_2PI/xprd); - const double unitky = (MY_2PI/yprd); - const double unitkz = (MY_2PI/zprd_slab); - - double snx,sny,snz,sqk; - double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; - double numerator,denominator; - int k,l,m,n,kper,lper,mper; - - const int twoorder = 2*order; - - for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - qz = unitkz*mper; - snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); - sz = exp(-0.25*square(qz/g_ewald)); - argz = 0.5*qz*zprd_slab/nz_pppm; - wz = powsinxx(argz,twoorder); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - qy = unitky*lper; - sny = square(sin(0.5*qy*yprd/ny_pppm)); - sy = exp(-0.25*square(qy/g_ewald)); - argy = 0.5*qy*yprd/ny_pppm; - wy = powsinxx(argy,twoorder); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - qx = unitkx*kper; - snx = square(sin(0.5*qx*xprd/nx_pppm)); - sx = exp(-0.25*square(qx/g_ewald)); - argx = 0.5*qx*xprd/nx_pppm; - wx = powsinxx(argx,twoorder); - - sqk = qx*qx + qy*qy + qz*qz; - - if (sqk != 0.0) { - numerator = MY_4PI/sqk; - denominator = gf_denom(snx,sny,snz); - greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; - sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; - sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; - sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; - sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; - sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; - sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; - n++; - } else { - greensfn[n] = 0.0; - sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; - sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; - sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; - sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; - sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; - sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; - n++; - } - } - } - } - - // compute the coefficients for the self-force correction - - double prex, prey, prez; - prex = prey = prez = MY_PI/volume; - prex *= nx_pppm/xprd; - prey *= ny_pppm/yprd; - prez *= nz_pppm/zprd_slab; - sf_coeff[0] *= prex; - sf_coeff[1] *= prex*2; - sf_coeff[2] *= prey; - sf_coeff[3] *= prey*2; - sf_coeff[4] *= prez; - sf_coeff[5] *= prez*2; - - // communicate values with other procs - - double tmp[6]; - MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); - for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; -} - -/* ---------------------------------------------------------------------- - compute self force coefficients for ad-differentiation scheme -------------------------------------------------------------------------- */ - -void PPPM::compute_sf_precoeff() -{ - int i,k,l,m,n; - int nx,ny,nz,kper,lper,mper; - double wx0[5],wy0[5],wz0[5],wx1[5],wy1[5],wz1[5],wx2[5],wy2[5],wz2[5]; - double qx0,qy0,qz0,qx1,qy1,qz1,qx2,qy2,qz2; - double u0,u1,u2,u3,u4,u5,u6; - double sum1,sum2,sum3,sum4,sum5,sum6; - - n = 0; - for (m = nzlo_fft; m <= nzhi_fft; m++) { - mper = m - nz_pppm*(2*m/nz_pppm); - - for (l = nylo_fft; l <= nyhi_fft; l++) { - lper = l - ny_pppm*(2*l/ny_pppm); - - for (k = nxlo_fft; k <= nxhi_fft; k++) { - kper = k - nx_pppm*(2*k/nx_pppm); - - sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0.0; - for (i = 0; i < 5; i++) { - - qx0 = MY_2PI*(kper+nx_pppm*(i-2)); - qx1 = MY_2PI*(kper+nx_pppm*(i-1)); - qx2 = MY_2PI*(kper+nx_pppm*(i )); - wx0[i] = powsinxx(0.5*qx0/nx_pppm,order); - wx1[i] = powsinxx(0.5*qx1/nx_pppm,order); - wx2[i] = powsinxx(0.5*qx2/nx_pppm,order); - - qy0 = MY_2PI*(lper+ny_pppm*(i-2)); - qy1 = MY_2PI*(lper+ny_pppm*(i-1)); - qy2 = MY_2PI*(lper+ny_pppm*(i )); - wy0[i] = powsinxx(0.5*qy0/ny_pppm,order); - wy1[i] = powsinxx(0.5*qy1/ny_pppm,order); - wy2[i] = powsinxx(0.5*qy2/ny_pppm,order); - - qz0 = MY_2PI*(mper+nz_pppm*(i-2)); - qz1 = MY_2PI*(mper+nz_pppm*(i-1)); - qz2 = MY_2PI*(mper+nz_pppm*(i )); - - wz0[i] = powsinxx(0.5*qz0/nz_pppm,order); - wz1[i] = powsinxx(0.5*qz1/nz_pppm,order); - wz2[i] = powsinxx(0.5*qz2/nz_pppm,order); - } - - for (nx = 0; nx < 5; nx++) { - for (ny = 0; ny < 5; ny++) { - for (nz = 0; nz < 5; nz++) { - u0 = wx0[nx]*wy0[ny]*wz0[nz]; - u1 = wx1[nx]*wy0[ny]*wz0[nz]; - u2 = wx2[nx]*wy0[ny]*wz0[nz]; - u3 = wx0[nx]*wy1[ny]*wz0[nz]; - u4 = wx0[nx]*wy2[ny]*wz0[nz]; - u5 = wx0[nx]*wy0[ny]*wz1[nz]; - u6 = wx0[nx]*wy0[ny]*wz2[nz]; - - sum1 += u0*u1; - sum2 += u0*u2; - sum3 += u0*u3; - sum4 += u0*u4; - sum5 += u0*u5; - sum6 += u0*u6; - } - } - } - - // store values - - sf_precoeff1[n] = sum1; - sf_precoeff2[n] = sum2; - sf_precoeff3[n] = sum3; - sf_precoeff4[n] = sum4; - sf_precoeff5[n] = sum5; - sf_precoeff6[n++] = sum6; - } - } - } -} - -/* ---------------------------------------------------------------------- - find center grid pt for each of my particles - check that full stencil for the particle will fit in my 3d brick - store central grid pt indices in part2grid array -------------------------------------------------------------------------- */ - -void PPPM::particle_map() -{ - int nx,ny,nz; - - double **x = atom->x; - int nlocal = atom->nlocal; - - int flag = 0; - for (int i = 0; i < nlocal; i++) { - - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // current particle coord can be outside global and local box - // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 - - nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift) - OFFSET; - ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift) - OFFSET; - nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift) - OFFSET; - - part2grid[i][0] = nx; - part2grid[i][1] = ny; - part2grid[i][2] = nz; - - // check that entire stencil around nx,ny,nz will fit in my 3d brick - - if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || - ny+nlower < nylo_out || ny+nupper > nyhi_out || - nz+nlower < nzlo_out || nz+nupper > nzhi_out) - flag = 1; - } - - if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); -} - -/* ---------------------------------------------------------------------- - create discretized "density" on section of global grid due to my particles - density(x,y,z) = charge "density" at grid points of my 3d brick - (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) - in global grid -------------------------------------------------------------------------- */ - -void PPPM::make_rho() -{ - int l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - - // clear 3d density array - - memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - // loop over my charges, add their contribution to nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - - for (int i = 0; i < nlocal; i++) { - - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - z0 = delvolinv * q[i]; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - y0 = z0*rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - x0 = y0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - density_brick[mz][my][mx] += x0*rho1d[0][l]; - } - } - } - } -} - -/* ---------------------------------------------------------------------- - remap density from 3d brick decomposition to FFT decomposition -------------------------------------------------------------------------- */ - -void PPPM::brick2fft() -{ - int n,ix,iy,iz; - - // copy grabs inner portion of density from 3d brick - // remap could be done as pre-stage of FFT, - // but this works optimally on only double values, not complex values - - n = 0; - for (iz = nzlo_in; iz <= nzhi_in; iz++) - for (iy = nylo_in; iy <= nyhi_in; iy++) - for (ix = nxlo_in; ix <= nxhi_in; ix++) - density_fft[n++] = density_brick[iz][iy][ix]; - - remap->perform(density_fft,density_fft,work1); -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver -------------------------------------------------------------------------- */ - -void PPPM::poisson() -{ - if (differentiation_flag == 1) poisson_ad(); - else poisson_ik(); -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for ik -------------------------------------------------------------------------- */ - -void PPPM::poisson_ik() -{ - int i,j,k,n; - double eng; - - // transform charge density (r -> k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] = density_fft[i]; - work1[n++] = ZEROF; - } - - fft1->compute(work1,work1,1); - - // global energy and virial contribution - - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); - double s2 = scaleinv*scaleinv; - - if (eflag_global || vflag_global) { - if (vflag_global) { - n = 0; - for (i = 0; i < nfft; i++) { - eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; - if (eflag_global) energy += eng; - n += 2; - } - } else { - n = 0; - for (i = 0; i < nfft; i++) { - energy += - s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - n += 2; - } - } - } - - // scale by 1/total-grid-pts to get rho(k) - // multiply by Green's function to get V(k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] *= scaleinv * greensfn[i]; - work1[n++] *= scaleinv * greensfn[i]; - } - - // extra FFTs for per-atom energy/virial - - if (evflag_atom) poisson_peratom(); - - // triclinic system - - if (triclinic) { - poisson_ik_triclinic(); - return; - } - - // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) - // FFT leaves data in 3d brick decomposition - // copy it into inner portion of vdx,vdy,vdz arrays - - // x direction gradient - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - work2[n] = fkx[i]*work1[n+1]; - work2[n+1] = -fkx[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdx_brick[k][j][i] = work2[n]; - n += 2; - } - - // y direction gradient - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - work2[n] = fky[j]*work1[n+1]; - work2[n+1] = -fky[j]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdy_brick[k][j][i] = work2[n]; - n += 2; - } - - // z direction gradient - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - work2[n] = fkz[k]*work1[n+1]; - work2[n+1] = -fkz[k]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdz_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for ik for a triclinic system -------------------------------------------------------------------------- */ - -void PPPM::poisson_ik_triclinic() -{ - int i,j,k,n; - - // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) - // FFT leaves data in 3d brick decomposition - // copy it into inner portion of vdx,vdy,vdz arrays - - // x direction gradient - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = fkx[i]*work1[n+1]; - work2[n+1] = -fkx[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdx_brick[k][j][i] = work2[n]; - n += 2; - } - - // y direction gradient - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = fky[i]*work1[n+1]; - work2[n+1] = -fky[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdy_brick[k][j][i] = work2[n]; - n += 2; - } - - // z direction gradient - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = fkz[i]*work1[n+1]; - work2[n+1] = -fkz[i]*work1[n]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - vdz_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for ad -------------------------------------------------------------------------- */ - -void PPPM::poisson_ad() -{ - int i,j,k,n; - double eng; - - // transform charge density (r -> k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] = density_fft[i]; - work1[n++] = ZEROF; - } - - fft1->compute(work1,work1,1); - - // global energy and virial contribution - - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); - double s2 = scaleinv*scaleinv; - - if (eflag_global || vflag_global) { - if (vflag_global) { - n = 0; - for (i = 0; i < nfft; i++) { - eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; - if (eflag_global) energy += eng; - n += 2; - } - } else { - n = 0; - for (i = 0; i < nfft; i++) { - energy += - s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); - n += 2; - } - } - } - - // scale by 1/total-grid-pts to get rho(k) - // multiply by Green's function to get V(k) - - n = 0; - for (i = 0; i < nfft; i++) { - work1[n++] *= scaleinv * greensfn[i]; - work1[n++] *= scaleinv * greensfn[i]; - } - - // extra FFTs for per-atom energy/virial - - if (vflag_atom) poisson_peratom(); - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]; - work2[n+1] = work1[n+1]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - u_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for per-atom energy/virial -------------------------------------------------------------------------- */ - -void PPPM::poisson_peratom() -{ - int i,j,k,n; - - // energy - - if (eflag_atom && differentiation_flag != 1) { - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]; - work2[n+1] = work1[n+1]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - u_brick[k][j][i] = work2[n]; - n += 2; - } - } - - // 6 components of virial in v0 thru v5 - - if (!vflag_atom) return; - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][0]; - work2[n+1] = work1[n+1]*vg[i][0]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v0_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][1]; - work2[n+1] = work1[n+1]*vg[i][1]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v1_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][2]; - work2[n+1] = work1[n+1]*vg[i][2]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v2_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][3]; - work2[n+1] = work1[n+1]*vg[i][3]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v3_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][4]; - work2[n+1] = work1[n+1]*vg[i][4]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v4_brick[k][j][i] = work2[n]; - n += 2; - } - - n = 0; - for (i = 0; i < nfft; i++) { - work2[n] = work1[n]*vg[i][5]; - work2[n+1] = work1[n+1]*vg[i][5]; - n += 2; - } - - fft2->compute(work2,work2,-1); - - n = 0; - for (k = nzlo_in; k <= nzhi_in; k++) - for (j = nylo_in; j <= nyhi_in; j++) - for (i = nxlo_in; i <= nxhi_in; i++) { - v5_brick[k][j][i] = work2[n]; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles -------------------------------------------------------------------------- */ - -void PPPM::fieldforce() -{ - if (differentiation_flag == 1) fieldforce_ad(); - else fieldforce_ik(); -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles for ik -------------------------------------------------------------------------- */ - -void PPPM::fieldforce_ik() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - FFT_SCALAR ekx,eky,ekz; - - // loop over my charges, interpolate electric field from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - // ek = 3 components of E-field on particle - - double *q = atom->q; - double **x = atom->x; - double **f = atom->f; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - ekx = eky = ekz = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - z0 = rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - y0 = z0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - x0 = y0*rho1d[0][l]; - ekx -= x0*vdx_brick[mz][my][mx]; - eky -= x0*vdy_brick[mz][my][mx]; - ekz -= x0*vdz_brick[mz][my][mx]; - } - } - } - - // convert E-field to force - - const double qfactor = force->qqrd2e * scale * q[i]; - f[i][0] += qfactor*ekx; - f[i][1] += qfactor*eky; - if (slabflag != 2) f[i][2] += qfactor*ekz; - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get electric field & force on my particles for ad -------------------------------------------------------------------------- */ - -void PPPM::fieldforce_ad() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz; - FFT_SCALAR ekx,eky,ekz; - double s1,s2,s3; - double sf = 0.0; - double *prd; - - prd = domain->prd; - double xprd = prd[0]; - double yprd = prd[1]; - double zprd = prd[2]; - - double hx_inv = nx_pppm/xprd; - double hy_inv = ny_pppm/yprd; - double hz_inv = nz_pppm/zprd; - - // loop over my charges, interpolate electric field from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - // ek = 3 components of E-field on particle - - double *q = atom->q; - double **x = atom->x; - double **f = atom->f; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - compute_drho1d(dx,dy,dz); - - ekx = eky = ekz = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; - eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; - ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; - } - } - } - ekx *= hx_inv; - eky *= hy_inv; - ekz *= hz_inv; - - // convert E-field to force and substract self forces - - const double qfactor = force->qqrd2e * scale; - - s1 = x[i][0]*hx_inv; - s2 = x[i][1]*hy_inv; - s3 = x[i][2]*hz_inv; - sf = sf_coeff[0]*sin(2*MY_PI*s1); - sf += sf_coeff[1]*sin(4*MY_PI*s1); - sf *= 2*q[i]*q[i]; - f[i][0] += qfactor*(ekx*q[i] - sf); - - sf = sf_coeff[2]*sin(2*MY_PI*s2); - sf += sf_coeff[3]*sin(4*MY_PI*s2); - sf *= 2*q[i]*q[i]; - f[i][1] += qfactor*(eky*q[i] - sf); - - - sf = sf_coeff[4]*sin(2*MY_PI*s3); - sf += sf_coeff[5]*sin(4*MY_PI*s3); - sf *= 2*q[i]*q[i]; - if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); - } -} - -/* ---------------------------------------------------------------------- - interpolate from grid to get per-atom energy/virial -------------------------------------------------------------------------- */ - -void PPPM::fieldforce_peratom() -{ - int i,l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - FFT_SCALAR u,v0,v1,v2,v3,v4,v5; - - // loop over my charges, interpolate from nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - - int nlocal = atom->nlocal; - - for (i = 0; i < nlocal; i++) { - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - z0 = rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - y0 = z0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - x0 = y0*rho1d[0][l]; - if (eflag_atom) u += x0*u_brick[mz][my][mx]; - if (vflag_atom) { - v0 += x0*v0_brick[mz][my][mx]; - v1 += x0*v1_brick[mz][my][mx]; - v2 += x0*v2_brick[mz][my][mx]; - v3 += x0*v3_brick[mz][my][mx]; - v4 += x0*v4_brick[mz][my][mx]; - v5 += x0*v5_brick[mz][my][mx]; - } - } - } - } - - if (eflag_atom) eatom[i] += q[i]*u; - if (vflag_atom) { - vatom[i][0] += q[i]*v0; - vatom[i][1] += q[i]*v1; - vatom[i][2] += q[i]*v2; - vatom[i][3] += q[i]*v3; - vatom[i][4] += q[i]*v4; - vatom[i][5] += q[i]*v5; - } - } -} - -/* ---------------------------------------------------------------------- - pack own values to buf to send to another proc -------------------------------------------------------------------------- */ - -void PPPM::pack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - int n = 0; - - if (flag == FORWARD_IK) { - FFT_SCALAR *xsrc = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *ysrc = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *zsrc = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - buf[n++] = xsrc[list[i]]; - buf[n++] = ysrc[list[i]]; - buf[n++] = zsrc[list[i]]; - } - } else if (flag == FORWARD_AD) { - FFT_SCALAR *src = &u_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - buf[i] = src[list[i]]; - } else if (flag == FORWARD_IK_PERATOM) { - FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - if (eflag_atom) buf[n++] = esrc[list[i]]; - if (vflag_atom) { - buf[n++] = v0src[list[i]]; - buf[n++] = v1src[list[i]]; - buf[n++] = v2src[list[i]]; - buf[n++] = v3src[list[i]]; - buf[n++] = v4src[list[i]]; - buf[n++] = v5src[list[i]]; - } - } - } else if (flag == FORWARD_AD_PERATOM) { - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - buf[n++] = v0src[list[i]]; - buf[n++] = v1src[list[i]]; - buf[n++] = v2src[list[i]]; - buf[n++] = v3src[list[i]]; - buf[n++] = v4src[list[i]]; - buf[n++] = v5src[list[i]]; - } - } -} - -/* ---------------------------------------------------------------------- - unpack another proc's own values from buf and set own ghost values -------------------------------------------------------------------------- */ - -void PPPM::unpack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - int n = 0; - - if (flag == FORWARD_IK) { - FFT_SCALAR *xdest = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *ydest = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *zdest = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - xdest[list[i]] = buf[n++]; - ydest[list[i]] = buf[n++]; - zdest[list[i]] = buf[n++]; - } - } else if (flag == FORWARD_AD) { - FFT_SCALAR *dest = &u_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - dest[list[i]] = buf[i]; - } else if (flag == FORWARD_IK_PERATOM) { - FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - if (eflag_atom) esrc[list[i]] = buf[n++]; - if (vflag_atom) { - v0src[list[i]] = buf[n++]; - v1src[list[i]] = buf[n++]; - v2src[list[i]] = buf[n++]; - v3src[list[i]] = buf[n++]; - v4src[list[i]] = buf[n++]; - v5src[list[i]] = buf[n++]; - } - } - } else if (flag == FORWARD_AD_PERATOM) { - FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; - FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) { - v0src[list[i]] = buf[n++]; - v1src[list[i]] = buf[n++]; - v2src[list[i]] = buf[n++]; - v3src[list[i]] = buf[n++]; - v4src[list[i]] = buf[n++]; - v5src[list[i]] = buf[n++]; - } - } -} - -/* ---------------------------------------------------------------------- - pack ghost values into buf to send to another proc -------------------------------------------------------------------------- */ - -void PPPM::pack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - if (flag == REVERSE_RHO) { - FFT_SCALAR *src = &density_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - buf[i] = src[list[i]]; - } -} - -/* ---------------------------------------------------------------------- - unpack another proc's ghost values from buf and add to own values -------------------------------------------------------------------------- */ - -void PPPM::unpack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) -{ - if (flag == REVERSE_RHO) { - FFT_SCALAR *dest = &density_brick[nzlo_out][nylo_out][nxlo_out]; - for (int i = 0; i < nlist; i++) - dest[list[i]] += buf[i]; - } -} - -/* ---------------------------------------------------------------------- - map nprocs to NX by NY grid as PX by PY procs - return optimal px,py -------------------------------------------------------------------------- */ - -void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py) -{ - // loop thru all possible factorizations of nprocs - // surf = surface area of largest proc sub-domain - // innermost if test minimizes surface area and surface/volume ratio - - int bestsurf = 2 * (nx + ny); - int bestboxx = 0; - int bestboxy = 0; - - int boxx,boxy,surf,ipx,ipy; - - ipx = 1; - while (ipx <= nprocs) { - if (nprocs % ipx == 0) { - ipy = nprocs/ipx; - boxx = nx/ipx; - if (nx % ipx) boxx++; - boxy = ny/ipy; - if (ny % ipy) boxy++; - surf = boxx + boxy; - if (surf < bestsurf || - (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) { - bestsurf = surf; - bestboxx = boxx; - bestboxy = boxy; - *px = ipx; - *py = ipy; - } - } - ipx++; - } -} - -/* ---------------------------------------------------------------------- - charge assignment into rho1d - dx,dy,dz = distance of particle from "lower left" grid point -------------------------------------------------------------------------- */ - -void PPPM::compute_rho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, - const FFT_SCALAR &dz) -{ - int k,l; - FFT_SCALAR r1,r2,r3; - - for (k = (1-order)/2; k <= order/2; k++) { - r1 = r2 = r3 = ZEROF; - - for (l = order-1; l >= 0; l--) { - r1 = rho_coeff[l][k] + r1*dx; - r2 = rho_coeff[l][k] + r2*dy; - r3 = rho_coeff[l][k] + r3*dz; - } - rho1d[0][k] = r1; - rho1d[1][k] = r2; - rho1d[2][k] = r3; - } -} - -/* ---------------------------------------------------------------------- - charge assignment into drho1d - dx,dy,dz = distance of particle from "lower left" grid point -------------------------------------------------------------------------- */ - -void PPPM::compute_drho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, - const FFT_SCALAR &dz) -{ - int k,l; - FFT_SCALAR r1,r2,r3; - - for (k = (1-order)/2; k <= order/2; k++) { - r1 = r2 = r3 = ZEROF; - - for (l = order-2; l >= 0; l--) { - r1 = drho_coeff[l][k] + r1*dx; - r2 = drho_coeff[l][k] + r2*dy; - r3 = drho_coeff[l][k] + r3*dz; - } - drho1d[0][k] = r1; - drho1d[1][k] = r2; - drho1d[2][k] = r3; - } -} - -/* ---------------------------------------------------------------------- - generate coeffients for the weight function of order n - - (n-1) - Wn(x) = Sum wn(k,x) , Sum is over every other integer - k=-(n-1) - For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1 - k is odd integers if n is even and even integers if n is odd - --- - | n-1 - | Sum a(l,j)*(x-k/2)**l if abs(x-k/2) < 1/2 - wn(k,x) = < l=0 - | - | 0 otherwise - --- - a coeffients are packed into the array rho_coeff to eliminate zeros - rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k) -------------------------------------------------------------------------- */ - -void PPPM::compute_rho_coeff() -{ - int j,k,l,m; - FFT_SCALAR s; - - FFT_SCALAR **a; - memory->create2d_offset(a,order,-order,order,"pppm:a"); - - for (k = -order; k <= order; k++) - for (l = 0; l < order; l++) - a[l][k] = 0.0; - - a[0][0] = 1.0; - for (j = 1; j < order; j++) { - for (k = -j; k <= j; k += 2) { - s = 0.0; - for (l = 0; l < j; l++) { - a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1); -#ifdef FFT_SINGLE - s += powf(0.5,(float) l+1) * - (a[l][k-1] + powf(-1.0,(float) l) * a[l][k+1]) / (l+1); -#else - s += pow(0.5,(double) l+1) * - (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1); -#endif - } - a[0][k] = s; - } - } - - m = (1-order)/2; - for (k = -(order-1); k < order; k += 2) { - for (l = 0; l < order; l++) - rho_coeff[l][m] = a[l][k]; - for (l = 1; l < order; l++) - drho_coeff[l-1][m] = l*a[l][k]; - m++; - } - - memory->destroy2d_offset(a,-order); -} - -/* ---------------------------------------------------------------------- - Slab-geometry correction term to dampen inter-slab interactions between - periodically repeating slabs. Yields good approximation to 2D Ewald if - adequate empty space is left between repeating slabs (J. Chem. Phys. - 111, 3155). Slabs defined here to be parallel to the xy plane. -------------------------------------------------------------------------- */ - -void PPPM::slabcorr() -{ - // compute local contribution to global dipole moment - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - - double dipole = 0.0; - for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; - - // sum local contributions to get global dipole moment - - double dipole_all; - MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); - - // compute corrections - - const double e_slabcorr = MY_2PI*dipole_all*dipole_all/volume; - const double qscale = force->qqrd2e * scale; - - if (eflag_global) energy += qscale * e_slabcorr; - - // per-atom energy - - if (eflag_atom) { - double efact = MY_2PI*dipole_all/volume; - for (int i = 0; i < nlocal; i++) eatom[i] += qscale * q[i]*x[i][2]*efact; - } - - // add on force corrections - - double ffact = -4.0*MY_PI*dipole_all/volume; - double **f = atom->f; - - for (int i = 0; i < nlocal; i++) f[i][2] += qscale * q[i]*ffact; -} - -/* ---------------------------------------------------------------------- - perform and time the 1d FFTs required for N timesteps -------------------------------------------------------------------------- */ - -int PPPM::timing_1d(int n, double &time1d) -{ - double time1,time2; - - for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; - - MPI_Barrier(world); - time1 = MPI_Wtime(); - - for (int i = 0; i < n; i++) { - fft1->timing1d(work1,nfft_both,1); - fft2->timing1d(work1,nfft_both,-1); - if (differentiation_flag != 1) { - fft2->timing1d(work1,nfft_both,-1); - fft2->timing1d(work1,nfft_both,-1); - } - } - - MPI_Barrier(world); - time2 = MPI_Wtime(); - time1d = time2 - time1; - - if (differentiation_flag) return 2; - return 4; -} - -/* ---------------------------------------------------------------------- - perform and time the 3d FFTs required for N timesteps -------------------------------------------------------------------------- */ - -int PPPM::timing_3d(int n, double &time3d) -{ - double time1,time2; - - for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; - - MPI_Barrier(world); - time1 = MPI_Wtime(); - - for (int i = 0; i < n; i++) { - fft1->compute(work1,work1,1); - fft2->compute(work1,work1,-1); - if (differentiation_flag != 1) { - fft2->compute(work1,work1,-1); - fft2->compute(work1,work1,-1); - } - } - - MPI_Barrier(world); - time2 = MPI_Wtime(); - time3d = time2 - time1; - - if (differentiation_flag) return 2; - return 4; -} - -/* ---------------------------------------------------------------------- - memory usage of local arrays -------------------------------------------------------------------------- */ - -double PPPM::memory_usage() -{ - double bytes = nmax*3 * sizeof(double); - int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * - (nzhi_out-nzlo_out+1); - if (differentiation_flag == 1) { - bytes += 2 * nbrick * sizeof(FFT_SCALAR); - } else { - bytes += 4 * nbrick * sizeof(FFT_SCALAR); - } - if (triclinic) bytes += 3 * nfft_both * sizeof(double); - bytes += 6 * nfft_both * sizeof(double); - bytes += nfft_both * sizeof(double); - bytes += nfft_both*5 * sizeof(FFT_SCALAR); - - if (peratom_allocate_flag) - bytes += 6 * nbrick * sizeof(FFT_SCALAR); - - if (group_allocate_flag) { - bytes += 2 * nbrick * sizeof(FFT_SCALAR); - bytes += 2 * nfft_both * sizeof(FFT_SCALAR);; - } - - bytes += cg->memory_usage(); - - return bytes; -} - -/* ---------------------------------------------------------------------- - group-group interactions - ------------------------------------------------------------------------- */ - -/* ---------------------------------------------------------------------- - compute the PPPM total long-range force and energy for groups A and B - ------------------------------------------------------------------------- */ - -void PPPM::compute_group_group(int groupbit_A, int groupbit_B, int BA_flag) -{ - if (slabflag) - error->all(FLERR,"Cannot (yet) use K-space slab " - "correction with compute group/group"); - - if (differentiation_flag) - error->all(FLERR,"Cannot (yet) use 'kspace_modify " - "diff ad' with compute group/group"); - - if (!group_allocate_flag) allocate_groups(); - - // convert atoms from box to lamda coords - - if (triclinic == 0) boxlo = domain->boxlo; - else { - boxlo = domain->boxlo_lamda; - domain->x2lamda(atom->nlocal); - } - - e2group = 0; //energy - f2group[0] = 0; //force in x-direction - f2group[1] = 0; //force in y-direction - f2group[2] = 0; //force in z-direction - - // map my particle charge onto my local 3d density grid - - make_rho_groups(groupbit_A,groupbit_B,BA_flag); - - // all procs communicate density values from their ghost cells - // to fully sum contribution in their 3d bricks - // remap from 3d decomposition to FFT decomposition - - // temporarily store and switch pointers so we can - // use brick2fft() for groups A and B (without - // writing an additional function) - - FFT_SCALAR ***density_brick_real = density_brick; - FFT_SCALAR *density_fft_real = density_fft; - - // group A - - density_brick = density_A_brick; - density_fft = density_A_fft; - - cg->reverse_comm(this,REVERSE_RHO); - brick2fft(); - - // group B - - density_brick = density_B_brick; - density_fft = density_B_fft; - - cg->reverse_comm(this,REVERSE_RHO); - brick2fft(); - - // switch back pointers - - density_brick = density_brick_real; - density_fft = density_fft_real; - - // compute potential gradient on my FFT grid and - // portion of group-group energy/force on this proc's FFT grid - - poisson_groups(BA_flag); - - const double qscale = force->qqrd2e * scale; - - // total group A <--> group B energy - // self and boundary correction terms are in compute_group_group.cpp - - double e2group_all; - MPI_Allreduce(&e2group,&e2group_all,1,MPI_DOUBLE,MPI_SUM,world); - e2group = e2group_all; - - e2group *= qscale*0.5*volume; - - // total group A <--> group B force - - double f2group_all[3]; - MPI_Allreduce(f2group,f2group_all,3,MPI_DOUBLE,MPI_SUM,world); - - for (int i = 0; i < 3; i++) f2group[i] = qscale*volume*f2group_all[i]; - - // convert atoms back from lamda to box coords - - if (triclinic) domain->lamda2x(atom->nlocal); -} - -/* ---------------------------------------------------------------------- - allocate group-group memory that depends on # of K-vectors and order - ------------------------------------------------------------------------- */ - -void PPPM::allocate_groups() -{ - group_allocate_flag = 1; - - memory->create3d_offset(density_A_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:density_A_brick"); - memory->create3d_offset(density_B_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, - nxlo_out,nxhi_out,"pppm:density_B_brick"); - memory->create(density_A_fft,nfft_both,"pppm:density_A_fft"); - memory->create(density_B_fft,nfft_both,"pppm:density_B_fft"); -} - -/* ---------------------------------------------------------------------- - deallocate group-group memory that depends on # of K-vectors and order - ------------------------------------------------------------------------- */ - -void PPPM::deallocate_groups() -{ - group_allocate_flag = 0; - - memory->destroy3d_offset(density_A_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy3d_offset(density_B_brick,nzlo_out,nylo_out,nxlo_out); - memory->destroy(density_A_fft); - memory->destroy(density_B_fft); -} - -/* ---------------------------------------------------------------------- - create discretized "density" on section of global grid due to my particles - density(x,y,z) = charge "density" at grid points of my 3d brick - (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) - in global grid for group-group interactions - ------------------------------------------------------------------------- */ - -void PPPM::make_rho_groups(int groupbit_A, int groupbit_B, int BA_flag) -{ - int l,m,n,nx,ny,nz,mx,my,mz; - FFT_SCALAR dx,dy,dz,x0,y0,z0; - - // clear 3d density arrays - - memset(&(density_A_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - memset(&(density_B_brick[nzlo_out][nylo_out][nxlo_out]),0, - ngrid*sizeof(FFT_SCALAR)); - - // loop over my charges, add their contribution to nearby grid points - // (nx,ny,nz) = global coords of grid pt to "lower left" of charge - // (dx,dy,dz) = distance to "lower left" grid pt - // (mx,my,mz) = global coords of moving stencil pt - - double *q = atom->q; - double **x = atom->x; - int nlocal = atom->nlocal; - int *mask = atom->mask; - - for (int i = 0; i < nlocal; i++) { - - if ((mask[i] & groupbit_A) && (mask[i] & groupbit_B)) - if (BA_flag) continue; - - if ((mask[i] & groupbit_A) || (mask[i] & groupbit_B)) { - - nx = part2grid[i][0]; - ny = part2grid[i][1]; - nz = part2grid[i][2]; - dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; - dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; - dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; - - compute_rho1d(dx,dy,dz); - - z0 = delvolinv * q[i]; - for (n = nlower; n <= nupper; n++) { - mz = n+nz; - y0 = z0*rho1d[2][n]; - for (m = nlower; m <= nupper; m++) { - my = m+ny; - x0 = y0*rho1d[1][m]; - for (l = nlower; l <= nupper; l++) { - mx = l+nx; - - // group A - - if (mask[i] & groupbit_A) - density_A_brick[mz][my][mx] += x0*rho1d[0][l]; - - // group B - - if (mask[i] & groupbit_B) - density_B_brick[mz][my][mx] += x0*rho1d[0][l]; - } - } - } - } - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for group-group interactions - ------------------------------------------------------------------------- */ - -void PPPM::poisson_groups(int BA_flag) -{ - int i,j,k,n; - - // reuse memory (already declared) - - FFT_SCALAR *work_A = work1; - FFT_SCALAR *work_B = work2; - - // transform charge density (r -> k) - - // group A - - n = 0; - for (i = 0; i < nfft; i++) { - work_A[n++] = density_A_fft[i]; - work_A[n++] = ZEROF; - } - - fft1->compute(work_A,work_A,1); - - // group B - - n = 0; - for (i = 0; i < nfft; i++) { - work_B[n++] = density_B_fft[i]; - work_B[n++] = ZEROF; - } - - fft1->compute(work_B,work_B,1); - - // group-group energy and force contribution, - // keep everything in reciprocal space so - // no inverse FFTs needed - - double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); - double s2 = scaleinv*scaleinv; - - // energy - - n = 0; - for (i = 0; i < nfft; i++) { - e2group += s2 * greensfn[i] * - (work_A[n]*work_B[n] + work_A[n+1]*work_B[n+1]); - n += 2; - } - - if (BA_flag) return; - - - // multiply by Green's function and s2 - // (only for work_A so it is not squared below) - - n = 0; - for (i = 0; i < nfft; i++) { - work_A[n++] *= s2 * greensfn[i]; - work_A[n++] *= s2 * greensfn[i]; - } - - // triclinic system - - if (triclinic) { - poisson_groups_triclinic(); - return; - } - - double partial_group; - - // force, x direction - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[0] += fkx[i] * partial_group; - n += 2; - } - - // force, y direction - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[1] += fky[j] * partial_group; - n += 2; - } - - // force, z direction - - n = 0; - for (k = nzlo_fft; k <= nzhi_fft; k++) - for (j = nylo_fft; j <= nyhi_fft; j++) - for (i = nxlo_fft; i <= nxhi_fft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[2] += fkz[k] * partial_group; - n += 2; - } -} - -/* ---------------------------------------------------------------------- - FFT-based Poisson solver for group-group interactions - for a triclinic system - ------------------------------------------------------------------------- */ - -void PPPM::poisson_groups_triclinic() -{ - int i,j,k,n; - - // reuse memory (already declared) - - FFT_SCALAR *work_A = work1; - FFT_SCALAR *work_B = work2; - - double partial_group; - - // force, x direction - - n = 0; - for (i = 0; i < nfft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[0] += fkx[i] * partial_group; - n += 2; - } - - // force, y direction - - n = 0; - for (i = 0; i < nfft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[1] += fky[i] * partial_group; - n += 2; - } - - // force, z direction - - n = 0; - for (i = 0; i < nfft; i++) { - partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; - f2group[2] += fkz[i] * partial_group; - n += 2; - } -} +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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: Roy Pollock (LLNL), Paul Crozier (SNL) + per-atom energy/virial & group/group energy/force added by Stan Moore (BYU) + analytic diff (2 FFT) option added by Rolf Isele-Holder (Aachen University) + triclinic added by Stan Moore (SNL) +------------------------------------------------------------------------- */ + +#include "lmptype.h" +#include "mpi.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" +#include "pppm.h" +#include "atom.h" +#include "comm.h" +#include "commgrid.h" +#include "neighbor.h" +#include "force.h" +#include "pair.h" +#include "bond.h" +#include "angle.h" +#include "domain.h" +#include "fft3d_wrap.h" +#include "remap_wrap.h" +#include "memory.h" +#include "error.h" + +#include "math_const.h" +#include "math_special.h" + +using namespace LAMMPS_NS; +using namespace MathConst; +using namespace MathSpecial; + +#define MAXORDER 7 +#define OFFSET 16384 +#define SMALL 0.00001 +#define LARGE 10000.0 +#define EPS_HOC 1.0e-7 + +enum{REVERSE_RHO}; +enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; + +#ifdef FFT_SINGLE +#define ZEROF 0.0f +#define ONEF 1.0f +#else +#define ZEROF 0.0 +#define ONEF 1.0 +#endif + +/* ---------------------------------------------------------------------- */ + +PPPM::PPPM(LAMMPS *lmp, int narg, char **arg) : KSpace(lmp, narg, arg) +{ + if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm command"); + + pppmflag = 1; + group_group_enable = 1; + + accuracy_relative = atof(arg[0]); + + nfactors = 3; + factors = new int[nfactors]; + factors[0] = 2; + factors[1] = 3; + factors[2] = 5; + + MPI_Comm_rank(world,&me); + MPI_Comm_size(world,&nprocs); + + density_brick = vdx_brick = vdy_brick = vdz_brick = NULL; + density_fft = NULL; + u_brick = NULL; + v0_brick = v1_brick = v2_brick = v3_brick = v4_brick = v5_brick = NULL; + greensfn = NULL; + work1 = work2 = NULL; + vg = NULL; + fkx = fky = fkz = NULL; + + sf_precoeff1 = sf_precoeff2 = sf_precoeff3 = + sf_precoeff4 = sf_precoeff5 = sf_precoeff6 = NULL; + + density_A_brick = density_B_brick = NULL; + density_A_fft = density_B_fft = NULL; + + gf_b = NULL; + rho1d = rho_coeff = drho1d = drho_coeff = NULL; + + fft1 = fft2 = NULL; + remap = NULL; + cg = NULL; + cg_peratom = NULL; + + nmax = 0; + part2grid = NULL; + + peratom_allocate_flag = 0; + group_allocate_flag = 0; + + // define acons coefficients for estimation of kspace errors + // see JCP 109, pg 7698 for derivation of coefficients + // higher order coefficients may be computed if needed + + memory->create(acons,8,7,"pppm:acons"); + acons[1][0] = 2.0 / 3.0; + acons[2][0] = 1.0 / 50.0; + acons[2][1] = 5.0 / 294.0; + acons[3][0] = 1.0 / 588.0; + acons[3][1] = 7.0 / 1440.0; + acons[3][2] = 21.0 / 3872.0; + acons[4][0] = 1.0 / 4320.0; + acons[4][1] = 3.0 / 1936.0; + acons[4][2] = 7601.0 / 2271360.0; + acons[4][3] = 143.0 / 28800.0; + acons[5][0] = 1.0 / 23232.0; + acons[5][1] = 7601.0 / 13628160.0; + acons[5][2] = 143.0 / 69120.0; + acons[5][3] = 517231.0 / 106536960.0; + acons[5][4] = 106640677.0 / 11737571328.0; + acons[6][0] = 691.0 / 68140800.0; + acons[6][1] = 13.0 / 57600.0; + acons[6][2] = 47021.0 / 35512320.0; + acons[6][3] = 9694607.0 / 2095994880.0; + acons[6][4] = 733191589.0 / 59609088000.0; + acons[6][5] = 326190917.0 / 11700633600.0; + acons[7][0] = 1.0 / 345600.0; + acons[7][1] = 3617.0 / 35512320.0; + acons[7][2] = 745739.0 / 838397952.0; + acons[7][3] = 56399353.0 / 12773376000.0; + acons[7][4] = 25091609.0 / 1560084480.0; + acons[7][5] = 1755948832039.0 / 36229939200000.0; + acons[7][6] = 4887769399.0 / 37838389248.0; +} + +/* ---------------------------------------------------------------------- + free all memory +------------------------------------------------------------------------- */ + +PPPM::~PPPM() +{ + delete [] factors; + deallocate(); + if (peratom_allocate_flag) deallocate_peratom(); + if (group_allocate_flag) deallocate_groups(); + memory->destroy(part2grid); + memory->destroy(acons); +} + +/* ---------------------------------------------------------------------- + called once before run +------------------------------------------------------------------------- */ + +void PPPM::init() +{ + if (me == 0) { + if (screen) fprintf(screen,"PPPM initialization ...\n"); + if (logfile) fprintf(logfile,"PPPM initialization ...\n"); + } + + // error check + + triclinic_check(); + if (domain->triclinic && differentiation_flag == 1) + error->all(FLERR,"Cannot (yet) use PPPM with triclinic box " + "and kspace_modify diff a'"); + if (domain->triclinic && slabflag) + error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and " + "slab correction"); + if (domain->dimension == 2) error->all(FLERR, + "Cannot use PPPM with 2d simulation"); + + if (!atom->q_flag) error->all(FLERR,"Kspace style requires atom attribute q"); + + if (slabflag == 0 && domain->nonperiodic > 0) + error->all(FLERR,"Cannot use nonperiodic boundaries with PPPM"); + if (slabflag) { + if (domain->xperiodic != 1 || domain->yperiodic != 1 || + domain->boundary[2][0] != 1 || domain->boundary[2][1] != 1) + error->all(FLERR,"Incorrect boundaries with slab PPPM"); + } + + if (order < 2 || order > MAXORDER) { + char str[128]; + sprintf(str,"PPPM order cannot be < 2 or > than %d",MAXORDER); + error->all(FLERR,str); + } + + // extract short-range Coulombic cutoff from pair style + + triclinic = domain->triclinic; + scale = 1.0; + + pair_check(); + + int itmp = 0; + double *p_cutoff = (double *) force->pair->extract("cut_coul",itmp); + if (p_cutoff == NULL) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + cutoff = *p_cutoff; + + // if kspace is TIP4P, extract TIP4P params from pair style + // bond/angle are not yet init(), so insure equilibrium request is valid + + qdist = 0.0; + + if (tip4pflag) { + double *p_qdist = (double *) force->pair->extract("qdist",itmp); + int *p_typeO = (int *) force->pair->extract("typeO",itmp); + int *p_typeH = (int *) force->pair->extract("typeH",itmp); + int *p_typeA = (int *) force->pair->extract("typeA",itmp); + int *p_typeB = (int *) force->pair->extract("typeB",itmp); + if (!p_qdist || !p_typeO || !p_typeH || !p_typeA || !p_typeB) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + qdist = *p_qdist; + typeO = *p_typeO; + typeH = *p_typeH; + int typeA = *p_typeA; + int typeB = *p_typeB; + + if (force->angle == NULL || force->bond == NULL) + error->all(FLERR,"Bond and angle potentials must be defined for TIP4P"); + if (typeA < 1 || typeA > atom->nangletypes || + force->angle->setflag[typeA] == 0) + error->all(FLERR,"Bad TIP4P angle type for PPPM/TIP4P"); + if (typeB < 1 || typeB > atom->nbondtypes || + force->bond->setflag[typeB] == 0) + error->all(FLERR,"Bad TIP4P bond type for PPPM/TIP4P"); + double theta = force->angle->equilibrium_angle(typeA); + double blen = force->bond->equilibrium_distance(typeB); + alpha = qdist / (cos(0.5*theta) * blen); + if (domain->triclinic) + error->all(FLERR,"Cannot (yet) use PPPM with triclinic box and TIP4P"); + } + + // compute qsum & qsqsum and warn if not charge-neutral + + qsum = qsqsum = 0.0; + for (int i = 0; i < atom->nlocal; i++) { + qsum += atom->q[i]; + qsqsum += atom->q[i]*atom->q[i]; + } + + double tmp; + MPI_Allreduce(&qsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsum = tmp; + MPI_Allreduce(&qsqsum,&tmp,1,MPI_DOUBLE,MPI_SUM,world); + qsqsum = tmp; + q2 = qsqsum * force->qqrd2e / force->dielectric; + + if (qsqsum == 0.0) + error->all(FLERR,"Cannot use kspace solver on system with no charge"); + if (fabs(qsum) > SMALL && me == 0) { + char str[128]; + sprintf(str,"System is not charge neutral, net charge = %g",qsum); + error->warning(FLERR,str); + } + + // set accuracy (force units) from accuracy_relative or accuracy_absolute + + if (accuracy_absolute >= 0.0) accuracy = accuracy_absolute; + else accuracy = accuracy_relative * two_charge_force; + + // free all arrays previously allocated + + deallocate(); + if (peratom_allocate_flag) deallocate_peratom(); + if (group_allocate_flag) deallocate_groups(); + + // setup FFT grid resolution and g_ewald + // normally one iteration thru while loop is all that is required + // if grid stencil does not extend beyond neighbor proc + // or overlap is allowed, then done + // else reduce order and try again + + int (*procneigh)[2] = comm->procneigh; + + CommGrid *cgtmp = NULL; + int iteration = 0; + + while (order >= minorder) { + if (iteration && me == 0) + error->warning(FLERR,"Reducing PPPM order b/c stencil extends " + "beyond nearest neighbor processor"); + + if (stagger_flag && !differentiation_flag) compute_gf_denom(); + set_grid_global(); + set_grid_local(); + if (overlap_allowed) break; + + cgtmp = new CommGrid(lmp,world,1,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + cgtmp->ghost_notify(); + if (!cgtmp->ghost_overlap()) break; + delete cgtmp; + + order--; + iteration++; + } + + if (order < minorder) error->all(FLERR,"PPPM order < minimum allowed order"); + if (!overlap_allowed && cgtmp->ghost_overlap()) + error->all(FLERR,"PPPM grid stencil extends " + "beyond nearest neighbor processor"); + if (cgtmp) delete cgtmp; + + // adjust g_ewald + + if (!gewaldflag) adjust_gewald(); + + // calculate the final accuracy + + double estimated_accuracy = final_accuracy(); + + // print stats + + int ngrid_max,nfft_both_max; + MPI_Allreduce(&ngrid,&ngrid_max,1,MPI_INT,MPI_MAX,world); + MPI_Allreduce(&nfft_both,&nfft_both_max,1,MPI_INT,MPI_MAX,world); + + if (me == 0) { + +#ifdef FFT_SINGLE + const char fft_prec[] = "single"; +#else + const char fft_prec[] = "double"; +#endif + + if (screen) { + fprintf(screen," G vector (1/distance)= %g\n",g_ewald); + fprintf(screen," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(screen," stencil order = %d\n",order); + fprintf(screen," estimated absolute RMS force accuracy = %g\n", + estimated_accuracy); + fprintf(screen," estimated relative force accuracy = %g\n", + estimated_accuracy/two_charge_force); + fprintf(screen," using %s precision FFTs\n",fft_prec); + fprintf(screen," 3d grid and FFT values/proc = %d %d\n", + ngrid_max,nfft_both_max); + } + if (logfile) { + fprintf(logfile," G vector (1/distance) = %g\n",g_ewald); + fprintf(logfile," grid = %d %d %d\n",nx_pppm,ny_pppm,nz_pppm); + fprintf(logfile," stencil order = %d\n",order); + fprintf(logfile," estimated absolute RMS force accuracy = %g\n", + estimated_accuracy); + fprintf(logfile," estimated relative force accuracy = %g\n", + estimated_accuracy/two_charge_force); + fprintf(logfile," using %s precision FFTs\n",fft_prec); + fprintf(logfile," 3d grid and FFT values/proc = %d %d\n", + ngrid_max,nfft_both_max); + } + } + + // allocate K-space dependent memory + // don't invoke allocate peratom() or group(), will be allocated when needed + + allocate(); + cg->ghost_notify(); + cg->setup(); + + // pre-compute Green's function denomiator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + if (differentiation_flag == 1) compute_sf_precoeff(); + compute_rho_coeff(); +} + +/* ---------------------------------------------------------------------- + adjust PPPM coeffs, called initially and whenever volume has changed +------------------------------------------------------------------------- */ + +void PPPM::setup() +{ + if (triclinic) { + setup_triclinic(); + return; + } + + int i,j,k,n; + double *prd; + + // volume-dependent factors + // adjust z dimension for 2d slab PPPM + // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 + + if (triclinic == 0) prd = domain->prd; + else prd = domain->prd_lamda; + + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + delxinv = nx_pppm/xprd; + delyinv = ny_pppm/yprd; + delzinv = nz_pppm/zprd_slab; + + delvolinv = delxinv*delyinv*delzinv; + + double unitkx = (MY_2PI/xprd); + double unitky = (MY_2PI/yprd); + double unitkz = (MY_2PI/zprd_slab); + + // fkx,fky,fkz for my FFT grid pts + + double per; + + for (i = nxlo_fft; i <= nxhi_fft; i++) { + per = i - nx_pppm*(2*i/nx_pppm); + fkx[i] = unitkx*per; + } + + for (i = nylo_fft; i <= nyhi_fft; i++) { + per = i - ny_pppm*(2*i/ny_pppm); + fky[i] = unitky*per; + } + + for (i = nzlo_fft; i <= nzhi_fft; i++) { + per = i - nz_pppm*(2*i/nz_pppm); + fkz[i] = unitkz*per; + } + + // virial coefficients + + double sqk,vterm; + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) { + for (j = nylo_fft; j <= nyhi_fft; j++) { + for (i = nxlo_fft; i <= nxhi_fft; i++) { + sqk = fkx[i]*fkx[i] + fky[j]*fky[j] + fkz[k]*fkz[k]; + if (sqk == 0.0) { + vg[n][0] = 0.0; + vg[n][1] = 0.0; + vg[n][2] = 0.0; + vg[n][3] = 0.0; + vg[n][4] = 0.0; + vg[n][5] = 0.0; + } else { + vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); + vg[n][0] = 1.0 + vterm*fkx[i]*fkx[i]; + vg[n][1] = 1.0 + vterm*fky[j]*fky[j]; + vg[n][2] = 1.0 + vterm*fkz[k]*fkz[k]; + vg[n][3] = vterm*fkx[i]*fky[j]; + vg[n][4] = vterm*fkx[i]*fkz[k]; + vg[n][5] = vterm*fky[j]*fkz[k]; + } + n++; + } + } + } + + if (differentiation_flag == 1) compute_gf_ad(); + else compute_gf_ik(); +} + +/* ---------------------------------------------------------------------- + adjust PPPM coeffs, called initially and whenever volume has changed + for a triclinic system +------------------------------------------------------------------------- */ + +void PPPM::setup_triclinic() +{ + int i,j,k,n; + double *prd; + + // volume-dependent factors + // adjust z dimension for 2d slab PPPM + // z dimension for 3d PPPM is zprd since slab_volfactor = 1.0 + + prd = domain->prd; + + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + // use lamda (0-1) coordinates + + delxinv = nx_pppm; + delyinv = ny_pppm; + delzinv = nz_pppm; + delvolinv = delxinv*delyinv*delzinv/volume; + + // fkx,fky,fkz for my FFT grid pts + + double per_i,per_j,per_k; + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) { + per_k = k - nz_pppm*(2*k/nz_pppm); + for (j = nylo_fft; j <= nyhi_fft; j++) { + per_j = j - ny_pppm*(2*j/ny_pppm); + for (i = nxlo_fft; i <= nxhi_fft; i++) { + per_i = i - nx_pppm*(2*i/nx_pppm); + + double unitk_lamda[3]; + unitk_lamda[0] = 2.0*MY_PI*per_i; + unitk_lamda[1] = 2.0*MY_PI*per_j; + unitk_lamda[2] = 2.0*MY_PI*per_k; + x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); + fkx[n] = unitk_lamda[0]; + fky[n] = unitk_lamda[1]; + fkz[n] = unitk_lamda[2]; + n++; + } + } + } + + // virial coefficients + + double sqk,vterm; + + for (n = 0; n < nfft; n++) { + sqk = fkx[n]*fkx[n] + fky[n]*fky[n] + fkz[n]*fkz[n]; + if (sqk == 0.0) { + vg[n][0] = 0.0; + vg[n][1] = 0.0; + vg[n][2] = 0.0; + vg[n][3] = 0.0; + vg[n][4] = 0.0; + vg[n][5] = 0.0; + } else { + vterm = -2.0 * (1.0/sqk + 0.25/(g_ewald*g_ewald)); + vg[n][0] = 1.0 + vterm*fkx[n]*fkx[n]; + vg[n][1] = 1.0 + vterm*fky[n]*fky[n]; + vg[n][2] = 1.0 + vterm*fkz[n]*fkz[n]; + vg[n][3] = vterm*fkx[n]*fky[n]; + vg[n][4] = vterm*fkx[n]*fkz[n]; + vg[n][5] = vterm*fky[n]*fkz[n]; + } + } + + compute_gf_ik_triclinic(); +} + +/* ---------------------------------------------------------------------- + reset local grid arrays and communication stencils + called by fix balance b/c it changed sizes of processor sub-domains +------------------------------------------------------------------------- */ + +void PPPM::setup_grid() +{ + // free all arrays previously allocated + + deallocate(); + if (peratom_allocate_flag) deallocate_peratom(); + if (group_allocate_flag) deallocate_groups(); + + // reset portion of global grid that each proc owns + + set_grid_local(); + + // reallocate K-space dependent memory + // check if grid communication is now overlapping if not allowed + // don't invoke allocate peratom() or group(), will be allocated when needed + + allocate(); + + cg->ghost_notify(); + if (overlap_allowed == 0 && cg->ghost_overlap()) + error->all(FLERR,"PPPM grid stencil extends " + "beyond nearest neighbor processor"); + cg->setup(); + + // pre-compute Green's function denomiator expansion + // pre-compute 1d charge distribution coefficients + + compute_gf_denom(); + if (differentiation_flag == 1) compute_sf_precoeff(); + compute_rho_coeff(); + + // pre-compute volume-dependent coeffs + + setup(); +} + +/* ---------------------------------------------------------------------- + compute the PPPM long-range force, energy, virial +------------------------------------------------------------------------- */ + +void PPPM::compute(int eflag, int vflag) +{ + int i,j; + + // set energy/virial flags + // invoke allocate_peratom() if needed for first time + + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = evflag_atom = eflag_global = vflag_global = + eflag_atom = vflag_atom = 0; + + if (evflag_atom && !peratom_allocate_flag) { + allocate_peratom(); + cg_peratom->ghost_notify(); + cg_peratom->setup(); + } + + // convert atoms from box to lamda coords + + if (triclinic == 0) boxlo = domain->boxlo; + else { + boxlo = domain->boxlo_lamda; + domain->x2lamda(atom->nlocal); + } + + // extend size of per-atom arrays if necessary + + if (atom->nlocal > nmax) { + memory->destroy(part2grid); + nmax = atom->nmax; + memory->create(part2grid,nmax,3,"pppm:part2grid"); + } + + // find grid points for all my particles + // map my particle charge onto my local 3d density grid + + particle_map(); + make_rho(); + + // all procs communicate density values from their ghost cells + // to fully sum contribution in their 3d bricks + // remap from 3d decomposition to FFT decomposition + + cg->reverse_comm(this,REVERSE_RHO); + brick2fft(); + + // compute potential gradient on my FFT grid and + // portion of e_long on this proc's FFT grid + // return gradients (electric fields) in 3d brick decomposition + // also performs per-atom calculations via poisson_peratom() + + poisson(); + + // all procs communicate E-field values + // to fill ghost cells surrounding their 3d bricks + + if (differentiation_flag == 1) cg->forward_comm(this,FORWARD_AD); + else cg->forward_comm(this,FORWARD_IK); + + // extra per-atom energy/virial communication + + if (evflag_atom) { + if (differentiation_flag == 1 && vflag_atom) + cg_peratom->forward_comm(this,FORWARD_AD_PERATOM); + else if (differentiation_flag == 0) + cg_peratom->forward_comm(this,FORWARD_IK_PERATOM); + } + + // calculate the force on my particles + + fieldforce(); + + // extra per-atom energy/virial communication + + if (evflag_atom) fieldforce_peratom(); + + // sum global energy across procs and add in volume-dependent term + + const double qscale = force->qqrd2e * scale; + + if (eflag_global) { + double energy_all; + MPI_Allreduce(&energy,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); + energy = energy_all; + + energy *= 0.5*volume; + energy -= g_ewald*qsqsum/MY_PIS + + MY_PI2*qsum*qsum / (g_ewald*g_ewald*volume); + energy *= qscale; + } + + // sum global virial across procs + + if (vflag_global) { + double virial_all[6]; + MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); + for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]; + } + + // per-atom energy/virial + // energy includes self-energy correction + // notal accounts for TIP4P tallying eatom/vatom for ghost atoms + + if (evflag_atom) { + double *q = atom->q; + int nlocal = atom->nlocal; + int ntotal = nlocal; + if (tip4pflag) ntotal += atom->nghost; + + if (eflag_atom) { + for (i = 0; i < nlocal; i++) { + eatom[i] *= 0.5; + eatom[i] -= g_ewald*q[i]*q[i]/MY_PIS + MY_PI2*q[i]*qsum / + (g_ewald*g_ewald*volume); + eatom[i] *= qscale; + } + for (i = nlocal; i < ntotal; i++) eatom[i] *= 0.5*qscale; + } + + if (vflag_atom) { + for (i = 0; i < ntotal; i++) + for (j = 0; j < 6; j++) vatom[i][j] *= 0.5*qscale; + } + } + + // 2d slab correction + + if (slabflag == 1) slabcorr(); + + // convert atoms back from lamda to box coords + + if (triclinic) domain->lamda2x(atom->nlocal); +} + +/* ---------------------------------------------------------------------- + allocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::allocate() +{ + memory->create3d_offset(density_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_brick"); + + memory->create(density_fft,nfft_both,"pppm:density_fft"); + memory->create(greensfn,nfft_both,"pppm:greensfn"); + memory->create(work1,2*nfft_both,"pppm:work1"); + memory->create(work2,2*nfft_both,"pppm:work2"); + memory->create(vg,nfft_both,6,"pppm:vg"); + + if (triclinic == 0) { + memory->create1d_offset(fkx,nxlo_fft,nxhi_fft,"pppm:fkx"); + memory->create1d_offset(fky,nylo_fft,nyhi_fft,"pppm:fky"); + memory->create1d_offset(fkz,nzlo_fft,nzhi_fft,"pppm:fkz"); + } else { + memory->create(fkx,nfft_both,"pppm:fkx"); + memory->create(fky,nfft_both,"pppm:fky"); + memory->create(fkz,nfft_both,"pppm:fkz"); + } + + if (differentiation_flag == 1) { + memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:u_brick"); + + memory->create(sf_precoeff1,nfft_both,"pppm:sf_precoeff1"); + memory->create(sf_precoeff2,nfft_both,"pppm:sf_precoeff2"); + memory->create(sf_precoeff3,nfft_both,"pppm:sf_precoeff3"); + memory->create(sf_precoeff4,nfft_both,"pppm:sf_precoeff4"); + memory->create(sf_precoeff5,nfft_both,"pppm:sf_precoeff5"); + memory->create(sf_precoeff6,nfft_both,"pppm:sf_precoeff6"); + + } else { + memory->create3d_offset(vdx_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdx_brick"); + memory->create3d_offset(vdy_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdy_brick"); + memory->create3d_offset(vdz_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:vdz_brick"); + } + + // summation coeffs + + if (!stagger_flag) memory->create(gf_b,order,"pppm:gf_b"); + memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); + memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); + memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); + memory->create2d_offset(drho_coeff,order,(1-order)/2,order/2, + "pppm:drho_coeff"); + + // create 2 FFTs and a Remap + // 1st FFT keeps data in FFT decompostion + // 2nd FFT returns data in 3d brick decomposition + // remap takes data from 3d brick to FFT decomposition + + int tmp; + + fft1 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 0,0,&tmp); + + fft2 = new FFT3d(lmp,world,nx_pppm,ny_pppm,nz_pppm, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + 0,0,&tmp); + + remap = new Remap(lmp,world, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_fft,nxhi_fft,nylo_fft,nyhi_fft,nzlo_fft,nzhi_fft, + 1,0,0,FFT_PRECISION); + + // create ghost grid object for rho and electric field communication + + int (*procneigh)[2] = comm->procneigh; + + if (differentiation_flag == 1) + cg = new CommGrid(lmp,world,1,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + else + cg = new CommGrid(lmp,world,3,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); +} + +/* ---------------------------------------------------------------------- + deallocate memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::deallocate() +{ + memory->destroy3d_offset(density_brick,nzlo_out,nylo_out,nxlo_out); + + if (differentiation_flag == 1) { + memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy(sf_precoeff1); + memory->destroy(sf_precoeff2); + memory->destroy(sf_precoeff3); + memory->destroy(sf_precoeff4); + memory->destroy(sf_precoeff5); + memory->destroy(sf_precoeff6); + } else { + memory->destroy3d_offset(vdx_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(vdy_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(vdz_brick,nzlo_out,nylo_out,nxlo_out); + } + + memory->destroy(density_fft); + memory->destroy(greensfn); + memory->destroy(work1); + memory->destroy(work2); + memory->destroy(vg); + + if (triclinic == 0) { + memory->destroy1d_offset(fkx,nxlo_fft); + memory->destroy1d_offset(fky,nylo_fft); + memory->destroy1d_offset(fkz,nzlo_fft); + } else { + memory->destroy(fkx); + memory->destroy(fky); + memory->destroy(fkz); + } + + memory->destroy(gf_b); + if (stagger_flag) gf_b = NULL; + memory->destroy2d_offset(rho1d,-order/2); + memory->destroy2d_offset(drho1d,-order/2); + memory->destroy2d_offset(rho_coeff,(1-order)/2); + memory->destroy2d_offset(drho_coeff,(1-order)/2); + + delete fft1; + delete fft2; + delete remap; + delete cg; +} + +/* ---------------------------------------------------------------------- + allocate per-atom memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::allocate_peratom() +{ + peratom_allocate_flag = 1; + + if (differentiation_flag != 1) + memory->create3d_offset(u_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:u_brick"); + + memory->create3d_offset(v0_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v0_brick"); + + memory->create3d_offset(v1_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v1_brick"); + memory->create3d_offset(v2_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v2_brick"); + memory->create3d_offset(v3_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v3_brick"); + memory->create3d_offset(v4_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v4_brick"); + memory->create3d_offset(v5_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:v5_brick"); + + // create ghost grid object for rho and electric field communication + + int (*procneigh)[2] = comm->procneigh; + + if (differentiation_flag == 1) + cg_peratom = + new CommGrid(lmp,world,6,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); + else + cg_peratom = + new CommGrid(lmp,world,7,1, + nxlo_in,nxhi_in,nylo_in,nyhi_in,nzlo_in,nzhi_in, + nxlo_out,nxhi_out,nylo_out,nyhi_out,nzlo_out,nzhi_out, + procneigh[0][0],procneigh[0][1],procneigh[1][0], + procneigh[1][1],procneigh[2][0],procneigh[2][1]); +} + +/* ---------------------------------------------------------------------- + deallocate per-atom memory that depends on # of K-vectors and order +------------------------------------------------------------------------- */ + +void PPPM::deallocate_peratom() +{ + peratom_allocate_flag = 0; + + memory->destroy3d_offset(v0_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v1_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v2_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v3_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v4_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(v5_brick,nzlo_out,nylo_out,nxlo_out); + + if (differentiation_flag != 1) + memory->destroy3d_offset(u_brick,nzlo_out,nylo_out,nxlo_out); + + delete cg_peratom; +} + +/* ---------------------------------------------------------------------- + set global size of PPPM grid = nx,ny,nz_pppm + used for charge accumulation, FFTs, and electric field interpolation +------------------------------------------------------------------------- */ + +void PPPM::set_grid_global() +{ + // use xprd,yprd,zprd (even if triclinic, and then scale later) + // adjust z dimension for 2d slab PPPM + // 3d PPPM just uses zprd since slab_volfactor = 1.0 + + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + + // make initial g_ewald estimate + // based on desired accuracy and real space cutoff + // fluid-occupied volume used to estimate real-space error + // zprd used rather than zprd_slab + + double h; + bigint natoms = atom->natoms; + + if (!gewaldflag) { + if (accuracy <= 0.0) + error->all(FLERR,"KSpace accuracy must be > 0"); + g_ewald = accuracy*sqrt(natoms*cutoff*xprd*yprd*zprd) / (2.0*q2); + if (g_ewald >= 1.0) g_ewald = (1.35 - 0.15*log(accuracy))/cutoff; + else g_ewald = sqrt(-log(g_ewald)) / cutoff; + } + + // set optimal nx_pppm,ny_pppm,nz_pppm based on order and accuracy + // nz_pppm uses extended zprd_slab instead of zprd + // reduce it until accuracy target is met + + if (!gridflag) { + + if (differentiation_flag == 1 || stagger_flag) { + + h = h_x = h_y = h_z = 4.0/g_ewald; + int count = 0; + while (1) { + + // set grid dimension + nx_pppm = static_cast (xprd/h_x); + ny_pppm = static_cast (yprd/h_y); + nz_pppm = static_cast (zprd_slab/h_z); + + if (nx_pppm <= 1) nx_pppm = 2; + if (ny_pppm <= 1) ny_pppm = 2; + if (nz_pppm <= 1) nz_pppm = 2; + + //set local grid dimension + int npey_fft,npez_fft; + if (nz_pppm >= nprocs) { + npey_fft = 1; + npez_fft = nprocs; + } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + + int me_y = me % npey_fft; + int me_z = me / npey_fft; + + nxlo_fft = 0; + nxhi_fft = nx_pppm - 1; + nylo_fft = me_y*ny_pppm/npey_fft; + nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; + nzlo_fft = me_z*nz_pppm/npez_fft; + nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; + + double df_kspace = compute_df_kspace(); + + count++; + + // break loop if the accuracy has been reached or + // too many loops have been performed + + if (df_kspace <= accuracy) break; + if (count > 500) error->all(FLERR, "Could not compute grid size"); + h *= 0.95; + h_x = h_y = h_z = h; + } + + } else { + + double err; + h_x = h_y = h_z = 1.0/g_ewald; + + nx_pppm = static_cast (xprd/h_x) + 1; + ny_pppm = static_cast (yprd/h_y) + 1; + nz_pppm = static_cast (zprd_slab/h_z) + 1; + + err = estimate_ik_error(h_x,xprd,natoms); + while (err > accuracy) { + err = estimate_ik_error(h_x,xprd,natoms); + nx_pppm++; + h_x = xprd/nx_pppm; + } + + err = estimate_ik_error(h_y,yprd,natoms); + while (err > accuracy) { + err = estimate_ik_error(h_y,yprd,natoms); + ny_pppm++; + h_y = yprd/ny_pppm; + } + + err = estimate_ik_error(h_z,zprd_slab,natoms); + while (err > accuracy) { + err = estimate_ik_error(h_z,zprd_slab,natoms); + nz_pppm++; + h_z = zprd_slab/nz_pppm; + } + } + + // scale grid for triclinic skew + + if (triclinic) { + double tmp[3]; + tmp[0] = nx_pppm/xprd; + tmp[1] = ny_pppm/yprd; + tmp[2] = nz_pppm/zprd; + lamda2xT(&tmp[0],&tmp[0]); + nx_pppm = static_cast(tmp[0]) + 1; + ny_pppm = static_cast(tmp[1]) + 1; + nz_pppm = static_cast(tmp[2]) + 1; + } + } + + // boost grid size until it is factorable + + while (!factorable(nx_pppm)) nx_pppm++; + while (!factorable(ny_pppm)) ny_pppm++; + while (!factorable(nz_pppm)) nz_pppm++; + + if (triclinic == 0) { + h_x = xprd/nx_pppm; + h_y = yprd/ny_pppm; + h_z = zprd_slab/nz_pppm; + } else { + double tmp[3]; + tmp[0] = nx_pppm; + tmp[1] = ny_pppm; + tmp[2] = nz_pppm; + x2lamdaT(&tmp[0],&tmp[0]); + h_x = 1.0/tmp[0]; + h_y = 1.0/tmp[1]; + h_z = 1.0/tmp[2]; + } + + if (nx_pppm >= OFFSET || ny_pppm >= OFFSET || nz_pppm >= OFFSET) + error->all(FLERR,"PPPM grid is too large"); +} + +/* ---------------------------------------------------------------------- + check if all factors of n are in list of factors + return 1 if yes, 0 if no +------------------------------------------------------------------------- */ + +int PPPM::factorable(int n) +{ + int i; + + while (n > 1) { + for (i = 0; i < nfactors; i++) { + if (n % factors[i] == 0) { + n /= factors[i]; + break; + } + } + if (i == nfactors) return 0; + } + + return 1; +} + +/* ---------------------------------------------------------------------- + compute estimated kspace force error +------------------------------------------------------------------------- */ + +double PPPM::compute_df_kspace() +{ + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + bigint natoms = atom->natoms; + double df_kspace = 0.0; + if (differentiation_flag == 1 || stagger_flag) { + double qopt = compute_qopt(); + df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab); + } else { + double lprx = estimate_ik_error(h_x,xprd,natoms); + double lpry = estimate_ik_error(h_y,yprd,natoms); + double lprz = estimate_ik_error(h_z,zprd_slab,natoms); + df_kspace = sqrt(lprx*lprx + lpry*lpry + lprz*lprz) / sqrt(3.0); + } + return df_kspace; +} + +/* ---------------------------------------------------------------------- + compute qopt +------------------------------------------------------------------------- */ + +double PPPM::compute_qopt() +{ + double qopt = 0.0; + double *prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + volume = xprd * yprd * zprd_slab; + + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double u1, u2, sqk; + double sum1,sum2,sum3,sum4,dot2; + + int k,l,m,nx,ny,nz; + const int twoorder = 2*order; + + for (m = nzlo_fft; m <= nzhi_fft; m++) { + const int mper = m - nz_pppm*(2*m/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + const int lper = l - ny_pppm*(2*l/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + const int kper = k - nx_pppm*(2*k/nx_pppm); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + + sum1 = 0.0; + sum2 = 0.0; + sum3 = 0.0; + sum4 = 0.0; + for (nx = -2; nx <= 2; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + qx *= qx; + + for (ny = -2; ny <= 2; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + qy *= qy; + + for (nz = -2; nz <= 2; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + qz *= qz; + + dot2 = qx+qy+qz; + u1 = sx*sy*sz; + u2 = wx*wy*wz; + sum1 += u1*u1/dot2*MY_4PI*MY_4PI; + sum2 += u1 * u2 * MY_4PI; + sum3 += u2; + sum4 += dot2*u2; + } + } + } + sum2 *= sum2; + qopt += sum1 - sum2/(sum3*sum4); + } + } + } + } + double qopt_all; + MPI_Allreduce(&qopt,&qopt_all,1,MPI_DOUBLE,MPI_SUM,world); + return qopt_all; +} + +/* ---------------------------------------------------------------------- + estimate kspace force error for ik method +------------------------------------------------------------------------- */ + +double PPPM::estimate_ik_error(double h, double prd, bigint natoms) +{ + double sum = 0.0; + for (int m = 0; m < order; m++) + sum += acons[order][m] * pow(h*g_ewald,2.0*m); + double value = q2 * pow(h*g_ewald,(double)order) * + sqrt(g_ewald*prd*sqrt(MY_2PI)*sum/natoms) / (prd*prd); + + return value; +} + +/* ---------------------------------------------------------------------- + adjust the g_ewald parameter to near its optimal value + using a Newton-Raphson solver +------------------------------------------------------------------------- */ + +void PPPM::adjust_gewald() +{ + double dx; + + for (int i = 0; i < LARGE; i++) { + dx = newton_raphson_f() / derivf(); + g_ewald -= dx; + if (fabs(newton_raphson_f()) < SMALL) return; + } + + char str[128]; + sprintf(str, "Could not compute g_ewald"); + error->all(FLERR, str); +} + +/* ---------------------------------------------------------------------- + Calculate f(x) using Newton-Raphson solver + ------------------------------------------------------------------------- */ + +double PPPM::newton_raphson_f() +{ + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + bigint natoms = atom->natoms; + + double df_rspace = 2.0*q2*exp(-g_ewald*g_ewald*cutoff*cutoff) / + sqrt(natoms*cutoff*xprd*yprd*zprd); + + double df_kspace = compute_df_kspace(); + + return df_rspace - df_kspace; +} + +/* ---------------------------------------------------------------------- + Calculate numerical derivative f'(x) using forward difference + [f(x + h) - f(x)] / h + ------------------------------------------------------------------------- */ + +double PPPM::derivf() +{ + double h = 0.000001; //Derivative step-size + double df,f1,f2,g_ewald_old; + + f1 = newton_raphson_f(); + g_ewald_old = g_ewald; + g_ewald += h; + f2 = newton_raphson_f(); + g_ewald = g_ewald_old; + df = (f2 - f1)/h; + + return df; +} + +/* ---------------------------------------------------------------------- + Calculate the final estimate of the accuracy +------------------------------------------------------------------------- */ + +double PPPM::final_accuracy() +{ + double xprd = domain->xprd; + double yprd = domain->yprd; + double zprd = domain->zprd; + double zprd_slab = zprd*slab_volfactor; + bigint natoms = atom->natoms; + + double df_kspace = compute_df_kspace(); + double q2_over_sqrt = q2 / sqrt(natoms*cutoff*xprd*yprd*zprd); + double df_rspace = 2.0 * q2_over_sqrt * exp(-g_ewald*g_ewald*cutoff*cutoff); + double df_table = estimate_table_accuracy(q2_over_sqrt,df_rspace); + double estimated_accuracy = sqrt(df_kspace*df_kspace + df_rspace*df_rspace + + df_table*df_table); + + return estimated_accuracy; +} + +/* ---------------------------------------------------------------------- + set local subset of PPPM/FFT grid that I own + n xyz lo/hi in = 3d brick that I own (inclusive) + n xyz lo/hi out = 3d brick + ghost cells in 6 directions (inclusive) + n xyz lo/hi fft = FFT columns that I own (all of x dim, 2d decomp in yz) +------------------------------------------------------------------------- */ + +void PPPM::set_grid_local() +{ + // global indices of PPPM grid range from 0 to N-1 + // nlo_in,nhi_in = lower/upper limits of the 3d sub-brick of + // global PPPM grid that I own without ghost cells + // for slab PPPM, assign z grid as if it were not extended + + nxlo_in = static_cast (comm->xsplit[comm->myloc[0]] * nx_pppm); + nxhi_in = static_cast (comm->xsplit[comm->myloc[0]+1] * nx_pppm) - 1; + + nylo_in = static_cast (comm->ysplit[comm->myloc[1]] * ny_pppm); + nyhi_in = static_cast (comm->ysplit[comm->myloc[1]+1] * ny_pppm) - 1; + + nzlo_in = static_cast + (comm->zsplit[comm->myloc[2]] * nz_pppm/slab_volfactor); + nzhi_in = static_cast + (comm->zsplit[comm->myloc[2]+1] * nz_pppm/slab_volfactor) - 1; + + // nlower,nupper = stencil size for mapping particles to PPPM grid + + nlower = -(order-1)/2; + nupper = order/2; + + // shift values for particle <-> grid mapping + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + if (order % 2) shift = OFFSET + 0.5; + else shift = OFFSET; + if (order % 2) shiftone = 0.0; + else shiftone = 0.5; + + // nlo_out,nhi_out = lower/upper limits of the 3d sub-brick of + // global PPPM grid that my particles can contribute charge to + // effectively nlo_in,nhi_in + ghost cells + // nlo,nhi = global coords of grid pt to "lower left" of smallest/largest + // position a particle in my box can be at + // dist[3] = particle position bound = subbox + skin/2.0 + qdist + // qdist = offset due to TIP4P fictitious charge + // convert to triclinic if necessary + // nlo_out,nhi_out = nlo,nhi + stencil size for particle mapping + // for slab PPPM, assign z grid as if it were not extended + + double *prd,*sublo,*subhi; + + if (triclinic == 0) { + prd = domain->prd; + boxlo = domain->boxlo; + sublo = domain->sublo; + subhi = domain->subhi; + } else { + prd = domain->prd_lamda; + boxlo = domain->boxlo_lamda; + sublo = domain->sublo_lamda; + subhi = domain->subhi_lamda; + } + + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + double zprd_slab = zprd*slab_volfactor; + + double dist[3]; + double cuthalf = 0.5*neighbor->skin + qdist; + if (triclinic == 0) dist[0] = dist[1] = dist[2] = cuthalf; + else kspacebbox(cuthalf,&dist[0]); + + int nlo,nhi; + + nlo = static_cast ((sublo[0]-dist[0]-boxlo[0]) * + nx_pppm/xprd + shift) - OFFSET; + nhi = static_cast ((subhi[0]+dist[0]-boxlo[0]) * + nx_pppm/xprd + shift) - OFFSET; + nxlo_out = nlo + nlower; + nxhi_out = nhi + nupper; + + nlo = static_cast ((sublo[1]-dist[1]-boxlo[1]) * + ny_pppm/yprd + shift) - OFFSET; + nhi = static_cast ((subhi[1]+dist[1]-boxlo[1]) * + ny_pppm/yprd + shift) - OFFSET; + nylo_out = nlo + nlower; + nyhi_out = nhi + nupper; + + nlo = static_cast ((sublo[2]-dist[2]-boxlo[2]) * + nz_pppm/zprd_slab + shift) - OFFSET; + nhi = static_cast ((subhi[2]+dist[2]-boxlo[2]) * + nz_pppm/zprd_slab + shift) - OFFSET; + nzlo_out = nlo + nlower; + nzhi_out = nhi + nupper; + + if (stagger_flag) { + nxhi_out++; + nyhi_out++; + nzhi_out++; + } + + // for slab PPPM, change the grid boundary for processors at +z end + // to include the empty volume between periodically repeating slabs + // for slab PPPM, want charge data communicated from -z proc to +z proc, + // but not vice versa, also want field data communicated from +z proc to + // -z proc, but not vice versa + // this is accomplished by nzhi_in = nzhi_out on +z end (no ghost cells) + // also insure no other procs use ghost cells beyond +z limit + + if (slabflag) { + if (comm->myloc[2] == comm->procgrid[2]-1) + nzhi_in = nzhi_out = nz_pppm - 1; + nzhi_out = MIN(nzhi_out,nz_pppm-1); + } + + // decomposition of FFT mesh + // global indices range from 0 to N-1 + // proc owns entire x-dimension, clumps of columns in y,z dimensions + // npey_fft,npez_fft = # of procs in y,z dims + // if nprocs is small enough, proc can own 1 or more entire xy planes, + // else proc owns 2d sub-blocks of yz plane + // me_y,me_z = which proc (0-npe_fft-1) I am in y,z dimensions + // nlo_fft,nhi_fft = lower/upper limit of the section + // of the global FFT mesh that I own + + int npey_fft,npez_fft; + if (nz_pppm >= nprocs) { + npey_fft = 1; + npez_fft = nprocs; + } else procs2grid2d(nprocs,ny_pppm,nz_pppm,&npey_fft,&npez_fft); + + int me_y = me % npey_fft; + int me_z = me / npey_fft; + + nxlo_fft = 0; + nxhi_fft = nx_pppm - 1; + nylo_fft = me_y*ny_pppm/npey_fft; + nyhi_fft = (me_y+1)*ny_pppm/npey_fft - 1; + nzlo_fft = me_z*nz_pppm/npez_fft; + nzhi_fft = (me_z+1)*nz_pppm/npez_fft - 1; + + // PPPM grid pts owned by this proc, including ghosts + + ngrid = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + + // FFT grids owned by this proc, without ghosts + // nfft = FFT points in FFT decomposition on this proc + // nfft_brick = FFT points in 3d brick-decomposition on this proc + // nfft_both = greater of 2 values + + nfft = (nxhi_fft-nxlo_fft+1) * (nyhi_fft-nylo_fft+1) * + (nzhi_fft-nzlo_fft+1); + int nfft_brick = (nxhi_in-nxlo_in+1) * (nyhi_in-nylo_in+1) * + (nzhi_in-nzlo_in+1); + nfft_both = MAX(nfft,nfft_brick); +} + +/* ---------------------------------------------------------------------- + pre-compute Green's function denominator expansion coeffs, Gamma(2n) +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_denom() +{ + int k,l,m; + + for (l = 1; l < order; l++) gf_b[l] = 0.0; + gf_b[0] = 1.0; + + for (m = 1; m < order; m++) { + for (l = m; l > 0; l--) + gf_b[l] = 4.0 * (gf_b[l]*(l-m)*(l-m-0.5)-gf_b[l-1]*(l-m-1)*(l-m-1)); + gf_b[0] = 4.0 * (gf_b[0]*(l-m)*(l-m-0.5)); + } + + bigint ifact = 1; + for (k = 1; k < 2*order; k++) ifact *= k; + double gaminv = 1.0/ifact; + for (l = 0; l < order; l++) gf_b[l] *= gaminv; +} + +/* ---------------------------------------------------------------------- + pre-compute modified (Hockney-Eastwood) Coulomb Green's function +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_ik() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + double sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + const int nbx = static_cast ((g_ewald*xprd/(MY_PI*nx_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nby = static_cast ((g_ewald*yprd/(MY_PI*ny_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int nbz = static_cast ((g_ewald*zprd_slab/(MY_PI*nz_pppm)) * + pow(-log(EPS_HOC),0.25)); + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(0.5*unitkz*mper*zprd_slab/nz_pppm)); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(0.5*unitky*lper*yprd/ny_pppm)); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(0.5*unitkx*kper*xprd/nx_pppm)); + + sqk = square(unitkx*kper) + square(unitky*lper) + square(unitkz*mper); + + if (sqk != 0.0) { + numerator = 12.5663706/sqk; + denominator = gf_denom(snx,sny,snz); + sum1 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + qx = unitkx*(kper+nx_pppm*nx); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + qy = unitky*(lper+ny_pppm*ny); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + qz = unitkz*(mper+nz_pppm*nz); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + pre-compute modified (Hockney-Eastwood) Coulomb Green's function + for a triclinic system +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_ik_triclinic() +{ + double snx,sny,snz; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double sum1,dot1,dot2; + double numerator,denominator; + double sqk; + + int k,l,m,n,nx,ny,nz,kper,lper,mper; + + double tmp[3]; + tmp[0] = (g_ewald/(MY_PI*nx_pppm)) * pow(-log(EPS_HOC),0.25); + tmp[1] = (g_ewald/(MY_PI*ny_pppm)) * pow(-log(EPS_HOC),0.25); + tmp[2] = (g_ewald/(MY_PI*nz_pppm)) * pow(-log(EPS_HOC),0.25); + lamda2xT(&tmp[0],&tmp[0]); + const int nbx = static_cast (tmp[0]); + const int nby = static_cast (tmp[1]); + const int nbz = static_cast (tmp[2]); + + const int twoorder = 2*order; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + snz = square(sin(MY_PI*mper/nz_pppm)); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + sny = square(sin(MY_PI*lper/ny_pppm)); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + snx = square(sin(MY_PI*kper/nx_pppm)); + + double unitk_lamda[3]; + unitk_lamda[0] = 2.0*MY_PI*kper; + unitk_lamda[1] = 2.0*MY_PI*lper; + unitk_lamda[2] = 2.0*MY_PI*mper; + x2lamdaT(&unitk_lamda[0],&unitk_lamda[0]); + + sqk = square(unitk_lamda[0]) + square(unitk_lamda[1]) + square(unitk_lamda[2]); + + if (sqk != 0.0) { + numerator = 12.5663706/sqk; + denominator = gf_denom(snx,sny,snz); + sum1 = 0.0; + + for (nx = -nbx; nx <= nbx; nx++) { + argx = MY_PI*kper/nx_pppm + MY_PI*nx; + wx = powsinxx(argx,twoorder); + + for (ny = -nby; ny <= nby; ny++) { + argy = MY_PI*lper/ny_pppm + MY_PI*ny; + wy = powsinxx(argy,twoorder); + + for (nz = -nbz; nz <= nbz; nz++) { + argz = MY_PI*mper/nz_pppm + MY_PI*nz; + wz = powsinxx(argz,twoorder); + + double b[3]; + b[0] = 2.0*MY_PI*nx_pppm*nx; + b[1] = 2.0*MY_PI*ny_pppm*ny; + b[2] = 2.0*MY_PI*nz_pppm*nz; + x2lamdaT(&b[0],&b[0]); + + qx = unitk_lamda[0]+b[0]; + sx = exp(-0.25*square(qx/g_ewald)); + + qy = unitk_lamda[1]+b[1]; + sy = exp(-0.25*square(qy/g_ewald)); + + qz = unitk_lamda[2]+b[2]; + sz = exp(-0.25*square(qz/g_ewald)); + + dot1 = unitk_lamda[0]*qx + unitk_lamda[1]*qy + unitk_lamda[2]*qz; + dot2 = qx*qx+qy*qy+qz*qz; + sum1 += (dot1/dot2) * sx*sy*sz * wx*wy*wz; + } + } + } + greensfn[n++] = numerator*sum1/denominator; + } else greensfn[n++] = 0.0; + } + } + } +} + +/* ---------------------------------------------------------------------- + compute optimized Green's function for energy calculation +------------------------------------------------------------------------- */ + +void PPPM::compute_gf_ad() +{ + const double * const prd = domain->prd; + + const double xprd = prd[0]; + const double yprd = prd[1]; + const double zprd = prd[2]; + const double zprd_slab = zprd*slab_volfactor; + const double unitkx = (MY_2PI/xprd); + const double unitky = (MY_2PI/yprd); + const double unitkz = (MY_2PI/zprd_slab); + + double snx,sny,snz,sqk; + double argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz; + double numerator,denominator; + int k,l,m,n,kper,lper,mper; + + const int twoorder = 2*order; + + for (int i = 0; i < 6; i++) sf_coeff[i] = 0.0; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + qz = unitkz*mper; + snz = square(sin(0.5*qz*zprd_slab/nz_pppm)); + sz = exp(-0.25*square(qz/g_ewald)); + argz = 0.5*qz*zprd_slab/nz_pppm; + wz = powsinxx(argz,twoorder); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + qy = unitky*lper; + sny = square(sin(0.5*qy*yprd/ny_pppm)); + sy = exp(-0.25*square(qy/g_ewald)); + argy = 0.5*qy*yprd/ny_pppm; + wy = powsinxx(argy,twoorder); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + qx = unitkx*kper; + snx = square(sin(0.5*qx*xprd/nx_pppm)); + sx = exp(-0.25*square(qx/g_ewald)); + argx = 0.5*qx*xprd/nx_pppm; + wx = powsinxx(argx,twoorder); + + sqk = qx*qx + qy*qy + qz*qz; + + if (sqk != 0.0) { + numerator = MY_4PI/sqk; + denominator = gf_denom(snx,sny,snz); + greensfn[n] = numerator*sx*sy*sz*wx*wy*wz/denominator; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } else { + greensfn[n] = 0.0; + sf_coeff[0] += sf_precoeff1[n]*greensfn[n]; + sf_coeff[1] += sf_precoeff2[n]*greensfn[n]; + sf_coeff[2] += sf_precoeff3[n]*greensfn[n]; + sf_coeff[3] += sf_precoeff4[n]*greensfn[n]; + sf_coeff[4] += sf_precoeff5[n]*greensfn[n]; + sf_coeff[5] += sf_precoeff6[n]*greensfn[n]; + n++; + } + } + } + } + + // compute the coefficients for the self-force correction + + double prex, prey, prez; + prex = prey = prez = MY_PI/volume; + prex *= nx_pppm/xprd; + prey *= ny_pppm/yprd; + prez *= nz_pppm/zprd_slab; + sf_coeff[0] *= prex; + sf_coeff[1] *= prex*2; + sf_coeff[2] *= prey; + sf_coeff[3] *= prey*2; + sf_coeff[4] *= prez; + sf_coeff[5] *= prez*2; + + // communicate values with other procs + + double tmp[6]; + MPI_Allreduce(sf_coeff,tmp,6,MPI_DOUBLE,MPI_SUM,world); + for (n = 0; n < 6; n++) sf_coeff[n] = tmp[n]; +} + +/* ---------------------------------------------------------------------- + compute self force coefficients for ad-differentiation scheme +------------------------------------------------------------------------- */ + +void PPPM::compute_sf_precoeff() +{ + int i,k,l,m,n; + int nx,ny,nz,kper,lper,mper; + double wx0[5],wy0[5],wz0[5],wx1[5],wy1[5],wz1[5],wx2[5],wy2[5],wz2[5]; + double qx0,qy0,qz0,qx1,qy1,qz1,qx2,qy2,qz2; + double u0,u1,u2,u3,u4,u5,u6; + double sum1,sum2,sum3,sum4,sum5,sum6; + + n = 0; + for (m = nzlo_fft; m <= nzhi_fft; m++) { + mper = m - nz_pppm*(2*m/nz_pppm); + + for (l = nylo_fft; l <= nyhi_fft; l++) { + lper = l - ny_pppm*(2*l/ny_pppm); + + for (k = nxlo_fft; k <= nxhi_fft; k++) { + kper = k - nx_pppm*(2*k/nx_pppm); + + sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0.0; + for (i = 0; i < 5; i++) { + + qx0 = MY_2PI*(kper+nx_pppm*(i-2)); + qx1 = MY_2PI*(kper+nx_pppm*(i-1)); + qx2 = MY_2PI*(kper+nx_pppm*(i )); + wx0[i] = powsinxx(0.5*qx0/nx_pppm,order); + wx1[i] = powsinxx(0.5*qx1/nx_pppm,order); + wx2[i] = powsinxx(0.5*qx2/nx_pppm,order); + + qy0 = MY_2PI*(lper+ny_pppm*(i-2)); + qy1 = MY_2PI*(lper+ny_pppm*(i-1)); + qy2 = MY_2PI*(lper+ny_pppm*(i )); + wy0[i] = powsinxx(0.5*qy0/ny_pppm,order); + wy1[i] = powsinxx(0.5*qy1/ny_pppm,order); + wy2[i] = powsinxx(0.5*qy2/ny_pppm,order); + + qz0 = MY_2PI*(mper+nz_pppm*(i-2)); + qz1 = MY_2PI*(mper+nz_pppm*(i-1)); + qz2 = MY_2PI*(mper+nz_pppm*(i )); + + wz0[i] = powsinxx(0.5*qz0/nz_pppm,order); + wz1[i] = powsinxx(0.5*qz1/nz_pppm,order); + wz2[i] = powsinxx(0.5*qz2/nz_pppm,order); + } + + for (nx = 0; nx < 5; nx++) { + for (ny = 0; ny < 5; ny++) { + for (nz = 0; nz < 5; nz++) { + u0 = wx0[nx]*wy0[ny]*wz0[nz]; + u1 = wx1[nx]*wy0[ny]*wz0[nz]; + u2 = wx2[nx]*wy0[ny]*wz0[nz]; + u3 = wx0[nx]*wy1[ny]*wz0[nz]; + u4 = wx0[nx]*wy2[ny]*wz0[nz]; + u5 = wx0[nx]*wy0[ny]*wz1[nz]; + u6 = wx0[nx]*wy0[ny]*wz2[nz]; + + sum1 += u0*u1; + sum2 += u0*u2; + sum3 += u0*u3; + sum4 += u0*u4; + sum5 += u0*u5; + sum6 += u0*u6; + } + } + } + + // store values + + sf_precoeff1[n] = sum1; + sf_precoeff2[n] = sum2; + sf_precoeff3[n] = sum3; + sf_precoeff4[n] = sum4; + sf_precoeff5[n] = sum5; + sf_precoeff6[n++] = sum6; + } + } + } +} + +/* ---------------------------------------------------------------------- + find center grid pt for each of my particles + check that full stencil for the particle will fit in my 3d brick + store central grid pt indices in part2grid array +------------------------------------------------------------------------- */ + +void PPPM::particle_map() +{ + int nx,ny,nz; + + double **x = atom->x; + int nlocal = atom->nlocal; + + int flag = 0; + for (int i = 0; i < nlocal; i++) { + + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // current particle coord can be outside global and local box + // add/subtract OFFSET to avoid int(-0.75) = 0 when want it to be -1 + + nx = static_cast ((x[i][0]-boxlo[0])*delxinv+shift) - OFFSET; + ny = static_cast ((x[i][1]-boxlo[1])*delyinv+shift) - OFFSET; + nz = static_cast ((x[i][2]-boxlo[2])*delzinv+shift) - OFFSET; + + part2grid[i][0] = nx; + part2grid[i][1] = ny; + part2grid[i][2] = nz; + + // check that entire stencil around nx,ny,nz will fit in my 3d brick + + if (nx+nlower < nxlo_out || nx+nupper > nxhi_out || + ny+nlower < nylo_out || ny+nupper > nyhi_out || + nz+nlower < nzlo_out || nz+nupper > nzhi_out) + flag = 1; + } + + if (flag) error->one(FLERR,"Out of range atoms - cannot compute PPPM"); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid +------------------------------------------------------------------------- */ + +void PPPM::make_rho() +{ + int l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + + // clear 3d density array + + memset(&(density_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + for (int i = 0; i < nlocal; i++) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + density_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } +} + +/* ---------------------------------------------------------------------- + remap density from 3d brick decomposition to FFT decomposition +------------------------------------------------------------------------- */ + +void PPPM::brick2fft() +{ + int n,ix,iy,iz; + + // copy grabs inner portion of density from 3d brick + // remap could be done as pre-stage of FFT, + // but this works optimally on only double values, not complex values + + n = 0; + for (iz = nzlo_in; iz <= nzhi_in; iz++) + for (iy = nylo_in; iy <= nyhi_in; iy++) + for (ix = nxlo_in; ix <= nxhi_in; ix++) + density_fft[n++] = density_brick[iz][iy][ix]; + + remap->perform(density_fft,density_fft,work1); +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver +------------------------------------------------------------------------- */ + +void PPPM::poisson() +{ + if (differentiation_flag == 1) poisson_ad(); + else poisson_ik(); +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for ik +------------------------------------------------------------------------- */ + +void PPPM::poisson_ik() +{ + int i,j,k,n; + double eng; + + // transform charge density (r -> k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] = density_fft[i]; + work1[n++] = ZEROF; + } + + fft1->compute(work1,work1,1); + + // global energy and virial contribution + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + if (eflag_global || vflag_global) { + if (vflag_global) { + n = 0; + for (i = 0; i < nfft; i++) { + eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; + if (eflag_global) energy += eng; + n += 2; + } + } else { + n = 0; + for (i = 0; i < nfft; i++) { + energy += + s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + n += 2; + } + } + } + + // scale by 1/total-grid-pts to get rho(k) + // multiply by Green's function to get V(k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] *= scaleinv * greensfn[i]; + work1[n++] *= scaleinv * greensfn[i]; + } + + // extra FFTs for per-atom energy/virial + + if (evflag_atom) poisson_peratom(); + + // triclinic system + + if (triclinic) { + poisson_ik_triclinic(); + return; + } + + // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) + // FFT leaves data in 3d brick decomposition + // copy it into inner portion of vdx,vdy,vdz arrays + + // x direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkx[i]*work1[n+1]; + work2[n+1] = -fkx[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdx_brick[k][j][i] = work2[n]; + n += 2; + } + + // y direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fky[j]*work1[n+1]; + work2[n+1] = -fky[j]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdy_brick[k][j][i] = work2[n]; + n += 2; + } + + // z direction gradient + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + work2[n] = fkz[k]*work1[n+1]; + work2[n+1] = -fkz[k]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdz_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for ik for a triclinic system +------------------------------------------------------------------------- */ + +void PPPM::poisson_ik_triclinic() +{ + int i,j,k,n; + + // compute gradients of V(r) in each of 3 dims by transformimg -ik*V(k) + // FFT leaves data in 3d brick decomposition + // copy it into inner portion of vdx,vdy,vdz arrays + + // x direction gradient + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = fkx[i]*work1[n+1]; + work2[n+1] = -fkx[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdx_brick[k][j][i] = work2[n]; + n += 2; + } + + // y direction gradient + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = fky[i]*work1[n+1]; + work2[n+1] = -fky[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdy_brick[k][j][i] = work2[n]; + n += 2; + } + + // z direction gradient + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = fkz[i]*work1[n+1]; + work2[n+1] = -fkz[i]*work1[n]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + vdz_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for ad +------------------------------------------------------------------------- */ + +void PPPM::poisson_ad() +{ + int i,j,k,n; + double eng; + + // transform charge density (r -> k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] = density_fft[i]; + work1[n++] = ZEROF; + } + + fft1->compute(work1,work1,1); + + // global energy and virial contribution + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + if (eflag_global || vflag_global) { + if (vflag_global) { + n = 0; + for (i = 0; i < nfft; i++) { + eng = s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + for (j = 0; j < 6; j++) virial[j] += eng*vg[i][j]; + if (eflag_global) energy += eng; + n += 2; + } + } else { + n = 0; + for (i = 0; i < nfft; i++) { + energy += + s2 * greensfn[i] * (work1[n]*work1[n] + work1[n+1]*work1[n+1]); + n += 2; + } + } + } + + // scale by 1/total-grid-pts to get rho(k) + // multiply by Green's function to get V(k) + + n = 0; + for (i = 0; i < nfft; i++) { + work1[n++] *= scaleinv * greensfn[i]; + work1[n++] *= scaleinv * greensfn[i]; + } + + // extra FFTs for per-atom energy/virial + + if (vflag_atom) poisson_peratom(); + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]; + work2[n+1] = work1[n+1]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + u_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for per-atom energy/virial +------------------------------------------------------------------------- */ + +void PPPM::poisson_peratom() +{ + int i,j,k,n; + + // energy + + if (eflag_atom && differentiation_flag != 1) { + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]; + work2[n+1] = work1[n+1]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + u_brick[k][j][i] = work2[n]; + n += 2; + } + } + + // 6 components of virial in v0 thru v5 + + if (!vflag_atom) return; + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][0]; + work2[n+1] = work1[n+1]*vg[i][0]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v0_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][1]; + work2[n+1] = work1[n+1]*vg[i][1]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v1_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][2]; + work2[n+1] = work1[n+1]*vg[i][2]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v2_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][3]; + work2[n+1] = work1[n+1]*vg[i][3]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v3_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][4]; + work2[n+1] = work1[n+1]*vg[i][4]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v4_brick[k][j][i] = work2[n]; + n += 2; + } + + n = 0; + for (i = 0; i < nfft; i++) { + work2[n] = work1[n]*vg[i][5]; + work2[n+1] = work1[n+1]*vg[i][5]; + n += 2; + } + + fft2->compute(work2,work2,-1); + + n = 0; + for (k = nzlo_in; k <= nzhi_in; k++) + for (j = nylo_in; j <= nyhi_in; j++) + for (i = nxlo_in; i <= nxhi_in; i++) { + v5_brick[k][j][i] = work2[n]; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles +------------------------------------------------------------------------- */ + +void PPPM::fieldforce() +{ + if (differentiation_flag == 1) fieldforce_ad(); + else fieldforce_ik(); +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ik +------------------------------------------------------------------------- */ + +void PPPM::fieldforce_ik() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR ekx,eky,ekz; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + ekx -= x0*vdx_brick[mz][my][mx]; + eky -= x0*vdy_brick[mz][my][mx]; + ekz -= x0*vdz_brick[mz][my][mx]; + } + } + } + + // convert E-field to force + + const double qfactor = force->qqrd2e * scale * q[i]; + f[i][0] += qfactor*ekx; + f[i][1] += qfactor*eky; + if (slabflag != 2) f[i][2] += qfactor*ekz; + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get electric field & force on my particles for ad +------------------------------------------------------------------------- */ + +void PPPM::fieldforce_ad() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz; + FFT_SCALAR ekx,eky,ekz; + double s1,s2,s3; + double sf = 0.0; + double *prd; + + prd = domain->prd; + double xprd = prd[0]; + double yprd = prd[1]; + double zprd = prd[2]; + + double hx_inv = nx_pppm/xprd; + double hy_inv = ny_pppm/yprd; + double hz_inv = nz_pppm/zprd; + + // loop over my charges, interpolate electric field from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + // ek = 3 components of E-field on particle + + double *q = atom->q; + double **x = atom->x; + double **f = atom->f; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + compute_drho1d(dx,dy,dz); + + ekx = eky = ekz = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + ekx += drho1d[0][l]*rho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + eky += rho1d[0][l]*drho1d[1][m]*rho1d[2][n]*u_brick[mz][my][mx]; + ekz += rho1d[0][l]*rho1d[1][m]*drho1d[2][n]*u_brick[mz][my][mx]; + } + } + } + ekx *= hx_inv; + eky *= hy_inv; + ekz *= hz_inv; + + // convert E-field to force and substract self forces + + const double qfactor = force->qqrd2e * scale; + + s1 = x[i][0]*hx_inv; + s2 = x[i][1]*hy_inv; + s3 = x[i][2]*hz_inv; + sf = sf_coeff[0]*sin(2*MY_PI*s1); + sf += sf_coeff[1]*sin(4*MY_PI*s1); + sf *= 2*q[i]*q[i]; + f[i][0] += qfactor*(ekx*q[i] - sf); + + sf = sf_coeff[2]*sin(2*MY_PI*s2); + sf += sf_coeff[3]*sin(4*MY_PI*s2); + sf *= 2*q[i]*q[i]; + f[i][1] += qfactor*(eky*q[i] - sf); + + + sf = sf_coeff[4]*sin(2*MY_PI*s3); + sf += sf_coeff[5]*sin(4*MY_PI*s3); + sf *= 2*q[i]*q[i]; + if (slabflag != 2) f[i][2] += qfactor*(ekz*q[i] - sf); + } +} + +/* ---------------------------------------------------------------------- + interpolate from grid to get per-atom energy/virial +------------------------------------------------------------------------- */ + +void PPPM::fieldforce_peratom() +{ + int i,l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + FFT_SCALAR u,v0,v1,v2,v3,v4,v5; + + // loop over my charges, interpolate from nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + + int nlocal = atom->nlocal; + + for (i = 0; i < nlocal; i++) { + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + u = v0 = v1 = v2 = v3 = v4 = v5 = ZEROF; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + z0 = rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + y0 = z0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + x0 = y0*rho1d[0][l]; + if (eflag_atom) u += x0*u_brick[mz][my][mx]; + if (vflag_atom) { + v0 += x0*v0_brick[mz][my][mx]; + v1 += x0*v1_brick[mz][my][mx]; + v2 += x0*v2_brick[mz][my][mx]; + v3 += x0*v3_brick[mz][my][mx]; + v4 += x0*v4_brick[mz][my][mx]; + v5 += x0*v5_brick[mz][my][mx]; + } + } + } + } + + if (eflag_atom) eatom[i] += q[i]*u; + if (vflag_atom) { + vatom[i][0] += q[i]*v0; + vatom[i][1] += q[i]*v1; + vatom[i][2] += q[i]*v2; + vatom[i][3] += q[i]*v3; + vatom[i][4] += q[i]*v4; + vatom[i][5] += q[i]*v5; + } + } +} + +/* ---------------------------------------------------------------------- + pack own values to buf to send to another proc +------------------------------------------------------------------------- */ + +void PPPM::pack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + int n = 0; + + if (flag == FORWARD_IK) { + FFT_SCALAR *xsrc = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *ysrc = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *zsrc = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + buf[n++] = xsrc[list[i]]; + buf[n++] = ysrc[list[i]]; + buf[n++] = zsrc[list[i]]; + } + } else if (flag == FORWARD_AD) { + FFT_SCALAR *src = &u_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + buf[i] = src[list[i]]; + } else if (flag == FORWARD_IK_PERATOM) { + FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + if (eflag_atom) buf[n++] = esrc[list[i]]; + if (vflag_atom) { + buf[n++] = v0src[list[i]]; + buf[n++] = v1src[list[i]]; + buf[n++] = v2src[list[i]]; + buf[n++] = v3src[list[i]]; + buf[n++] = v4src[list[i]]; + buf[n++] = v5src[list[i]]; + } + } + } else if (flag == FORWARD_AD_PERATOM) { + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + buf[n++] = v0src[list[i]]; + buf[n++] = v1src[list[i]]; + buf[n++] = v2src[list[i]]; + buf[n++] = v3src[list[i]]; + buf[n++] = v4src[list[i]]; + buf[n++] = v5src[list[i]]; + } + } +} + +/* ---------------------------------------------------------------------- + unpack another proc's own values from buf and set own ghost values +------------------------------------------------------------------------- */ + +void PPPM::unpack_forward(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + int n = 0; + + if (flag == FORWARD_IK) { + FFT_SCALAR *xdest = &vdx_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *ydest = &vdy_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *zdest = &vdz_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + xdest[list[i]] = buf[n++]; + ydest[list[i]] = buf[n++]; + zdest[list[i]] = buf[n++]; + } + } else if (flag == FORWARD_AD) { + FFT_SCALAR *dest = &u_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + dest[list[i]] = buf[i]; + } else if (flag == FORWARD_IK_PERATOM) { + FFT_SCALAR *esrc = &u_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + if (eflag_atom) esrc[list[i]] = buf[n++]; + if (vflag_atom) { + v0src[list[i]] = buf[n++]; + v1src[list[i]] = buf[n++]; + v2src[list[i]] = buf[n++]; + v3src[list[i]] = buf[n++]; + v4src[list[i]] = buf[n++]; + v5src[list[i]] = buf[n++]; + } + } + } else if (flag == FORWARD_AD_PERATOM) { + FFT_SCALAR *v0src = &v0_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v1src = &v1_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v2src = &v2_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v3src = &v3_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v4src = &v4_brick[nzlo_out][nylo_out][nxlo_out]; + FFT_SCALAR *v5src = &v5_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) { + v0src[list[i]] = buf[n++]; + v1src[list[i]] = buf[n++]; + v2src[list[i]] = buf[n++]; + v3src[list[i]] = buf[n++]; + v4src[list[i]] = buf[n++]; + v5src[list[i]] = buf[n++]; + } + } +} + +/* ---------------------------------------------------------------------- + pack ghost values into buf to send to another proc +------------------------------------------------------------------------- */ + +void PPPM::pack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + if (flag == REVERSE_RHO) { + FFT_SCALAR *src = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + buf[i] = src[list[i]]; + } +} + +/* ---------------------------------------------------------------------- + unpack another proc's ghost values from buf and add to own values +------------------------------------------------------------------------- */ + +void PPPM::unpack_reverse(int flag, FFT_SCALAR *buf, int nlist, int *list) +{ + if (flag == REVERSE_RHO) { + FFT_SCALAR *dest = &density_brick[nzlo_out][nylo_out][nxlo_out]; + for (int i = 0; i < nlist; i++) + dest[list[i]] += buf[i]; + } +} + +/* ---------------------------------------------------------------------- + map nprocs to NX by NY grid as PX by PY procs - return optimal px,py +------------------------------------------------------------------------- */ + +void PPPM::procs2grid2d(int nprocs, int nx, int ny, int *px, int *py) +{ + // loop thru all possible factorizations of nprocs + // surf = surface area of largest proc sub-domain + // innermost if test minimizes surface area and surface/volume ratio + + int bestsurf = 2 * (nx + ny); + int bestboxx = 0; + int bestboxy = 0; + + int boxx,boxy,surf,ipx,ipy; + + ipx = 1; + while (ipx <= nprocs) { + if (nprocs % ipx == 0) { + ipy = nprocs/ipx; + boxx = nx/ipx; + if (nx % ipx) boxx++; + boxy = ny/ipy; + if (ny % ipy) boxy++; + surf = boxx + boxy; + if (surf < bestsurf || + (surf == bestsurf && boxx*boxy > bestboxx*bestboxy)) { + bestsurf = surf; + bestboxx = boxx; + bestboxy = boxy; + *px = ipx; + *py = ipy; + } + } + ipx++; + } +} + +/* ---------------------------------------------------------------------- + charge assignment into rho1d + dx,dy,dz = distance of particle from "lower left" grid point +------------------------------------------------------------------------- */ + +void PPPM::compute_rho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, + const FFT_SCALAR &dz) +{ + int k,l; + FFT_SCALAR r1,r2,r3; + + for (k = (1-order)/2; k <= order/2; k++) { + r1 = r2 = r3 = ZEROF; + + for (l = order-1; l >= 0; l--) { + r1 = rho_coeff[l][k] + r1*dx; + r2 = rho_coeff[l][k] + r2*dy; + r3 = rho_coeff[l][k] + r3*dz; + } + rho1d[0][k] = r1; + rho1d[1][k] = r2; + rho1d[2][k] = r3; + } +} + +/* ---------------------------------------------------------------------- + charge assignment into drho1d + dx,dy,dz = distance of particle from "lower left" grid point +------------------------------------------------------------------------- */ + +void PPPM::compute_drho1d(const FFT_SCALAR &dx, const FFT_SCALAR &dy, + const FFT_SCALAR &dz) +{ + int k,l; + FFT_SCALAR r1,r2,r3; + + for (k = (1-order)/2; k <= order/2; k++) { + r1 = r2 = r3 = ZEROF; + + for (l = order-2; l >= 0; l--) { + r1 = drho_coeff[l][k] + r1*dx; + r2 = drho_coeff[l][k] + r2*dy; + r3 = drho_coeff[l][k] + r3*dz; + } + drho1d[0][k] = r1; + drho1d[1][k] = r2; + drho1d[2][k] = r3; + } +} + +/* ---------------------------------------------------------------------- + generate coeffients for the weight function of order n + + (n-1) + Wn(x) = Sum wn(k,x) , Sum is over every other integer + k=-(n-1) + For k=-(n-1),-(n-1)+2, ....., (n-1)-2,n-1 + k is odd integers if n is even and even integers if n is odd + --- + | n-1 + | Sum a(l,j)*(x-k/2)**l if abs(x-k/2) < 1/2 + wn(k,x) = < l=0 + | + | 0 otherwise + --- + a coeffients are packed into the array rho_coeff to eliminate zeros + rho_coeff(l,((k+mod(n+1,2))/2) = a(l,k) +------------------------------------------------------------------------- */ + +void PPPM::compute_rho_coeff() +{ + int j,k,l,m; + FFT_SCALAR s; + + FFT_SCALAR **a; + memory->create2d_offset(a,order,-order,order,"pppm:a"); + + for (k = -order; k <= order; k++) + for (l = 0; l < order; l++) + a[l][k] = 0.0; + + a[0][0] = 1.0; + for (j = 1; j < order; j++) { + for (k = -j; k <= j; k += 2) { + s = 0.0; + for (l = 0; l < j; l++) { + a[l+1][k] = (a[l][k+1]-a[l][k-1]) / (l+1); +#ifdef FFT_SINGLE + s += powf(0.5,(float) l+1) * + (a[l][k-1] + powf(-1.0,(float) l) * a[l][k+1]) / (l+1); +#else + s += pow(0.5,(double) l+1) * + (a[l][k-1] + pow(-1.0,(double) l) * a[l][k+1]) / (l+1); +#endif + } + a[0][k] = s; + } + } + + m = (1-order)/2; + for (k = -(order-1); k < order; k += 2) { + for (l = 0; l < order; l++) + rho_coeff[l][m] = a[l][k]; + for (l = 1; l < order; l++) + drho_coeff[l-1][m] = l*a[l][k]; + m++; + } + + memory->destroy2d_offset(a,-order); +} + +/* ---------------------------------------------------------------------- + Slab-geometry correction term to dampen inter-slab interactions between + periodically repeating slabs. Yields good approximation to 2D Ewald if + adequate empty space is left between repeating slabs (J. Chem. Phys. + 111, 3155). Slabs defined here to be parallel to the xy plane. +------------------------------------------------------------------------- */ + +void PPPM::slabcorr() +{ + // compute local contribution to global dipole moment + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + + double dipole = 0.0; + for (int i = 0; i < nlocal; i++) dipole += q[i]*x[i][2]; + + // sum local contributions to get global dipole moment + + double dipole_all; + MPI_Allreduce(&dipole,&dipole_all,1,MPI_DOUBLE,MPI_SUM,world); + + // compute corrections + + const double e_slabcorr = MY_2PI*dipole_all*dipole_all/volume; + const double qscale = force->qqrd2e * scale; + + if (eflag_global) energy += qscale * e_slabcorr; + + // per-atom energy + + if (eflag_atom) { + double efact = MY_2PI*dipole_all/volume; + for (int i = 0; i < nlocal; i++) eatom[i] += qscale * q[i]*x[i][2]*efact; + } + + // add on force corrections + + double ffact = -4.0*MY_PI*dipole_all/volume; + double **f = atom->f; + + for (int i = 0; i < nlocal; i++) f[i][2] += qscale * q[i]*ffact; +} + +/* ---------------------------------------------------------------------- + perform and time the 1d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPM::timing_1d(int n, double &time1d) +{ + double time1,time2; + + for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->timing1d(work1,nfft_both,1); + fft2->timing1d(work1,nfft_both,-1); + if (differentiation_flag != 1) { + fft2->timing1d(work1,nfft_both,-1); + fft2->timing1d(work1,nfft_both,-1); + } + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time1d = time2 - time1; + + if (differentiation_flag) return 2; + return 4; +} + +/* ---------------------------------------------------------------------- + perform and time the 3d FFTs required for N timesteps +------------------------------------------------------------------------- */ + +int PPPM::timing_3d(int n, double &time3d) +{ + double time1,time2; + + for (int i = 0; i < 2*nfft_both; i++) work1[i] = ZEROF; + + MPI_Barrier(world); + time1 = MPI_Wtime(); + + for (int i = 0; i < n; i++) { + fft1->compute(work1,work1,1); + fft2->compute(work1,work1,-1); + if (differentiation_flag != 1) { + fft2->compute(work1,work1,-1); + fft2->compute(work1,work1,-1); + } + } + + MPI_Barrier(world); + time2 = MPI_Wtime(); + time3d = time2 - time1; + + if (differentiation_flag) return 2; + return 4; +} + +/* ---------------------------------------------------------------------- + memory usage of local arrays +------------------------------------------------------------------------- */ + +double PPPM::memory_usage() +{ + double bytes = nmax*3 * sizeof(double); + int nbrick = (nxhi_out-nxlo_out+1) * (nyhi_out-nylo_out+1) * + (nzhi_out-nzlo_out+1); + if (differentiation_flag == 1) { + bytes += 2 * nbrick * sizeof(FFT_SCALAR); + } else { + bytes += 4 * nbrick * sizeof(FFT_SCALAR); + } + if (triclinic) bytes += 3 * nfft_both * sizeof(double); + bytes += 6 * nfft_both * sizeof(double); + bytes += nfft_both * sizeof(double); + bytes += nfft_both*5 * sizeof(FFT_SCALAR); + + if (peratom_allocate_flag) + bytes += 6 * nbrick * sizeof(FFT_SCALAR); + + if (group_allocate_flag) { + bytes += 2 * nbrick * sizeof(FFT_SCALAR); + bytes += 2 * nfft_both * sizeof(FFT_SCALAR);; + } + + bytes += cg->memory_usage(); + + return bytes; +} + +/* ---------------------------------------------------------------------- + group-group interactions + ------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- + compute the PPPM total long-range force and energy for groups A and B + ------------------------------------------------------------------------- */ + +void PPPM::compute_group_group(int groupbit_A, int groupbit_B, int BA_flag) +{ + if (slabflag) + error->all(FLERR,"Cannot (yet) use K-space slab " + "correction with compute group/group"); + + if (differentiation_flag) + error->all(FLERR,"Cannot (yet) use 'kspace_modify " + "diff ad' with compute group/group"); + + if (!group_allocate_flag) allocate_groups(); + + // convert atoms from box to lamda coords + + if (triclinic == 0) boxlo = domain->boxlo; + else { + boxlo = domain->boxlo_lamda; + domain->x2lamda(atom->nlocal); + } + + e2group = 0; //energy + f2group[0] = 0; //force in x-direction + f2group[1] = 0; //force in y-direction + f2group[2] = 0; //force in z-direction + + // map my particle charge onto my local 3d density grid + + make_rho_groups(groupbit_A,groupbit_B,BA_flag); + + // all procs communicate density values from their ghost cells + // to fully sum contribution in their 3d bricks + // remap from 3d decomposition to FFT decomposition + + // temporarily store and switch pointers so we can + // use brick2fft() for groups A and B (without + // writing an additional function) + + FFT_SCALAR ***density_brick_real = density_brick; + FFT_SCALAR *density_fft_real = density_fft; + + // group A + + density_brick = density_A_brick; + density_fft = density_A_fft; + + cg->reverse_comm(this,REVERSE_RHO); + brick2fft(); + + // group B + + density_brick = density_B_brick; + density_fft = density_B_fft; + + cg->reverse_comm(this,REVERSE_RHO); + brick2fft(); + + // switch back pointers + + density_brick = density_brick_real; + density_fft = density_fft_real; + + // compute potential gradient on my FFT grid and + // portion of group-group energy/force on this proc's FFT grid + + poisson_groups(BA_flag); + + const double qscale = force->qqrd2e * scale; + + // total group A <--> group B energy + // self and boundary correction terms are in compute_group_group.cpp + + double e2group_all; + MPI_Allreduce(&e2group,&e2group_all,1,MPI_DOUBLE,MPI_SUM,world); + e2group = e2group_all; + + e2group *= qscale*0.5*volume; + + // total group A <--> group B force + + double f2group_all[3]; + MPI_Allreduce(f2group,f2group_all,3,MPI_DOUBLE,MPI_SUM,world); + + for (int i = 0; i < 3; i++) f2group[i] = qscale*volume*f2group_all[i]; + + // convert atoms back from lamda to box coords + + if (triclinic) domain->lamda2x(atom->nlocal); +} + +/* ---------------------------------------------------------------------- + allocate group-group memory that depends on # of K-vectors and order + ------------------------------------------------------------------------- */ + +void PPPM::allocate_groups() +{ + group_allocate_flag = 1; + + memory->create3d_offset(density_A_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_A_brick"); + memory->create3d_offset(density_B_brick,nzlo_out,nzhi_out,nylo_out,nyhi_out, + nxlo_out,nxhi_out,"pppm:density_B_brick"); + memory->create(density_A_fft,nfft_both,"pppm:density_A_fft"); + memory->create(density_B_fft,nfft_both,"pppm:density_B_fft"); +} + +/* ---------------------------------------------------------------------- + deallocate group-group memory that depends on # of K-vectors and order + ------------------------------------------------------------------------- */ + +void PPPM::deallocate_groups() +{ + group_allocate_flag = 0; + + memory->destroy3d_offset(density_A_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy3d_offset(density_B_brick,nzlo_out,nylo_out,nxlo_out); + memory->destroy(density_A_fft); + memory->destroy(density_B_fft); +} + +/* ---------------------------------------------------------------------- + create discretized "density" on section of global grid due to my particles + density(x,y,z) = charge "density" at grid points of my 3d brick + (nxlo:nxhi,nylo:nyhi,nzlo:nzhi) is extent of my brick (including ghosts) + in global grid for group-group interactions + ------------------------------------------------------------------------- */ + +void PPPM::make_rho_groups(int groupbit_A, int groupbit_B, int BA_flag) +{ + int l,m,n,nx,ny,nz,mx,my,mz; + FFT_SCALAR dx,dy,dz,x0,y0,z0; + + // clear 3d density arrays + + memset(&(density_A_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + memset(&(density_B_brick[nzlo_out][nylo_out][nxlo_out]),0, + ngrid*sizeof(FFT_SCALAR)); + + // loop over my charges, add their contribution to nearby grid points + // (nx,ny,nz) = global coords of grid pt to "lower left" of charge + // (dx,dy,dz) = distance to "lower left" grid pt + // (mx,my,mz) = global coords of moving stencil pt + + double *q = atom->q; + double **x = atom->x; + int nlocal = atom->nlocal; + int *mask = atom->mask; + + for (int i = 0; i < nlocal; i++) { + + if ((mask[i] & groupbit_A) && (mask[i] & groupbit_B)) + if (BA_flag) continue; + + if ((mask[i] & groupbit_A) || (mask[i] & groupbit_B)) { + + nx = part2grid[i][0]; + ny = part2grid[i][1]; + nz = part2grid[i][2]; + dx = nx+shiftone - (x[i][0]-boxlo[0])*delxinv; + dy = ny+shiftone - (x[i][1]-boxlo[1])*delyinv; + dz = nz+shiftone - (x[i][2]-boxlo[2])*delzinv; + + compute_rho1d(dx,dy,dz); + + z0 = delvolinv * q[i]; + for (n = nlower; n <= nupper; n++) { + mz = n+nz; + y0 = z0*rho1d[2][n]; + for (m = nlower; m <= nupper; m++) { + my = m+ny; + x0 = y0*rho1d[1][m]; + for (l = nlower; l <= nupper; l++) { + mx = l+nx; + + // group A + + if (mask[i] & groupbit_A) + density_A_brick[mz][my][mx] += x0*rho1d[0][l]; + + // group B + + if (mask[i] & groupbit_B) + density_B_brick[mz][my][mx] += x0*rho1d[0][l]; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for group-group interactions + ------------------------------------------------------------------------- */ + +void PPPM::poisson_groups(int BA_flag) +{ + int i,j,k,n; + + // reuse memory (already declared) + + FFT_SCALAR *work_A = work1; + FFT_SCALAR *work_B = work2; + + // transform charge density (r -> k) + + // group A + + n = 0; + for (i = 0; i < nfft; i++) { + work_A[n++] = density_A_fft[i]; + work_A[n++] = ZEROF; + } + + fft1->compute(work_A,work_A,1); + + // group B + + n = 0; + for (i = 0; i < nfft; i++) { + work_B[n++] = density_B_fft[i]; + work_B[n++] = ZEROF; + } + + fft1->compute(work_B,work_B,1); + + // group-group energy and force contribution, + // keep everything in reciprocal space so + // no inverse FFTs needed + + double scaleinv = 1.0/(nx_pppm*ny_pppm*nz_pppm); + double s2 = scaleinv*scaleinv; + + // energy + + n = 0; + for (i = 0; i < nfft; i++) { + e2group += s2 * greensfn[i] * + (work_A[n]*work_B[n] + work_A[n+1]*work_B[n+1]); + n += 2; + } + + if (BA_flag) return; + + + // multiply by Green's function and s2 + // (only for work_A so it is not squared below) + + n = 0; + for (i = 0; i < nfft; i++) { + work_A[n++] *= s2 * greensfn[i]; + work_A[n++] *= s2 * greensfn[i]; + } + + // triclinic system + + if (triclinic) { + poisson_groups_triclinic(); + return; + } + + double partial_group; + + // force, x direction + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[0] += fkx[i] * partial_group; + n += 2; + } + + // force, y direction + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[1] += fky[j] * partial_group; + n += 2; + } + + // force, z direction + + n = 0; + for (k = nzlo_fft; k <= nzhi_fft; k++) + for (j = nylo_fft; j <= nyhi_fft; j++) + for (i = nxlo_fft; i <= nxhi_fft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[2] += fkz[k] * partial_group; + n += 2; + } +} + +/* ---------------------------------------------------------------------- + FFT-based Poisson solver for group-group interactions + for a triclinic system + ------------------------------------------------------------------------- */ + +void PPPM::poisson_groups_triclinic() +{ + int i,j,k,n; + + // reuse memory (already declared) + + FFT_SCALAR *work_A = work1; + FFT_SCALAR *work_B = work2; + + double partial_group; + + // force, x direction + + n = 0; + for (i = 0; i < nfft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[0] += fkx[i] * partial_group; + n += 2; + } + + // force, y direction + + n = 0; + for (i = 0; i < nfft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[1] += fky[i] * partial_group; + n += 2; + } + + // force, z direction + + n = 0; + for (i = 0; i < nfft; i++) { + partial_group = work_A[n+1]*work_B[n] - work_A[n]*work_B[n+1]; + f2group[2] += fkz[i] * partial_group; + n += 2; + } +} diff --git a/src/KSPACE/pppm.h b/src/KSPACE/pppm.h index 133d425b6d..a9686487aa 100644 --- a/src/KSPACE/pppm.h +++ b/src/KSPACE/pppm.h @@ -1,335 +1,335 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 KSPACE_CLASS - -KSpaceStyle(pppm,PPPM) - -#else - -#ifndef LMP_PPPM_H -#define LMP_PPPM_H - -#include "lmptype.h" -#include "mpi.h" - -#ifdef FFT_SINGLE -typedef float FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_FLOAT -#else -typedef double FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_DOUBLE -#endif - -#include "kspace.h" - -namespace LAMMPS_NS { - -class PPPM : public KSpace { - public: - PPPM(class LAMMPS *, int, char **); - virtual ~PPPM(); - virtual void init(); - virtual void setup(); - void setup_grid(); - virtual void compute(int, int); - virtual int timing_1d(int, double &); - virtual int timing_3d(int, double &); - virtual double memory_usage(); - - virtual void compute_group_group(int, int, int); - - protected: - int me,nprocs; - int nfactors; - int *factors; - double qsum,qsqsum,q2; - double cutoff; - double volume; - double delxinv,delyinv,delzinv,delvolinv; - double h_x,h_y,h_z; - double shift,shiftone; - int peratom_allocate_flag; - - int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; - int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; - int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; - int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; - int nlower,nupper; - int ngrid,nfft,nfft_both; - - FFT_SCALAR ***density_brick; - FFT_SCALAR ***vdx_brick,***vdy_brick,***vdz_brick; - FFT_SCALAR ***u_brick; - FFT_SCALAR ***v0_brick,***v1_brick,***v2_brick; - FFT_SCALAR ***v3_brick,***v4_brick,***v5_brick; - double *greensfn; - double **vg; - double *fkx,*fky,*fkz; - FFT_SCALAR *density_fft; - FFT_SCALAR *work1,*work2; - - double *gf_b; - FFT_SCALAR **rho1d,**rho_coeff,**drho1d,**drho_coeff; - double *sf_precoeff1, *sf_precoeff2, *sf_precoeff3; - double *sf_precoeff4, *sf_precoeff5, *sf_precoeff6; - double sf_coeff[6]; // coefficients for calculating ad self-forces - double **acons; - - // group-group interactions - - int group_allocate_flag; - FFT_SCALAR ***density_A_brick,***density_B_brick; - FFT_SCALAR *density_A_fft,*density_B_fft; - - class FFT3d *fft1,*fft2; - class Remap *remap; - class CommGrid *cg; - class CommGrid *cg_peratom; - - int **part2grid; // storage for particle -> grid mapping - int nmax; - - double *boxlo; - // TIP4P settings - int typeH,typeO; // atom types of TIP4P water H and O atoms - double qdist; // distance from O site to negative charge - double alpha; // geometric factor - - void set_grid_global(); - void set_grid_local(); - void adjust_gewald(); - double newton_raphson_f(); - double derivf(); - double final_accuracy(); - - virtual void allocate(); - virtual void allocate_peratom(); - virtual void deallocate(); - virtual void deallocate_peratom(); - int factorable(int); - double compute_df_kspace(); - double estimate_ik_error(double, double, bigint); - virtual double compute_qopt(); - virtual void compute_gf_denom(); - virtual void compute_gf_ik(); - virtual void compute_gf_ad(); - void compute_sf_precoeff(); - - virtual void particle_map(); - virtual void make_rho(); - virtual void brick2fft(); - - virtual void poisson(); - virtual void poisson_ik(); - virtual void poisson_ad(); - - virtual void fieldforce(); - virtual void fieldforce_ik(); - virtual void fieldforce_ad(); - - virtual void poisson_peratom(); - virtual void fieldforce_peratom(); - void procs2grid2d(int,int,int,int *, int*); - void compute_rho1d(const FFT_SCALAR &, const FFT_SCALAR &, - const FFT_SCALAR &); - void compute_drho1d(const FFT_SCALAR &, const FFT_SCALAR &, - const FFT_SCALAR &); - void compute_rho_coeff(); - void slabcorr(); - - // grid communication - - virtual void pack_forward(int, FFT_SCALAR *, int, int *); - virtual void unpack_forward(int, FFT_SCALAR *, int, int *); - virtual void pack_reverse(int, FFT_SCALAR *, int, int *); - virtual void unpack_reverse(int, FFT_SCALAR *, int, int *); - - // triclinic - - int triclinic; // domain settings, orthog or triclinic - void setup_triclinic(); - void compute_gf_ik_triclinic(); - void poisson_ik_triclinic(); - void poisson_groups_triclinic(); - - // group-group interactions - - virtual void allocate_groups(); - virtual void deallocate_groups(); - virtual void make_rho_groups(int, int, int); - virtual void poisson_groups(int); - -/* ---------------------------------------------------------------------- - denominator for Hockney-Eastwood Green's function - of x,y,z = sin(kx*deltax/2), etc - - inf n-1 - S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l - j=-inf l=0 - - = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) - gf_b = denominator expansion coeffs -------------------------------------------------------------------------- */ - - inline double gf_denom(const double &x, const double &y, - const double &z) const { - double sx,sy,sz; - sz = sy = sx = 0.0; - for (int l = order-1; l >= 0; l--) { - sx = gf_b[l] + sx*x; - sy = gf_b[l] + sy*y; - sz = gf_b[l] + sz*z; - } - double s = sx*sy*sz; - return s*s; - }; -}; - -} - -#endif -#endif - -/* ERROR/WARNING messages: - -E: Illegal ... command - -Self-explanatory. Check the input script syntax and compare to the -documentation for the command. You can use -echo screen as a -command-line option when running LAMMPS to see the offending line. - -E: Cannot (yet) use PPPM with triclinic box and 'kspace_modify diff ad' - -This feature is not yet supported. - -E: Cannot (yet) use PPPM with triclinic box and slab correction - -This feature is not yet supported. - -E: Cannot use PPPM with 2d simulation - -The kspace style pppm cannot be used in 2d simulations. You can use -2d PPPM in a 3d simulation; see the kspace_modify command. - -E: Kspace style requires atom attribute q - -The atom style defined does not have these attributes. - -E: Cannot use nonperiodic boundaries with PPPM - -For kspace style pppm, all 3 dimensions must have periodic boundaries -unless you use the kspace_modify command to define a 2d slab with a -non-periodic z dimension. - -E: Incorrect boundaries with slab PPPM - -Must have periodic x,y dimensions and non-periodic z dimension to use -2d slab option with PPPM. - -E: PPPM order cannot be < 2 or > than %d - -This is a limitation of the PPPM implementation in LAMMPS. - -E: KSpace style is incompatible with Pair style - -Setting a kspace style requires that a pair style with a long-range -Coulombic or dispersion component be used. - -E: Bond and angle potentials must be defined for TIP4P - -Cannot use TIP4P pair potential unless bond and angle potentials -are defined. - -E: Bad TIP4P angle type for PPPM/TIP4P - -Specified angle type is not valid. - -E: Bad TIP4P bond type for PPPM/TIP4P - -Specified bond type is not valid. - -E: Cannot (yet) use PPPM with triclinic box and TIP4P - -This feature is not yet supported. - -E: Cannot use kspace solver on system with no charge - -No atoms in system have a non-zero charge. - -W: System is not charge neutral, net charge = %g - -The total charge on all atoms on the system is not 0.0, which -is not valid for the long-range Coulombic solvers. - -W: Reducing PPPM order b/c stencil extends beyond nearest neighbor processor - -This may lead to a larger grid than desired. See the kspace_modify overlap -command to prevent changing of the PPPM order. - -E: PPPM order < minimum allowed order - -The default minimum order is 2. This can be reset by the -kspace_modify minorder command. - -E: PPPM grid stencil extends beyond nearest neighbor processor - -This is not allowed if the kspace_modify overlap setting is no. - -E: KSpace accuracy must be > 0 - -The kspace accuracy designated in the input must be greater than zero. - -E: Could not compute grid size - -The code is unable to compute a grid size consistent with the desired -accuracy. This error should not occur for typical problems. Please -send an email to the developers. - -E: PPPM grid is too large - -The global PPPM grid is larger than OFFSET in one or more dimensions. -OFFSET is currently set to 4096. You likely need to decrease the -requested accuracy. - -E: Could not compute g_ewald - -The Newton-Raphson solver failed to converge to a good value for -g_ewald. This error should not occur for typical problems. Please -send an email to the developers. - -E: Out of range atoms - cannot compute PPPM - -One or more atoms are attempting to map their charge to a PPPM grid -point that is not owned by a processor. This is likely for one of two -reasons, both of them bad. First, it may mean that an atom near the -boundary of a processor's sub-domain has moved more than 1/2 the -"neighbor skin distance"_neighbor.html without neighbor lists being -rebuilt and atoms being migrated to new processors. This also means -you may be missing pairwise interactions that need to be computed. -The solution is to change the re-neighboring criteria via the -"neigh_modify"_neigh_modify command. The safest settings are "delay 0 -every 1 check yes". Second, it may mean that an atom has moved far -outside a processor's sub-domain or even the entire simulation box. -This indicates bad physics, e.g. due to highly overlapping atoms, too -large a timestep, etc. - -E: Cannot (yet) use K-space slab correction with compute group/group - -This option is not yet supported. - -E: Cannot (yet) use 'kspace_modify diff ad' with compute group/group - -This option is not yet supported. - -*/ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 KSPACE_CLASS + +KSpaceStyle(pppm,PPPM) + +#else + +#ifndef LMP_PPPM_H +#define LMP_PPPM_H + +#include "lmptype.h" +#include "mpi.h" + +#ifdef FFT_SINGLE +typedef float FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_FLOAT +#else +typedef double FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_DOUBLE +#endif + +#include "kspace.h" + +namespace LAMMPS_NS { + +class PPPM : public KSpace { + public: + PPPM(class LAMMPS *, int, char **); + virtual ~PPPM(); + virtual void init(); + virtual void setup(); + void setup_grid(); + virtual void compute(int, int); + virtual int timing_1d(int, double &); + virtual int timing_3d(int, double &); + virtual double memory_usage(); + + virtual void compute_group_group(int, int, int); + + protected: + int me,nprocs; + int nfactors; + int *factors; + double qsum,qsqsum,q2; + double cutoff; + double volume; + double delxinv,delyinv,delzinv,delvolinv; + double h_x,h_y,h_z; + double shift,shiftone; + int peratom_allocate_flag; + + int nxlo_in,nylo_in,nzlo_in,nxhi_in,nyhi_in,nzhi_in; + int nxlo_out,nylo_out,nzlo_out,nxhi_out,nyhi_out,nzhi_out; + int nxlo_ghost,nxhi_ghost,nylo_ghost,nyhi_ghost,nzlo_ghost,nzhi_ghost; + int nxlo_fft,nylo_fft,nzlo_fft,nxhi_fft,nyhi_fft,nzhi_fft; + int nlower,nupper; + int ngrid,nfft,nfft_both; + + FFT_SCALAR ***density_brick; + FFT_SCALAR ***vdx_brick,***vdy_brick,***vdz_brick; + FFT_SCALAR ***u_brick; + FFT_SCALAR ***v0_brick,***v1_brick,***v2_brick; + FFT_SCALAR ***v3_brick,***v4_brick,***v5_brick; + double *greensfn; + double **vg; + double *fkx,*fky,*fkz; + FFT_SCALAR *density_fft; + FFT_SCALAR *work1,*work2; + + double *gf_b; + FFT_SCALAR **rho1d,**rho_coeff,**drho1d,**drho_coeff; + double *sf_precoeff1, *sf_precoeff2, *sf_precoeff3; + double *sf_precoeff4, *sf_precoeff5, *sf_precoeff6; + double sf_coeff[6]; // coefficients for calculating ad self-forces + double **acons; + + // group-group interactions + + int group_allocate_flag; + FFT_SCALAR ***density_A_brick,***density_B_brick; + FFT_SCALAR *density_A_fft,*density_B_fft; + + class FFT3d *fft1,*fft2; + class Remap *remap; + class CommGrid *cg; + class CommGrid *cg_peratom; + + int **part2grid; // storage for particle -> grid mapping + int nmax; + + double *boxlo; + // TIP4P settings + int typeH,typeO; // atom types of TIP4P water H and O atoms + double qdist; // distance from O site to negative charge + double alpha; // geometric factor + + void set_grid_global(); + void set_grid_local(); + void adjust_gewald(); + double newton_raphson_f(); + double derivf(); + double final_accuracy(); + + virtual void allocate(); + virtual void allocate_peratom(); + virtual void deallocate(); + virtual void deallocate_peratom(); + int factorable(int); + double compute_df_kspace(); + double estimate_ik_error(double, double, bigint); + virtual double compute_qopt(); + virtual void compute_gf_denom(); + virtual void compute_gf_ik(); + virtual void compute_gf_ad(); + void compute_sf_precoeff(); + + virtual void particle_map(); + virtual void make_rho(); + virtual void brick2fft(); + + virtual void poisson(); + virtual void poisson_ik(); + virtual void poisson_ad(); + + virtual void fieldforce(); + virtual void fieldforce_ik(); + virtual void fieldforce_ad(); + + virtual void poisson_peratom(); + virtual void fieldforce_peratom(); + void procs2grid2d(int,int,int,int *, int*); + void compute_rho1d(const FFT_SCALAR &, const FFT_SCALAR &, + const FFT_SCALAR &); + void compute_drho1d(const FFT_SCALAR &, const FFT_SCALAR &, + const FFT_SCALAR &); + void compute_rho_coeff(); + void slabcorr(); + + // grid communication + + virtual void pack_forward(int, FFT_SCALAR *, int, int *); + virtual void unpack_forward(int, FFT_SCALAR *, int, int *); + virtual void pack_reverse(int, FFT_SCALAR *, int, int *); + virtual void unpack_reverse(int, FFT_SCALAR *, int, int *); + + // triclinic + + int triclinic; // domain settings, orthog or triclinic + void setup_triclinic(); + void compute_gf_ik_triclinic(); + void poisson_ik_triclinic(); + void poisson_groups_triclinic(); + + // group-group interactions + + virtual void allocate_groups(); + virtual void deallocate_groups(); + virtual void make_rho_groups(int, int, int); + virtual void poisson_groups(int); + +/* ---------------------------------------------------------------------- + denominator for Hockney-Eastwood Green's function + of x,y,z = sin(kx*deltax/2), etc + + inf n-1 + S(n,k) = Sum W(k+pi*j)**2 = Sum b(l)*(z*z)**l + j=-inf l=0 + + = -(z*z)**n /(2n-1)! * (d/dx)**(2n-1) cot(x) at z = sin(x) + gf_b = denominator expansion coeffs +------------------------------------------------------------------------- */ + + inline double gf_denom(const double &x, const double &y, + const double &z) const { + double sx,sy,sz; + sz = sy = sx = 0.0; + for (int l = order-1; l >= 0; l--) { + sx = gf_b[l] + sx*x; + sy = gf_b[l] + sy*y; + sz = gf_b[l] + sz*z; + } + double s = sx*sy*sz; + return s*s; + }; +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Cannot (yet) use PPPM with triclinic box and 'kspace_modify diff ad' + +This feature is not yet supported. + +E: Cannot (yet) use PPPM with triclinic box and slab correction + +This feature is not yet supported. + +E: Cannot use PPPM with 2d simulation + +The kspace style pppm cannot be used in 2d simulations. You can use +2d PPPM in a 3d simulation; see the kspace_modify command. + +E: Kspace style requires atom attribute q + +The atom style defined does not have these attributes. + +E: Cannot use nonperiodic boundaries with PPPM + +For kspace style pppm, all 3 dimensions must have periodic boundaries +unless you use the kspace_modify command to define a 2d slab with a +non-periodic z dimension. + +E: Incorrect boundaries with slab PPPM + +Must have periodic x,y dimensions and non-periodic z dimension to use +2d slab option with PPPM. + +E: PPPM order cannot be < 2 or > than %d + +This is a limitation of the PPPM implementation in LAMMPS. + +E: KSpace style is incompatible with Pair style + +Setting a kspace style requires that a pair style with a long-range +Coulombic or dispersion component be used. + +E: Bond and angle potentials must be defined for TIP4P + +Cannot use TIP4P pair potential unless bond and angle potentials +are defined. + +E: Bad TIP4P angle type for PPPM/TIP4P + +Specified angle type is not valid. + +E: Bad TIP4P bond type for PPPM/TIP4P + +Specified bond type is not valid. + +E: Cannot (yet) use PPPM with triclinic box and TIP4P + +This feature is not yet supported. + +E: Cannot use kspace solver on system with no charge + +No atoms in system have a non-zero charge. + +W: System is not charge neutral, net charge = %g + +The total charge on all atoms on the system is not 0.0, which +is not valid for the long-range Coulombic solvers. + +W: Reducing PPPM order b/c stencil extends beyond nearest neighbor processor + +This may lead to a larger grid than desired. See the kspace_modify overlap +command to prevent changing of the PPPM order. + +E: PPPM order < minimum allowed order + +The default minimum order is 2. This can be reset by the +kspace_modify minorder command. + +E: PPPM grid stencil extends beyond nearest neighbor processor + +This is not allowed if the kspace_modify overlap setting is no. + +E: KSpace accuracy must be > 0 + +The kspace accuracy designated in the input must be greater than zero. + +E: Could not compute grid size + +The code is unable to compute a grid size consistent with the desired +accuracy. This error should not occur for typical problems. Please +send an email to the developers. + +E: PPPM grid is too large + +The global PPPM grid is larger than OFFSET in one or more dimensions. +OFFSET is currently set to 4096. You likely need to decrease the +requested accuracy. + +E: Could not compute g_ewald + +The Newton-Raphson solver failed to converge to a good value for +g_ewald. This error should not occur for typical problems. Please +send an email to the developers. + +E: Out of range atoms - cannot compute PPPM + +One or more atoms are attempting to map their charge to a PPPM grid +point that is not owned by a processor. This is likely for one of two +reasons, both of them bad. First, it may mean that an atom near the +boundary of a processor's sub-domain has moved more than 1/2 the +"neighbor skin distance"_neighbor.html without neighbor lists being +rebuilt and atoms being migrated to new processors. This also means +you may be missing pairwise interactions that need to be computed. +The solution is to change the re-neighboring criteria via the +"neigh_modify"_neigh_modify command. The safest settings are "delay 0 +every 1 check yes". Second, it may mean that an atom has moved far +outside a processor's sub-domain or even the entire simulation box. +This indicates bad physics, e.g. due to highly overlapping atoms, too +large a timestep, etc. + +E: Cannot (yet) use K-space slab correction with compute group/group + +This option is not yet supported. + +E: Cannot (yet) use 'kspace_modify diff ad' with compute group/group + +This option is not yet supported. + +*/ From b292458b401cfffacd0f6a1f63053bac4f0c326f Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:52:42 +0000 Subject: [PATCH 33/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10135 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/kspace.cpp | 894 ++++++++++++++++++++++++------------------------- src/kspace.h | 400 +++++++++++----------- 2 files changed, 647 insertions(+), 647 deletions(-) diff --git a/src/kspace.cpp b/src/kspace.cpp index ed158df815..dad7ab8815 100644 --- a/src/kspace.cpp +++ b/src/kspace.cpp @@ -1,447 +1,447 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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 "stdlib.h" -#include "string.h" -#include "kspace.h" -#include "atom.h" -#include "comm.h" -#include "force.h" -#include "pair.h" -#include "memory.h" -#include "atom_masks.h" -#include "error.h" -#include "suffix.h" -#include "domain.h" - -using namespace LAMMPS_NS; - -/* ---------------------------------------------------------------------- */ - -KSpace::KSpace(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) -{ - energy = 0.0; - virial[0] = virial[1] = virial[2] = virial[3] = virial[4] = virial[5] = 0.0; - - triclinic_support = 1; - ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = dipoleflag = 0; - compute_flag = 1; - group_group_enable = 0; - stagger_flag = 0; - - order = 5; - gridflag = 0; - gewaldflag = 0; - minorder = 2; - overlap_allowed = 1; - fftbench = 1; - - order_6 = 5; - gridflag_6 = 0; - gewaldflag_6 = 0; - - slabflag = 0; - differentiation_flag = 0; - slab_volfactor = 1; - suffix_flag = Suffix::NONE; - adjust_cutoff_flag = 1; - - accuracy_absolute = -1.0; - two_charge_force = force->qqr2e * - (force->qelectron * force->qelectron) / - (force->angstrom * force->angstrom); - - maxeatom = maxvatom = 0; - eatom = NULL; - vatom = NULL; - - datamask = ALL_MASK; - datamask_ext = ALL_MASK; - - memory->create(gcons,7,7,"kspace:gcons"); - gcons[2][0] = 15.0 / 8.0; - gcons[2][1] = -5.0 / 4.0; - gcons[2][2] = 3.0 / 8.0; - gcons[3][0] = 35.0 / 16.0; - gcons[3][1] = -35.0 / 16.0; - gcons[3][2] = 21.0 / 16.0; - gcons[3][3] = -5.0 / 16.0; - gcons[4][0] = 315.0 / 128.0; - gcons[4][1] = -105.0 / 32.0; - gcons[4][2] = 189.0 / 64.0; - gcons[4][3] = -45.0 / 32.0; - gcons[4][4] = 35.0 / 128.0; - gcons[5][0] = 693.0 / 256.0; - gcons[5][1] = -1155.0 / 256.0; - gcons[5][2] = 693.0 / 128.0; - gcons[5][3] = -495.0 / 128.0; - gcons[5][4] = 385.0 / 256.0; - gcons[5][5] = -63.0 / 256.0; - gcons[6][0] = 3003.0 / 1024.0; - gcons[6][1] = -3003.0 / 512.0; - gcons[6][2] = 9009.0 / 1024.0; - gcons[6][3] = -2145.0 / 256.0; - gcons[6][4] = 5005.0 / 1024.0; - gcons[6][5] = -819.0 / 512.0; - gcons[6][6] = 231.0 / 1024.0; - - memory->create(dgcons,7,6,"kspace:dgcons"); - dgcons[2][0] = -5.0 / 2.0; - dgcons[2][1] = 3.0 / 2.0; - dgcons[3][0] = -35.0 / 8.0; - dgcons[3][1] = 21.0 / 4.0; - dgcons[3][2] = -15.0 / 8.0; - dgcons[4][0] = -105.0 / 16.0; - dgcons[4][1] = 189.0 / 16.0; - dgcons[4][2] = -135.0 / 16.0; - dgcons[4][3] = 35.0 / 16.0; - dgcons[5][0] = -1155.0 / 128.0; - dgcons[5][1] = 693.0 / 32.0; - dgcons[5][2] = -1485.0 / 64.0; - dgcons[5][3] = 385.0 / 32.0; - dgcons[5][4] = -315.0 / 128.0; - dgcons[6][0] = -3003.0 / 256.0; - dgcons[6][1] = 9009.0 / 256.0; - dgcons[6][2] = -6435.0 / 128.0; - dgcons[6][3] = 5005.0 / 128.0; - dgcons[6][4] = -4095.0 / 256.0; - dgcons[6][5] = 693.0 / 256.0; -} - -/* ---------------------------------------------------------------------- */ - -KSpace::~KSpace() -{ - memory->destroy(eatom); - memory->destroy(vatom); - memory->destroy(gcons); - memory->destroy(dgcons); -} - -/* ---------------------------------------------------------------------- */ - -void KSpace::triclinic_check() -{ - if (domain->triclinic && triclinic_support != 1) - error->all(FLERR,"KSpace style does not yet support triclinic geometries"); -} - -/* ---------------------------------------------------------------------- */ - -void KSpace::compute_dummy(int eflag, int vflag) -{ - if (eflag || vflag) ev_setup(eflag,vflag); - else evflag = evflag_atom = eflag_global = vflag_global = - eflag_atom = vflag_atom = 0; -} - -/* ---------------------------------------------------------------------- - check that pair style is compatible with long-range solver -------------------------------------------------------------------------- */ - -void KSpace::pair_check() -{ - if (force->pair == NULL) - error->all(FLERR,"KSpace solver requires a pair style"); - if (ewaldflag && force->pair->ewaldflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (pppmflag && force->pair->pppmflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (msmflag && force->pair->msmflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (dispersionflag && force->pair->dispersionflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (tip4pflag && force->pair->tip4pflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); - if (dipoleflag && force->pair->dipoleflag == 0) - error->all(FLERR,"KSpace style is incompatible with Pair style"); -} - -/* ---------------------------------------------------------------------- - setup for energy, virial computation - see integrate::ev_set() for values of eflag (0-3) and vflag (0-6) -------------------------------------------------------------------------- */ - -void KSpace::ev_setup(int eflag, int vflag) -{ - int i,n; - - evflag = 1; - - eflag_either = eflag; - eflag_global = eflag % 2; - eflag_atom = eflag / 2; - - vflag_either = vflag; - vflag_global = vflag % 4; - vflag_atom = vflag / 4; - - if (eflag_atom || vflag_atom) evflag_atom = 1; - else evflag_atom = 0; - - // reallocate per-atom arrays if necessary - - if (eflag_atom && atom->nmax > maxeatom) { - maxeatom = atom->nmax; - memory->destroy(eatom); - memory->create(eatom,maxeatom,"kspace:eatom"); - } - if (vflag_atom && atom->nmax > maxvatom) { - maxvatom = atom->nmax; - memory->destroy(vatom); - memory->create(vatom,maxvatom,6,"kspace:vatom"); - } - - // zero accumulators - - if (eflag_global) energy = 0.0; - if (vflag_global) for (i = 0; i < 6; i++) virial[i] = 0.0; - if (eflag_atom) { - n = atom->nlocal; - if (tip4pflag) n += atom->nghost; - for (i = 0; i < n; i++) eatom[i] = 0.0; - } - if (vflag_atom) { - n = atom->nlocal; - if (tip4pflag) n += atom->nghost; - for (i = 0; i < n; i++) { - vatom[i][0] = 0.0; - vatom[i][1] = 0.0; - vatom[i][2] = 0.0; - vatom[i][3] = 0.0; - vatom[i][4] = 0.0; - vatom[i][5] = 0.0; - } - } -} - -/* ---------------------------------------------------------------------- - estimate the accuracy of the short-range coulomb tables -------------------------------------------------------------------------- */ - -double KSpace::estimate_table_accuracy(double q2_over_sqrt, double spr) -{ - double table_accuracy = 0.0; - int nctb = force->pair->ncoultablebits; - if (nctb) { - double empirical_precision[17]; - empirical_precision[6] = 6.99E-03; - empirical_precision[7] = 1.78E-03; - empirical_precision[8] = 4.72E-04; - empirical_precision[9] = 1.17E-04; - empirical_precision[10] = 2.95E-05; - empirical_precision[11] = 7.41E-06; - empirical_precision[12] = 1.76E-06; - empirical_precision[13] = 9.28E-07; - empirical_precision[14] = 7.46E-07; - empirical_precision[15] = 7.32E-07; - empirical_precision[16] = 7.30E-07; - if (nctb <= 6) table_accuracy = empirical_precision[6]; - else if (nctb <= 16) table_accuracy = empirical_precision[nctb]; - else table_accuracy = empirical_precision[16]; - table_accuracy *= q2_over_sqrt; - if ((table_accuracy > spr) && (comm->me == 0)) - error->warning(FLERR,"For better accuracy use 'pair_modify table 0'"); - } - - return table_accuracy; -} - -/* ---------------------------------------------------------------------- - convert box coords vector to transposed triclinic lamda (0-1) coords - vector, lamda = [(H^-1)^T] * v, does not preserve vector magnitude - v and lamda can point to same 3-vector -------------------------------------------------------------------------- */ - -void KSpace::x2lamdaT(double *v, double *lamda) -{ - double *h_inv = domain->h_inv; - double lamda_tmp[3]; - - lamda_tmp[0] = h_inv[0]*v[0]; - lamda_tmp[1] = h_inv[5]*v[0] + h_inv[1]*v[1]; - lamda_tmp[2] = h_inv[4]*v[0] + h_inv[3]*v[1] + h_inv[2]*v[2]; - - lamda[0] = lamda_tmp[0]; - lamda[1] = lamda_tmp[1]; - lamda[2] = lamda_tmp[2]; -} - -/* ---------------------------------------------------------------------- - convert lamda (0-1) coords vector to transposed box coords vector - lamda = (H^T) * v, does not preserve vector magnitude - v and lamda can point to same 3-vector -------------------------------------------------------------------------- */ - -void KSpace::lamda2xT(double *lamda, double *v) -{ - double *h = domain->h; - double v_tmp[3]; - - v_tmp[0] = h[0]*lamda[0]; - v_tmp[1] = h[5]*lamda[0] + h[1]*lamda[1]; - v_tmp[2] = h[4]*lamda[0] + h[3]*lamda[1] + h[2]*lamda[2]; - - v[0] = v_tmp[0]; - v[1] = v_tmp[1]; - v[2] = v_tmp[2]; -} - -/* ---------------------------------------------------------------------- - convert triclinic lamda (0-1) coords vector to box coords vector - v = H * lamda, does not preserve vector magnitude - lamda and v can point to same 3-vector -------------------------------------------------------------------------- */ - -void KSpace::lamda2xvector(double *lamda, double *v) -{ - double *h = domain->h; - - v[0] = h[0]*lamda[0] + h[5]*lamda[1] + h[4]*lamda[2]; - v[1] = h[1]*lamda[1] + h[3]*lamda[2]; - v[2] = h[2]*lamda[2]; -} - -/* ---------------------------------------------------------------------- - convert a sphere in box coords to an ellipsoid in lamda (0-1) - coords and return the tight (axis-aligned) bounding box, does not - preserve vector magnitude - see http://www.loria.fr/~shornus/ellipsoid-bbox.html and - http://yiningkarlli.blogspot.com/2013/02/bounding-boxes-for-ellipsoidsfigure.html -------------------------------------------------------------------------- */ - -void KSpace::kspacebbox(double r, double *b) -{ - double *h = domain->h; - double lx,ly,lz,xy,xz,yz; - lx = h[0]; - ly = h[1]; - lz = h[2]; - yz = h[3]; - xz = h[4]; - xy = h[5]; - - b[0] = r*sqrt(ly*ly*lz*lz + ly*ly*xz*xz - 2.0*ly*xy*xz*yz + lz*lz*xy*xy + - xy*xy*yz*yz)/(lx*ly*lz); - b[1] = r*sqrt(lz*lz + yz*yz)/(ly*lz); - b[2] = r/lz; -} - -/* ---------------------------------------------------------------------- - modify parameters of the KSpace style -------------------------------------------------------------------------- */ - -void KSpace::modify_params(int narg, char **arg) -{ - int iarg = 0; - while (iarg < narg) { - if (strcmp(arg[iarg],"mesh") == 0) { - if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); - nx_pppm = nx_msm_max = atoi(arg[iarg+1]); - ny_pppm = ny_msm_max = atoi(arg[iarg+2]); - nz_pppm = nz_msm_max = atoi(arg[iarg+3]); - if (nx_pppm == 0 && ny_pppm == 0 && nz_pppm == 0) gridflag = 0; - else gridflag = 1; - iarg += 4; - } else if (strcmp(arg[iarg],"mesh/disp") == 0) { - if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); - nx_pppm_6 = atoi(arg[iarg+1]); - ny_pppm_6 = atoi(arg[iarg+2]); - nz_pppm_6 = atoi(arg[iarg+3]); - if (nx_pppm_6 == 0 || ny_pppm_6 == 0 || nz_pppm_6 == 0) gridflag_6 = 0; - else gridflag_6 = 1; - iarg += 4; - } else if (strcmp(arg[iarg],"order") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - order = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"order/disp") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - order_6 = atoi(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"minorder") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - minorder = atoi(arg[iarg+1]); - if (minorder < 2) error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"overlap") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) overlap_allowed = 1; - else if (strcmp(arg[iarg+1],"no") == 0) overlap_allowed = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"force") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - accuracy_absolute = atof(arg[iarg+1]); - iarg += 2; - } else if (strcmp(arg[iarg],"gewald") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - g_ewald = atof(arg[iarg+1]); - if (g_ewald == 0.0) gewaldflag = 0; - else gewaldflag = 1; - iarg += 2; - } else if (strcmp(arg[iarg],"gewald/disp") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - g_ewald_6 = atof(arg[iarg+1]); - if (g_ewald_6 == 0.0) gewaldflag_6 = 0; - else gewaldflag_6 = 1; - iarg += 2; - } else if (strcmp(arg[iarg],"slab") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"nozforce") == 0) { - slabflag = 2; - } else { - slabflag = 1; - slab_volfactor = atof(arg[iarg+1]); - if (slab_volfactor <= 1.0) - error->all(FLERR,"Bad kspace_modify slab parameter"); - if (slab_volfactor < 2.0 && comm->me == 0) - error->warning(FLERR,"Kspace_modify slab param < 2.0 may " - "cause unphysical behavior"); - } - iarg += 2; - } else if (strcmp(arg[iarg],"compute") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) compute_flag = 1; - else if (strcmp(arg[iarg+1],"no") == 0) compute_flag = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"fftbench") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) fftbench = 1; - else if (strcmp(arg[iarg+1],"no") == 0) fftbench = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"diff") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"ad") == 0) differentiation_flag = 1; - else if (strcmp(arg[iarg+1],"ik") == 0) differentiation_flag = 0; - else error->all(FLERR, "Illegal kspace_modify command"); - iarg += 2; - } else if (strcmp(arg[iarg],"cutoff/adjust") == 0) { - if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); - if (strcmp(arg[iarg+1],"yes") == 0) adjust_cutoff_flag = 1; - else if (strcmp(arg[iarg+1],"no") == 0) adjust_cutoff_flag = 0; - else error->all(FLERR,"Illegal kspace_modify command"); - iarg += 2; - } else error->all(FLERR,"Illegal kspace_modify command"); - } -} - -/* ---------------------------------------------------------------------- */ - -void *KSpace::extract(const char *str) -{ - if (strcmp(str,"scale") == 0) return (void *) &scale; - return NULL; -} +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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 "stdlib.h" +#include "string.h" +#include "kspace.h" +#include "atom.h" +#include "comm.h" +#include "force.h" +#include "pair.h" +#include "memory.h" +#include "atom_masks.h" +#include "error.h" +#include "suffix.h" +#include "domain.h" + +using namespace LAMMPS_NS; + +/* ---------------------------------------------------------------------- */ + +KSpace::KSpace(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp) +{ + energy = 0.0; + virial[0] = virial[1] = virial[2] = virial[3] = virial[4] = virial[5] = 0.0; + + triclinic_support = 1; + ewaldflag = pppmflag = msmflag = dispersionflag = tip4pflag = dipoleflag = 0; + compute_flag = 1; + group_group_enable = 0; + stagger_flag = 0; + + order = 5; + gridflag = 0; + gewaldflag = 0; + minorder = 2; + overlap_allowed = 1; + fftbench = 1; + + order_6 = 5; + gridflag_6 = 0; + gewaldflag_6 = 0; + + slabflag = 0; + differentiation_flag = 0; + slab_volfactor = 1; + suffix_flag = Suffix::NONE; + adjust_cutoff_flag = 1; + + accuracy_absolute = -1.0; + two_charge_force = force->qqr2e * + (force->qelectron * force->qelectron) / + (force->angstrom * force->angstrom); + + maxeatom = maxvatom = 0; + eatom = NULL; + vatom = NULL; + + datamask = ALL_MASK; + datamask_ext = ALL_MASK; + + memory->create(gcons,7,7,"kspace:gcons"); + gcons[2][0] = 15.0 / 8.0; + gcons[2][1] = -5.0 / 4.0; + gcons[2][2] = 3.0 / 8.0; + gcons[3][0] = 35.0 / 16.0; + gcons[3][1] = -35.0 / 16.0; + gcons[3][2] = 21.0 / 16.0; + gcons[3][3] = -5.0 / 16.0; + gcons[4][0] = 315.0 / 128.0; + gcons[4][1] = -105.0 / 32.0; + gcons[4][2] = 189.0 / 64.0; + gcons[4][3] = -45.0 / 32.0; + gcons[4][4] = 35.0 / 128.0; + gcons[5][0] = 693.0 / 256.0; + gcons[5][1] = -1155.0 / 256.0; + gcons[5][2] = 693.0 / 128.0; + gcons[5][3] = -495.0 / 128.0; + gcons[5][4] = 385.0 / 256.0; + gcons[5][5] = -63.0 / 256.0; + gcons[6][0] = 3003.0 / 1024.0; + gcons[6][1] = -3003.0 / 512.0; + gcons[6][2] = 9009.0 / 1024.0; + gcons[6][3] = -2145.0 / 256.0; + gcons[6][4] = 5005.0 / 1024.0; + gcons[6][5] = -819.0 / 512.0; + gcons[6][6] = 231.0 / 1024.0; + + memory->create(dgcons,7,6,"kspace:dgcons"); + dgcons[2][0] = -5.0 / 2.0; + dgcons[2][1] = 3.0 / 2.0; + dgcons[3][0] = -35.0 / 8.0; + dgcons[3][1] = 21.0 / 4.0; + dgcons[3][2] = -15.0 / 8.0; + dgcons[4][0] = -105.0 / 16.0; + dgcons[4][1] = 189.0 / 16.0; + dgcons[4][2] = -135.0 / 16.0; + dgcons[4][3] = 35.0 / 16.0; + dgcons[5][0] = -1155.0 / 128.0; + dgcons[5][1] = 693.0 / 32.0; + dgcons[5][2] = -1485.0 / 64.0; + dgcons[5][3] = 385.0 / 32.0; + dgcons[5][4] = -315.0 / 128.0; + dgcons[6][0] = -3003.0 / 256.0; + dgcons[6][1] = 9009.0 / 256.0; + dgcons[6][2] = -6435.0 / 128.0; + dgcons[6][3] = 5005.0 / 128.0; + dgcons[6][4] = -4095.0 / 256.0; + dgcons[6][5] = 693.0 / 256.0; +} + +/* ---------------------------------------------------------------------- */ + +KSpace::~KSpace() +{ + memory->destroy(eatom); + memory->destroy(vatom); + memory->destroy(gcons); + memory->destroy(dgcons); +} + +/* ---------------------------------------------------------------------- */ + +void KSpace::triclinic_check() +{ + if (domain->triclinic && triclinic_support != 1) + error->all(FLERR,"KSpace style does not yet support triclinic geometries"); +} + +/* ---------------------------------------------------------------------- */ + +void KSpace::compute_dummy(int eflag, int vflag) +{ + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = evflag_atom = eflag_global = vflag_global = + eflag_atom = vflag_atom = 0; +} + +/* ---------------------------------------------------------------------- + check that pair style is compatible with long-range solver +------------------------------------------------------------------------- */ + +void KSpace::pair_check() +{ + if (force->pair == NULL) + error->all(FLERR,"KSpace solver requires a pair style"); + if (ewaldflag && force->pair->ewaldflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (pppmflag && force->pair->pppmflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (msmflag && force->pair->msmflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (dispersionflag && force->pair->dispersionflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (tip4pflag && force->pair->tip4pflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); + if (dipoleflag && force->pair->dipoleflag == 0) + error->all(FLERR,"KSpace style is incompatible with Pair style"); +} + +/* ---------------------------------------------------------------------- + setup for energy, virial computation + see integrate::ev_set() for values of eflag (0-3) and vflag (0-6) +------------------------------------------------------------------------- */ + +void KSpace::ev_setup(int eflag, int vflag) +{ + int i,n; + + evflag = 1; + + eflag_either = eflag; + eflag_global = eflag % 2; + eflag_atom = eflag / 2; + + vflag_either = vflag; + vflag_global = vflag % 4; + vflag_atom = vflag / 4; + + if (eflag_atom || vflag_atom) evflag_atom = 1; + else evflag_atom = 0; + + // reallocate per-atom arrays if necessary + + if (eflag_atom && atom->nmax > maxeatom) { + maxeatom = atom->nmax; + memory->destroy(eatom); + memory->create(eatom,maxeatom,"kspace:eatom"); + } + if (vflag_atom && atom->nmax > maxvatom) { + maxvatom = atom->nmax; + memory->destroy(vatom); + memory->create(vatom,maxvatom,6,"kspace:vatom"); + } + + // zero accumulators + + if (eflag_global) energy = 0.0; + if (vflag_global) for (i = 0; i < 6; i++) virial[i] = 0.0; + if (eflag_atom) { + n = atom->nlocal; + if (tip4pflag) n += atom->nghost; + for (i = 0; i < n; i++) eatom[i] = 0.0; + } + if (vflag_atom) { + n = atom->nlocal; + if (tip4pflag) n += atom->nghost; + for (i = 0; i < n; i++) { + vatom[i][0] = 0.0; + vatom[i][1] = 0.0; + vatom[i][2] = 0.0; + vatom[i][3] = 0.0; + vatom[i][4] = 0.0; + vatom[i][5] = 0.0; + } + } +} + +/* ---------------------------------------------------------------------- + estimate the accuracy of the short-range coulomb tables +------------------------------------------------------------------------- */ + +double KSpace::estimate_table_accuracy(double q2_over_sqrt, double spr) +{ + double table_accuracy = 0.0; + int nctb = force->pair->ncoultablebits; + if (nctb) { + double empirical_precision[17]; + empirical_precision[6] = 6.99E-03; + empirical_precision[7] = 1.78E-03; + empirical_precision[8] = 4.72E-04; + empirical_precision[9] = 1.17E-04; + empirical_precision[10] = 2.95E-05; + empirical_precision[11] = 7.41E-06; + empirical_precision[12] = 1.76E-06; + empirical_precision[13] = 9.28E-07; + empirical_precision[14] = 7.46E-07; + empirical_precision[15] = 7.32E-07; + empirical_precision[16] = 7.30E-07; + if (nctb <= 6) table_accuracy = empirical_precision[6]; + else if (nctb <= 16) table_accuracy = empirical_precision[nctb]; + else table_accuracy = empirical_precision[16]; + table_accuracy *= q2_over_sqrt; + if ((table_accuracy > spr) && (comm->me == 0)) + error->warning(FLERR,"For better accuracy use 'pair_modify table 0'"); + } + + return table_accuracy; +} + +/* ---------------------------------------------------------------------- + convert box coords vector to transposed triclinic lamda (0-1) coords + vector, lamda = [(H^-1)^T] * v, does not preserve vector magnitude + v and lamda can point to same 3-vector +------------------------------------------------------------------------- */ + +void KSpace::x2lamdaT(double *v, double *lamda) +{ + double *h_inv = domain->h_inv; + double lamda_tmp[3]; + + lamda_tmp[0] = h_inv[0]*v[0]; + lamda_tmp[1] = h_inv[5]*v[0] + h_inv[1]*v[1]; + lamda_tmp[2] = h_inv[4]*v[0] + h_inv[3]*v[1] + h_inv[2]*v[2]; + + lamda[0] = lamda_tmp[0]; + lamda[1] = lamda_tmp[1]; + lamda[2] = lamda_tmp[2]; +} + +/* ---------------------------------------------------------------------- + convert lamda (0-1) coords vector to transposed box coords vector + lamda = (H^T) * v, does not preserve vector magnitude + v and lamda can point to same 3-vector +------------------------------------------------------------------------- */ + +void KSpace::lamda2xT(double *lamda, double *v) +{ + double *h = domain->h; + double v_tmp[3]; + + v_tmp[0] = h[0]*lamda[0]; + v_tmp[1] = h[5]*lamda[0] + h[1]*lamda[1]; + v_tmp[2] = h[4]*lamda[0] + h[3]*lamda[1] + h[2]*lamda[2]; + + v[0] = v_tmp[0]; + v[1] = v_tmp[1]; + v[2] = v_tmp[2]; +} + +/* ---------------------------------------------------------------------- + convert triclinic lamda (0-1) coords vector to box coords vector + v = H * lamda, does not preserve vector magnitude + lamda and v can point to same 3-vector +------------------------------------------------------------------------- */ + +void KSpace::lamda2xvector(double *lamda, double *v) +{ + double *h = domain->h; + + v[0] = h[0]*lamda[0] + h[5]*lamda[1] + h[4]*lamda[2]; + v[1] = h[1]*lamda[1] + h[3]*lamda[2]; + v[2] = h[2]*lamda[2]; +} + +/* ---------------------------------------------------------------------- + convert a sphere in box coords to an ellipsoid in lamda (0-1) + coords and return the tight (axis-aligned) bounding box, does not + preserve vector magnitude + see http://www.loria.fr/~shornus/ellipsoid-bbox.html and + http://yiningkarlli.blogspot.com/2013/02/bounding-boxes-for-ellipsoidsfigure.html +------------------------------------------------------------------------- */ + +void KSpace::kspacebbox(double r, double *b) +{ + double *h = domain->h; + double lx,ly,lz,xy,xz,yz; + lx = h[0]; + ly = h[1]; + lz = h[2]; + yz = h[3]; + xz = h[4]; + xy = h[5]; + + b[0] = r*sqrt(ly*ly*lz*lz + ly*ly*xz*xz - 2.0*ly*xy*xz*yz + lz*lz*xy*xy + + xy*xy*yz*yz)/(lx*ly*lz); + b[1] = r*sqrt(lz*lz + yz*yz)/(ly*lz); + b[2] = r/lz; +} + +/* ---------------------------------------------------------------------- + modify parameters of the KSpace style +------------------------------------------------------------------------- */ + +void KSpace::modify_params(int narg, char **arg) +{ + int iarg = 0; + while (iarg < narg) { + if (strcmp(arg[iarg],"mesh") == 0) { + if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); + nx_pppm = nx_msm_max = atoi(arg[iarg+1]); + ny_pppm = ny_msm_max = atoi(arg[iarg+2]); + nz_pppm = nz_msm_max = atoi(arg[iarg+3]); + if (nx_pppm == 0 && ny_pppm == 0 && nz_pppm == 0) gridflag = 0; + else gridflag = 1; + iarg += 4; + } else if (strcmp(arg[iarg],"mesh/disp") == 0) { + if (iarg+4 > narg) error->all(FLERR,"Illegal kspace_modify command"); + nx_pppm_6 = atoi(arg[iarg+1]); + ny_pppm_6 = atoi(arg[iarg+2]); + nz_pppm_6 = atoi(arg[iarg+3]); + if (nx_pppm_6 == 0 || ny_pppm_6 == 0 || nz_pppm_6 == 0) gridflag_6 = 0; + else gridflag_6 = 1; + iarg += 4; + } else if (strcmp(arg[iarg],"order") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + order = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"order/disp") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + order_6 = atoi(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"minorder") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + minorder = atoi(arg[iarg+1]); + if (minorder < 2) error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"overlap") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) overlap_allowed = 1; + else if (strcmp(arg[iarg+1],"no") == 0) overlap_allowed = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"force") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + accuracy_absolute = atof(arg[iarg+1]); + iarg += 2; + } else if (strcmp(arg[iarg],"gewald") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + g_ewald = atof(arg[iarg+1]); + if (g_ewald == 0.0) gewaldflag = 0; + else gewaldflag = 1; + iarg += 2; + } else if (strcmp(arg[iarg],"gewald/disp") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + g_ewald_6 = atof(arg[iarg+1]); + if (g_ewald_6 == 0.0) gewaldflag_6 = 0; + else gewaldflag_6 = 1; + iarg += 2; + } else if (strcmp(arg[iarg],"slab") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"nozforce") == 0) { + slabflag = 2; + } else { + slabflag = 1; + slab_volfactor = atof(arg[iarg+1]); + if (slab_volfactor <= 1.0) + error->all(FLERR,"Bad kspace_modify slab parameter"); + if (slab_volfactor < 2.0 && comm->me == 0) + error->warning(FLERR,"Kspace_modify slab param < 2.0 may " + "cause unphysical behavior"); + } + iarg += 2; + } else if (strcmp(arg[iarg],"compute") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) compute_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) compute_flag = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"fftbench") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) fftbench = 1; + else if (strcmp(arg[iarg+1],"no") == 0) fftbench = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"diff") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"ad") == 0) differentiation_flag = 1; + else if (strcmp(arg[iarg+1],"ik") == 0) differentiation_flag = 0; + else error->all(FLERR, "Illegal kspace_modify command"); + iarg += 2; + } else if (strcmp(arg[iarg],"cutoff/adjust") == 0) { + if (iarg+2 > narg) error->all(FLERR,"Illegal kspace_modify command"); + if (strcmp(arg[iarg+1],"yes") == 0) adjust_cutoff_flag = 1; + else if (strcmp(arg[iarg+1],"no") == 0) adjust_cutoff_flag = 0; + else error->all(FLERR,"Illegal kspace_modify command"); + iarg += 2; + } else error->all(FLERR,"Illegal kspace_modify command"); + } +} + +/* ---------------------------------------------------------------------- */ + +void *KSpace::extract(const char *str) +{ + if (strcmp(str,"scale") == 0) return (void *) &scale; + return NULL; +} diff --git a/src/kspace.h b/src/kspace.h index f2c914ee95..010812bd48 100644 --- a/src/kspace.h +++ b/src/kspace.h @@ -1,200 +1,200 @@ -/* -*- c++ -*- ---------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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. -------------------------------------------------------------------------- */ - -#ifndef LMP_KSPACE_H -#define LMP_KSPACE_H - -#include "pointers.h" - -#ifdef FFT_SINGLE -typedef float FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_FLOAT -#else -typedef double FFT_SCALAR; -#define MPI_FFT_SCALAR MPI_DOUBLE -#endif - -namespace LAMMPS_NS { - -class KSpace : protected Pointers { - friend class ThrOMP; - friend class FixOMP; - public: - double energy; // accumulated energies - double energy_1,energy_6; - double virial[6]; // accumlated virial - double *eatom,**vatom; // accumulated per-atom energy/virial - double e2group; // accumulated group-group energy - double f2group[3]; // accumulated group-group force - int triclinic_support; // 1 if supports triclinic geometries - - int ewaldflag; // 1 if a Ewald solver - int pppmflag; // 1 if a PPPM solver - int msmflag; // 1 if a MSM solver - int dispersionflag; // 1 if a LJ/dispersion solver - int tip4pflag; // 1 if a TIP4P solver - int dipoleflag; // 1 if a dipole solver - int differentiation_flag; - int slabflag; - double slab_volfactor; - - int order,order_6; - double accuracy; // accuracy of KSpace solver (force units) - double accuracy_absolute; // user-specifed accuracy in force units - double accuracy_relative; // user-specified dimensionless accuracy - // accurary = acc_rel * two_charge_force - double two_charge_force; // force in user units of two point - // charges separated by 1 Angstrom - - double g_ewald,g_ewald_6; - int nx_pppm,ny_pppm,nz_pppm; // global FFT grid for Coulombics - int nx_pppm_6,ny_pppm_6,nz_pppm_6; // global FFT grid for dispersion - int nx_msm_max,ny_msm_max,nz_msm_max; - - int group_group_enable; // 1 if style supports group/group calculation - - unsigned int datamask; - unsigned int datamask_ext; - - int compute_flag; // 0 if skip compute() - int fftbench; // 0 if skip FFT timing - - int stagger_flag; // 1 if using staggered PPPM grids - - KSpace(class LAMMPS *, int, char **); - virtual ~KSpace(); - void triclinic_check(); - void modify_params(int, char **); - void *extract(const char *); - void compute_dummy(int, int); - - // triclinic - - void x2lamdaT(double *, double *); - void lamda2xT(double *, double *); - void lamda2xvector(double *, double *); - void kspacebbox(double, double *); - - // general child-class methods - - virtual void init() = 0; - virtual void setup() = 0; - virtual void setup_grid() {}; - virtual void compute(int, int) = 0; - virtual void compute_group_group(int, int, int) {}; - - virtual void pack_forward(int, FFT_SCALAR *, int, int *) {}; - virtual void unpack_forward(int, FFT_SCALAR *, int, int *) {}; - virtual void pack_reverse(int, FFT_SCALAR *, int, int *) {}; - virtual void unpack_reverse(int, FFT_SCALAR *, int, int *) {}; - - virtual int timing(int, double &, double &) {return 0;} - virtual int timing_1d(int, double &) {return 0;} - virtual int timing_3d(int, double &) {return 0;} - virtual double memory_usage() {return 0.0;} - -/* ---------------------------------------------------------------------- - compute gamma for MSM and pair styles - see Eq 4 from Parallel Computing 35 (2009) 164–177 -------------------------------------------------------------------------- */ - - double gamma(const double &rho) const { - if (rho <= 1.0) { - const int split_order = order/2; - const double rho2 = rho*rho; - double g = gcons[split_order][0]; - double rho_n = rho2; - for (int n=1; n<=split_order; n++) { - g += gcons[split_order][n]*rho_n; - rho_n *= rho2; - } - return g; - } else - return (1.0/rho); - } - -/* ---------------------------------------------------------------------- - compute the derivative of gamma for MSM and pair styles - see Eq 4 from Parallel Computing 35 (2009) 164-177 -------------------------------------------------------------------------- */ - - double dgamma(const double &rho) const { - if (rho <= 1.0) { - const int split_order = order/2; - const double rho2 = rho*rho; - double dg = dgcons[split_order][0]*rho; - double rho_n = rho*rho2; - for (int n=1; n= 2.0. - -W: Kspace_modify slab param < 2.0 may cause unphysical behavior - -The kspace_modify slab parameter should be larger to insure periodic -grids padded with empty space do not overlap. - -*/ +/* -*- c++ -*- ---------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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. +------------------------------------------------------------------------- */ + +#ifndef LMP_KSPACE_H +#define LMP_KSPACE_H + +#include "pointers.h" + +#ifdef FFT_SINGLE +typedef float FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_FLOAT +#else +typedef double FFT_SCALAR; +#define MPI_FFT_SCALAR MPI_DOUBLE +#endif + +namespace LAMMPS_NS { + +class KSpace : protected Pointers { + friend class ThrOMP; + friend class FixOMP; + public: + double energy; // accumulated energies + double energy_1,energy_6; + double virial[6]; // accumlated virial + double *eatom,**vatom; // accumulated per-atom energy/virial + double e2group; // accumulated group-group energy + double f2group[3]; // accumulated group-group force + int triclinic_support; // 1 if supports triclinic geometries + + int ewaldflag; // 1 if a Ewald solver + int pppmflag; // 1 if a PPPM solver + int msmflag; // 1 if a MSM solver + int dispersionflag; // 1 if a LJ/dispersion solver + int tip4pflag; // 1 if a TIP4P solver + int dipoleflag; // 1 if a dipole solver + int differentiation_flag; + int slabflag; + double slab_volfactor; + + int order,order_6; + double accuracy; // accuracy of KSpace solver (force units) + double accuracy_absolute; // user-specifed accuracy in force units + double accuracy_relative; // user-specified dimensionless accuracy + // accurary = acc_rel * two_charge_force + double two_charge_force; // force in user units of two point + // charges separated by 1 Angstrom + + double g_ewald,g_ewald_6; + int nx_pppm,ny_pppm,nz_pppm; // global FFT grid for Coulombics + int nx_pppm_6,ny_pppm_6,nz_pppm_6; // global FFT grid for dispersion + int nx_msm_max,ny_msm_max,nz_msm_max; + + int group_group_enable; // 1 if style supports group/group calculation + + unsigned int datamask; + unsigned int datamask_ext; + + int compute_flag; // 0 if skip compute() + int fftbench; // 0 if skip FFT timing + + int stagger_flag; // 1 if using staggered PPPM grids + + KSpace(class LAMMPS *, int, char **); + virtual ~KSpace(); + void triclinic_check(); + void modify_params(int, char **); + void *extract(const char *); + void compute_dummy(int, int); + + // triclinic + + void x2lamdaT(double *, double *); + void lamda2xT(double *, double *); + void lamda2xvector(double *, double *); + void kspacebbox(double, double *); + + // general child-class methods + + virtual void init() = 0; + virtual void setup() = 0; + virtual void setup_grid() {}; + virtual void compute(int, int) = 0; + virtual void compute_group_group(int, int, int) {}; + + virtual void pack_forward(int, FFT_SCALAR *, int, int *) {}; + virtual void unpack_forward(int, FFT_SCALAR *, int, int *) {}; + virtual void pack_reverse(int, FFT_SCALAR *, int, int *) {}; + virtual void unpack_reverse(int, FFT_SCALAR *, int, int *) {}; + + virtual int timing(int, double &, double &) {return 0;} + virtual int timing_1d(int, double &) {return 0;} + virtual int timing_3d(int, double &) {return 0;} + virtual double memory_usage() {return 0.0;} + +/* ---------------------------------------------------------------------- + compute gamma for MSM and pair styles + see Eq 4 from Parallel Computing 35 (2009) 164–177 +------------------------------------------------------------------------- */ + + double gamma(const double &rho) const { + if (rho <= 1.0) { + const int split_order = order/2; + const double rho2 = rho*rho; + double g = gcons[split_order][0]; + double rho_n = rho2; + for (int n=1; n<=split_order; n++) { + g += gcons[split_order][n]*rho_n; + rho_n *= rho2; + } + return g; + } else + return (1.0/rho); + } + +/* ---------------------------------------------------------------------- + compute the derivative of gamma for MSM and pair styles + see Eq 4 from Parallel Computing 35 (2009) 164-177 +------------------------------------------------------------------------- */ + + double dgamma(const double &rho) const { + if (rho <= 1.0) { + const int split_order = order/2; + const double rho2 = rho*rho; + double dg = dgcons[split_order][0]*rho; + double rho_n = rho*rho2; + for (int n=1; n= 2.0. + +W: Kspace_modify slab param < 2.0 may cause unphysical behavior + +The kspace_modify slab parameter should be larger to insure periodic +grids padded with empty space do not overlap. + +*/ From 83f360b90bc979bf634a59ec7a56c367702cd9b7 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:53:09 +0000 Subject: [PATCH 34/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10136 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/DIPOLE/pair_lj_long_dipole_long.cpp | 1374 +++++++++++------------ src/KSPACE/pppm.cpp | 14 +- src/KSPACE/pppm.h | 4 +- 3 files changed, 692 insertions(+), 700 deletions(-) diff --git a/src/DIPOLE/pair_lj_long_dipole_long.cpp b/src/DIPOLE/pair_lj_long_dipole_long.cpp index cbac0f2767..85eaf5e9a8 100755 --- a/src/DIPOLE/pair_lj_long_dipole_long.cpp +++ b/src/DIPOLE/pair_lj_long_dipole_long.cpp @@ -1,687 +1,687 @@ -/* ---------------------------------------------------------------------- - LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator - http://lammps.sandia.gov, Sandia National Laboratories - Steve Plimpton, sjplimp@sandia.gov - - 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: Pieter J. in 't Veld and Stan Moore (Sandia) -------------------------------------------------------------------------- */ - -#include "math.h" -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "math_const.h" -#include "math_vector.h" -#include "pair_lj_long_dipole_long.h" -#include "atom.h" -#include "comm.h" -#include "neighbor.h" -#include "neigh_list.h" -#include "neigh_request.h" -#include "force.h" -#include "kspace.h" -#include "update.h" -#include "integrate.h" -#include "respa.h" -#include "memory.h" -#include "error.h" - -using namespace LAMMPS_NS; -using namespace MathConst; - -#define EWALD_F 1.12837917 -#define EWALD_P 0.3275911 -#define A1 0.254829592 -#define A2 -0.284496736 -#define A3 1.421413741 -#define A4 -1.453152027 -#define A5 1.061405429 - -// ---------------------------------------------------------------------- - -PairLJLongDipoleLong::PairLJLongDipoleLong(LAMMPS *lmp) : Pair(lmp) -{ - dispersionflag = ewaldflag = dipoleflag = 1; - respa_enable = 0; - single_enable = 0; -} - -// ---------------------------------------------------------------------- -// global settings -// ---------------------------------------------------------------------- - -#define PAIR_ILLEGAL "Illegal pair_style lj/long/dipole/long command" -#define PAIR_CUTOFF "Only one cut-off allowed when requesting all long" -#define PAIR_MISSING "Cut-offs missing in pair_style lj/long/dipole/long" -#define PAIR_COUL_CUT "Coulombic cut not supported in pair_style lj/long/dipole/long" -#define PAIR_LARGEST "Using largest cut-off for lj/long/dipole/long long long" -#define PAIR_MIX "Geometric mixing assumed for 1/r^6 coefficients" - -void PairLJLongDipoleLong::options(char **arg, int order) -{ - const char *option[] = {"long", "cut", "off", NULL}; - int i; - - if (!*arg) error->all(FLERR,PAIR_ILLEGAL); - for (i=0; option[i]&&strcmp(arg[0], option[i]); ++i); - switch (i) { - default: error->all(FLERR,PAIR_ILLEGAL); - case 0: ewald_order |= 1<all(FLERR,"Illegal pair_style command"); - - ewald_off = 0; - ewald_order = 0; - options(arg, 6); - options(++arg, 3); - options(arg, 1); - if (!comm->me && ewald_order&(1<<6)) - error->warning(FLERR,PAIR_MIX); - if (!comm->me && ewald_order==((1<<3)|(1<<6))) - error->warning(FLERR,PAIR_LARGEST); - if (!*(++arg)) - error->all(FLERR,PAIR_MISSING); - if (!((ewald_order^ewald_off)&(1<<3))) - error->all(FLERR,PAIR_COUL_CUT); - cut_lj_global = force->numeric(FLERR,*(arg++)); - if (narg == 4 && (ewald_order==74)) - error->all(FLERR,PAIR_CUTOFF); - if (narg == 4) cut_coul = force->numeric(FLERR,*(arg++)); - else cut_coul = cut_lj_global; - - if (allocated) { // reset explicit cuts - int i,j; - for (i = 1; i <= atom->ntypes; i++) - for (j = i+1; j <= atom->ntypes; j++) - if (setflag[i][j]) cut_lj[i][j] = cut_lj_global; - } -} - -// ---------------------------------------------------------------------- -// free all arrays -// ---------------------------------------------------------------------- - -PairLJLongDipoleLong::~PairLJLongDipoleLong() -{ - if (allocated) { - memory->destroy(setflag); - memory->destroy(cutsq); - - memory->destroy(cut_lj_read); - memory->destroy(cut_lj); - memory->destroy(cut_ljsq); - memory->destroy(epsilon_read); - memory->destroy(epsilon); - memory->destroy(sigma_read); - memory->destroy(sigma); - memory->destroy(lj1); - memory->destroy(lj2); - memory->destroy(lj3); - memory->destroy(lj4); - memory->destroy(offset); - } - //if (ftable) free_tables(); -} - -/* ---------------------------------------------------------------------- - allocate all arrays -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::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"); - - memory->create(cut_lj_read,n+1,n+1,"pair:cut_lj_read"); - memory->create(cut_lj,n+1,n+1,"pair:cut_lj"); - memory->create(cut_ljsq,n+1,n+1,"pair:cut_ljsq"); - memory->create(epsilon_read,n+1,n+1,"pair:epsilon_read"); - memory->create(epsilon,n+1,n+1,"pair:epsilon"); - memory->create(sigma_read,n+1,n+1,"pair:sigma_read"); - memory->create(sigma,n+1,n+1,"pair:sigma"); - memory->create(lj1,n+1,n+1,"pair:lj1"); - memory->create(lj2,n+1,n+1,"pair:lj2"); - memory->create(lj3,n+1,n+1,"pair:lj3"); - memory->create(lj4,n+1,n+1,"pair:lj4"); - memory->create(offset,n+1,n+1,"pair:offset"); -} - -/* ---------------------------------------------------------------------- - extract protected data from object -------------------------------------------------------------------------- */ - -void *PairLJLongDipoleLong::extract(const char *id, int &dim) -{ - const char *ids[] = { - "B", "sigma", "epsilon", "ewald_order", "ewald_cut", "ewald_mix", - "cut_coul", "cut_vdwl", NULL}; - void *ptrs[] = { - lj4, sigma, epsilon, &ewald_order, &cut_coul, &mix_flag, &cut_coul, - &cut_lj_global, NULL}; - int i; - - for (i=0; ids[i]&&strcmp(ids[i], id); ++i); - if (i <= 2) dim = 2; - else dim = 0; - return ptrs[i]; -} - -/* ---------------------------------------------------------------------- - set coeffs for one or more type pairs -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::coeff(int narg, char **arg) -{ - if (narg < 4 || narg > 5) error->all(FLERR,"Incorrect args for pair coefficients"); - if (!allocated) allocate(); - - int ilo,ihi,jlo,jhi; - force->bounds(arg[0],atom->ntypes,ilo,ihi); - force->bounds(arg[1],atom->ntypes,jlo,jhi); - - double epsilon_one = force->numeric(FLERR,arg[2]); - double sigma_one = force->numeric(FLERR,arg[3]); - - double cut_lj_one = cut_lj_global; - if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); - - int count = 0; - for (int i = ilo; i <= ihi; i++) { - for (int j = MAX(jlo,i); j <= jhi; j++) { - epsilon_read[i][j] = epsilon_one; - sigma_read[i][j] = sigma_one; - cut_lj_read[i][j] = cut_lj_one; - setflag[i][j] = 1; - count++; - } - } - - if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); -} - -/* ---------------------------------------------------------------------- - init specific to this pair style -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::init_style() -{ - const char *style3[] = {"ewald/disp", NULL}; - const char *style6[] = {"ewald/disp", NULL}; - int i; - - if (strcmp(update->unit_style,"electron") == 0) - error->all(FLERR,"Cannot (yet) use 'electron' units with dipoles"); - - // require an atom style with charge defined - - if (!atom->q_flag && (ewald_order&(1<<1))) - error->all(FLERR, - "Invoking coulombic in pair style lj/long/dipole/long requires atom attribute q"); - if (!atom->mu && (ewald_order&(1<<3))) - error->all(FLERR,"Pair lj/long/dipole/long requires atom attributes mu, torque"); - if (!atom->torque && (ewald_order&(1<<3))) - error->all(FLERR,"Pair lj/long/dipole/long requires atom attributes mu, torque"); - - neighbor->request(this); - - cut_coulsq = cut_coul * cut_coul; - - // ensure use of KSpace long-range solver, set g_ewald - - if (ewald_order&(1<<3)) { // r^-1 kspace - if (force->kspace == NULL) - error->all(FLERR,"Pair style is incompatible with KSpace style"); - for (i=0; style3[i]&&strcmp(force->kspace_style, style3[i]); ++i); - if (!style3[i]) - error->all(FLERR,"Pair style is incompatible with KSpace style"); - } - if (ewald_order&(1<<6)) { // r^-6 kspace - if (force->kspace == NULL) - error->all(FLERR,"Pair style is incompatible with KSpace style"); - for (i=0; style6[i]&&strcmp(force->kspace_style, style6[i]); ++i); - if (!style6[i]) - error->all(FLERR,"Pair style is incompatible with KSpace style"); - } - if (force->kspace) g_ewald = force->kspace->g_ewald; -} - -/* ---------------------------------------------------------------------- - neighbor callback to inform pair style of neighbor list to use - regular or rRESPA -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::init_list(int id, NeighList *ptr) -{ - if (id == 0) list = ptr; - else if (id == 1) listinner = ptr; - else if (id == 2) listmiddle = ptr; - else if (id == 3) listouter = ptr; - - if (id) - error->all(FLERR,"Pair style lj/long/dipole/long does not currently support respa"); -} - -/* ---------------------------------------------------------------------- - init for one type pair i,j and corresponding j,i -------------------------------------------------------------------------- */ - -double PairLJLongDipoleLong::init_one(int i, int j) -{ - if ((ewald_order&(1<<6))||(setflag[i][j] == 0)) { - epsilon[i][j] = mix_energy(epsilon_read[i][i],epsilon_read[j][j], - sigma_read[i][i],sigma_read[j][j]); - sigma[i][j] = mix_distance(sigma_read[i][i],sigma_read[j][j]); - if (ewald_order&(1<<6)) - cut_lj[i][j] = cut_lj_global; - else - cut_lj[i][j] = mix_distance(cut_lj_read[i][i],cut_lj_read[j][j]); - } - else { - sigma[i][j] = sigma_read[i][j]; - epsilon[i][j] = epsilon_read[i][j]; - cut_lj[i][j] = cut_lj_read[i][j]; - } - - double cut = MAX(cut_lj[i][j], cut_coul); - cutsq[i][j] = cut*cut; - cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; - - lj1[i][j] = 48.0 * epsilon[i][j] * pow(sigma[i][j],12.0); - lj2[i][j] = 24.0 * epsilon[i][j] * pow(sigma[i][j],6.0); - lj3[i][j] = 4.0 * epsilon[i][j] * pow(sigma[i][j],12.0); - lj4[i][j] = 4.0 * epsilon[i][j] * pow(sigma[i][j],6.0); - - // check interior rRESPA cutoff - - //if (cut_respa && MIN(cut_lj[i][j],cut_coul) < cut_respa[3]) - //error->all(FLERR,"Pair cutoff < Respa interior cutoff"); - - if (offset_flag) { - double ratio = sigma[i][j] / cut_lj[i][j]; - offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); - } else offset[i][j] = 0.0; - - cutsq[j][i] = cutsq[i][j]; - cut_ljsq[j][i] = cut_ljsq[i][j]; - lj1[j][i] = lj1[i][j]; - lj2[j][i] = lj2[i][j]; - lj3[j][i] = lj3[i][j]; - lj4[j][i] = lj4[i][j]; - offset[j][i] = offset[i][j]; - - return cut; -} - -/* ---------------------------------------------------------------------- - proc 0 writes to restart file -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::write_restart(FILE *fp) -{ - write_restart_settings(fp); - - int i,j; - for (i = 1; i <= atom->ntypes; i++) - for (j = i; j <= atom->ntypes; j++) { - fwrite(&setflag[i][j],sizeof(int),1,fp); - if (setflag[i][j]) { - fwrite(&epsilon_read[i][j],sizeof(double),1,fp); - fwrite(&sigma_read[i][j],sizeof(double),1,fp); - fwrite(&cut_lj_read[i][j],sizeof(double),1,fp); - } - } -} - -/* ---------------------------------------------------------------------- - proc 0 reads from restart file, bcasts -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::read_restart(FILE *fp) -{ - read_restart_settings(fp); - - allocate(); - - int i,j; - int me = comm->me; - for (i = 1; i <= atom->ntypes; i++) - for (j = i; j <= atom->ntypes; j++) { - if (me == 0) fread(&setflag[i][j],sizeof(int),1,fp); - MPI_Bcast(&setflag[i][j],1,MPI_INT,0,world); - if (setflag[i][j]) { - if (me == 0) { - fread(&epsilon_read[i][j],sizeof(double),1,fp); - fread(&sigma_read[i][j],sizeof(double),1,fp); - fread(&cut_lj_read[i][j],sizeof(double),1,fp); - } - MPI_Bcast(&epsilon_read[i][j],1,MPI_DOUBLE,0,world); - MPI_Bcast(&sigma_read[i][j],1,MPI_DOUBLE,0,world); - MPI_Bcast(&cut_lj_read[i][j],1,MPI_DOUBLE,0,world); - } - } -} - -/* ---------------------------------------------------------------------- - proc 0 writes to restart file -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::write_restart_settings(FILE *fp) -{ - fwrite(&cut_lj_global,sizeof(double),1,fp); - fwrite(&cut_coul,sizeof(double),1,fp); - fwrite(&offset_flag,sizeof(int),1,fp); - fwrite(&mix_flag,sizeof(int),1,fp); - fwrite(&ewald_order,sizeof(int),1,fp); -} - -/* ---------------------------------------------------------------------- - proc 0 reads from restart file, bcasts -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::read_restart_settings(FILE *fp) -{ - if (comm->me == 0) { - fread(&cut_lj_global,sizeof(double),1,fp); - fread(&cut_coul,sizeof(double),1,fp); - fread(&offset_flag,sizeof(int),1,fp); - fread(&mix_flag,sizeof(int),1,fp); - fread(&ewald_order,sizeof(int),1,fp); - } - MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); - MPI_Bcast(&cut_coul,1,MPI_DOUBLE,0,world); - MPI_Bcast(&offset_flag,1,MPI_INT,0,world); - MPI_Bcast(&mix_flag,1,MPI_INT,0,world); - MPI_Bcast(&ewald_order,1,MPI_INT,0,world); -} - -/* ---------------------------------------------------------------------- - compute pair interactions -------------------------------------------------------------------------- */ - -void PairLJLongDipoleLong::compute(int eflag, int vflag) -{ - double evdwl,ecoul,fpair; - evdwl = ecoul = 0.0; - - if (eflag || vflag) ev_setup(eflag,vflag); - else evflag = vflag_fdotr = 0; - - double **x = atom->x, *x0 = x[0]; - double **mu = atom->mu, *mu0 = mu[0], *imu, *jmu; - double **tq = atom->torque, *tq0 = tq[0], *tqi; - double **f = atom->f, *f0 = f[0], *fi = f0, fx, fy, fz; - double *q = atom->q, qi = 0, qj; - int *type = atom->type; - int nlocal = atom->nlocal; - double *special_coul = force->special_coul; - double *special_lj = force->special_lj; - int newton_pair = force->newton_pair; - double qqrd2e = force->qqrd2e; - - int i, j; - int order1 = ewald_order&(1<<1), order3 = ewald_order&(1<<3), - order6 = ewald_order&(1<<6); - int *ineigh, *ineighn, *jneigh, *jneighn, typei, typej, ni; - double *cutsqi, *cut_ljsqi, *lj1i, *lj2i, *lj3i, *lj4i, *offseti; - double rsq, r2inv, force_coul, force_lj; - double g2 = g_ewald*g_ewald, g6 = g2*g2*g2, g8 = g6*g2; - double B0, B1, B2, B3, G0, G1, G2, mudi, mudj, muij; - vector force_d = VECTOR_NULL, ti = VECTOR_NULL, tj = VECTOR_NULL; - vector mui, muj, xi, d; - - double C1 = 2.0 * g_ewald / MY_PIS; - double C2 = 2.0 * g2 * C1; - double C3 = 2.0 * g2 * C2; - - ineighn = (ineigh = list->ilist)+list->inum; - - for (; ineighfirstneigh[i])+list->numneigh[i]; - - for (; jneigh= cutsqi[typej = type[j]]) continue; - r2inv = 1.0/rsq; - - if (order3 && (rsq < cut_coulsq)) { // dipole - memcpy(muj, jmu = mu0+(j<<2), sizeof(vector)); - { // series real space - register double r = sqrt(rsq); - register double x = g_ewald*r; - register double f = exp(-x*x)*qqrd2e; - - B0 = 1.0/(1.0+EWALD_P*x); // eqn 2.8 - B0 *= ((((A5*B0+A4)*B0+A3)*B0+A2)*B0+A1)*f/r; - B1 = (B0 + C1 * f) * r2inv; - B2 = (3.0*B1 + C2 * f) * r2inv; - B3 = (5.0*B2 + C3 * f) * r2inv; - - mudi = mui[0]*d[0]+mui[1]*d[1]+mui[2]*d[2]; - mudj = muj[0]*d[0]+muj[1]*d[1]+muj[2]*d[2]; - muij = mui[0]*muj[0]+mui[1]*muj[1]+mui[2]*muj[2]; - G0 = qi*(qj = q[j]); // eqn 2.10 - G1 = qi*mudj-qj*mudi+muij; - G2 = -mudi*mudj; - force_coul = G0*B1+G1*B2+G2*B3; - - mudi *= B2; mudj *= B2; // torque contribs - ti[0] = mudj*d[0]+(qj*d[0]-muj[0])*B1; - ti[1] = mudj*d[1]+(qj*d[1]-muj[1])*B1; - ti[2] = mudj*d[2]+(qj*d[2]-muj[2])*B1; - - if (newton_pair || j < nlocal) { - tj[0] = mudi*d[0]-(qi*d[0]+mui[0])*B1; - tj[1] = mudi*d[1]-(qi*d[1]+mui[1])*B1; - tj[2] = mudi*d[2]-(qi*d[2]+mui[2])*B1; - } - - if (eflag) ecoul = G0*B0+G1*B1+G2*B2; - if (ni > 0) { // adj part, eqn 2.13 - force_coul -= (f = qqrd2e*(1.0-special_coul[ni])/r)*( - (3.0*G1+15.0*G2*r2inv)*r2inv+G0)*r2inv; - if (eflag) - ecoul -= f*((G1+3.0*G2*r2inv)*r2inv+G0); - B1 -= f*r2inv; - } - B0 = mudj+qj*B1; B3 = -qi*B1+mudi; // position independent - if (ni > 0) B0 -= f*3.0*mudj*r2inv*r2inv/B2; - if (ni > 0) B3 -= f*3.0*mudi*r2inv*r2inv/B2; - force_d[0] = B0*mui[0]+B3*muj[0]; // force contribs - force_d[1] = B0*mui[1]+B3*muj[1]; - force_d[2] = B0*mui[2]+B3*muj[2]; - if (ni > 0) { - ti[0] -= f*(3.0*mudj*r2inv*r2inv*d[0]/B2+(qj*r2inv*d[0]-muj[0]*r2inv)); - ti[1] -= f*(3.0*mudj*r2inv*r2inv*d[1]/B2+(qj*r2inv*d[1]-muj[1]*r2inv)); - ti[2] -= f*(3.0*mudj*r2inv*r2inv*d[2]/B2+(qj*r2inv*d[2]-muj[2]*r2inv)); - if (newton_pair || j < nlocal) { - tj[0] -= f*(3.0*mudi*r2inv*r2inv*d[0]/B2-(qi*r2inv*d[0]+mui[0]*r2inv)); - tj[1] -= f*(3.0*mudi*r2inv*r2inv*d[1]/B2-(qi*r2inv*d[1]+mui[1]*r2inv)); - tj[2] -= f*(3.0*mudi*r2inv*r2inv*d[2]/B2-(qi*r2inv*d[2]+mui[2]*r2inv)); - } - } - } // table real space - } else { - force_coul = ecoul = 0.0; - memset(force_d, 0, 3*sizeof(double)); - } - - if (rsq < cut_ljsqi[typej]) { // lj - if (order6) { // long-range lj - register double rn = r2inv*r2inv*r2inv; - register double x2 = g2*rsq, a2 = 1.0/x2; - x2 = a2*exp(-x2)*lj4i[typej]; - if (ni < 0) { - force_lj = - (rn*=rn)*lj1i[typej]-g8*(((6.0*a2+6.0)*a2+3.0)*a2+1.0)*x2*rsq; - if (eflag) evdwl = rn*lj3i[typej]-g6*((a2+1.0)*a2+0.5)*x2; - } - else { // special case - register double f = special_lj[ni], t = rn*(1.0-f); - force_lj = f*(rn *= rn)*lj1i[typej]- - g8*(((6.0*a2+6.0)*a2+3.0)*a2+1.0)*x2*rsq+t*lj2i[typej]; - if (eflag) evdwl = - f*rn*lj3i[typej]-g6*((a2+1.0)*a2+0.5)*x2+t*lj4i[typej]; - } - } - else { // cut lj - register double rn = r2inv*r2inv*r2inv; - if (ni < 0) { - force_lj = rn*(rn*lj1i[typej]-lj2i[typej]); - if (eflag) evdwl = rn*(rn*lj3i[typej]-lj4i[typej])-offseti[typej]; - } - else { // special case - register double f = special_lj[ni]; - force_lj = f*rn*(rn*lj1i[typej]-lj2i[typej]); - if (eflag) evdwl = f*( - rn*(rn*lj3i[typej]-lj4i[typej])-offseti[typej]); - } - } - force_lj *= r2inv; - } - else force_lj = evdwl = 0.0; - - fpair = force_coul+force_lj; // force - if (newton_pair || j < nlocal) { - register double *fj = f0+(j+(j<<1)); - fi[0] += fx = d[0]*fpair+force_d[0]; fj[0] -= fx; - fi[1] += fy = d[1]*fpair+force_d[1]; fj[1] -= fy; - fi[2] += fz = d[2]*fpair+force_d[2]; fj[2] -= fz; - tqi[0] += mui[1]*ti[2]-mui[2]*ti[1]; // torque - tqi[1] += mui[2]*ti[0]-mui[0]*ti[2]; - tqi[2] += mui[0]*ti[1]-mui[1]*ti[0]; - register double *tqj = tq0+(j+(j<<1)); - tqj[0] += muj[1]*tj[2]-muj[2]*tj[1]; - tqj[1] += muj[2]*tj[0]-muj[0]*tj[2]; - tqj[2] += muj[0]*tj[1]-muj[1]*tj[0]; - } - else { - fi[0] += fx = d[0]*fpair+force_d[0]; // force - fi[1] += fy = d[1]*fpair+force_d[1]; - fi[2] += fz = d[2]*fpair+force_d[2]; - tqi[0] += mui[1]*ti[2]-mui[2]*ti[1]; // torque - tqi[1] += mui[2]*ti[0]-mui[0]*ti[2]; - tqi[2] += mui[0]*ti[1]-mui[1]*ti[0]; - } - - if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair, - evdwl,ecoul,fx,fy,fz,d[0],d[1],d[2]); - } - } - - if (vflag_fdotr) virial_fdotr_compute(); -} - -/* ---------------------------------------------------------------------- */ - -/* -double PairLJLongDipoleLong::single(int i, int j, int itype, int jtype, - double rsq, double factor_coul, double factor_lj, - double &fforce) -{ - double r6inv, force_coul, force_lj; - double g2 = g_ewald*g_ewald, g6 = g2*g2*g2, g8 = g6*g2, *q = atom->q; - - double eng = 0.0; - double r2inv = 1.0/rsq; - - if ((ewald_order&(1<<3)) && (rsq < cut_coulsq)) { // coulombic - double *mui = atom->mu[i], *muj = atom->mu[j]; - double *xi = atom->x[i], *xj = atom->x[j]; - double qi = q[i], qj = q[j]; - double G0, G1, G2, B0, B1, B2, B3, mudi, mudj, muij; - vector d = {xi[0]-xj[0], xi[1]-xj[1], xi[2]-xj[2]}; - { // series real space - register double r = sqrt(rsq); - register double x = g_ewald*r; - register double f = exp(-x*x)*qqrd2e; - - B0 = 1.0/(1.0+EWALD_P*x); // eqn 2.8 - B0 *= ((((A5*B0+A4)*B0+A3)*B0+A2)*B0+A1)*f/r; - B1 = (B0 + C1 * f) * r2inv; - B2 = (3.0*B1 + C2 * f) * r2inv; - B3 = (5.0*B2 + C3 * f) * r2inv; - - mudi = mui[0]*d[0]+mui[1]*d[1]+mui[2]*d[2]; - mudj = muj[0]*d[0]+muj[1]*d[1]+muj[2]*d[2]; - muij = mui[0]*muj[0]+mui[1]*muj[1]+mui[2]*muj[2]; - G0 = qi*(qj = q[j]); // eqn 2.10 - G1 = qi*mudj-qj*mudi+muij; - G2 = -mudi*mudj; - force_coul = G0*B1+G1*B2+G2*B3; - - eng += G0*B0+G1*B1+G2*B2; - if (factor_coul < 1.0) { // adj part, eqn 2.13 - force_coul -= (f = force->qqrd2e*(1.0-factor_coul)/r)*( - (3.0*G1+6.0*muij+15.0*G2*r2inv)*r2inv+G0); - eng -= f*((G1+3.0*G2*r2inv)*r2inv+G0); - B1 -= f*r2inv; - } - B0 = mudj*B2-qj*B1; B3 = qi*B1+mudi*B2; // position independent - //force_d[0] = B0*mui[0]+B3*muj[0]; // force contributions - //force_d[1] = B0*mui[1]+B3*muj[1]; - //force_d[2] = B0*mui[2]+B3*muj[2]; - } // table real space - } - else force_coul = 0.0; - - if (rsq < cut_ljsq[itype][jtype]) { // lennard-jones - r6inv = r2inv*r2inv*r2inv; - if (ewald_order&0x40) { // long-range - register double x2 = g2*rsq, a2 = 1.0/x2, t = r6inv*(1.0-factor_lj); - x2 = a2*exp(-x2)*lj4[itype][jtype]; - force_lj = factor_lj*(r6inv *= r6inv)*lj1[itype][jtype]- - g8*(((6.0*a2+6.0)*a2+3.0)*a2+a2)*x2*rsq+t*lj2[itype][jtype]; - eng += factor_lj*r6inv*lj3[itype][jtype]- - g6*((a2+1.0)*a2+0.5)*x2+t*lj4[itype][jtype]; - } - else { // cut - force_lj = factor_lj*r6inv*(lj1[itype][jtype]*r6inv-lj2[itype][jtype]); - eng += factor_lj*(r6inv*(r6inv*lj3[itype][jtype]- - lj4[itype][jtype])-offset[itype][jtype]); - } - } - else force_lj = 0.0; - - fforce = (force_coul+force_lj)*r2inv; - return eng; -} -*/ - +/* ---------------------------------------------------------------------- + LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator + http://lammps.sandia.gov, Sandia National Laboratories + Steve Plimpton, sjplimp@sandia.gov + + 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: Pieter J. in 't Veld and Stan Moore (Sandia) +------------------------------------------------------------------------- */ + +#include "math.h" +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "math_const.h" +#include "math_vector.h" +#include "pair_lj_long_dipole_long.h" +#include "atom.h" +#include "comm.h" +#include "neighbor.h" +#include "neigh_list.h" +#include "neigh_request.h" +#include "force.h" +#include "kspace.h" +#include "update.h" +#include "integrate.h" +#include "respa.h" +#include "memory.h" +#include "error.h" + +using namespace LAMMPS_NS; +using namespace MathConst; + +#define EWALD_F 1.12837917 +#define EWALD_P 0.3275911 +#define A1 0.254829592 +#define A2 -0.284496736 +#define A3 1.421413741 +#define A4 -1.453152027 +#define A5 1.061405429 + +// ---------------------------------------------------------------------- + +PairLJLongDipoleLong::PairLJLongDipoleLong(LAMMPS *lmp) : Pair(lmp) +{ + dispersionflag = ewaldflag = dipoleflag = 1; + respa_enable = 0; + single_enable = 0; +} + +// ---------------------------------------------------------------------- +// global settings +// ---------------------------------------------------------------------- + +#define PAIR_ILLEGAL "Illegal pair_style lj/long/dipole/long command" +#define PAIR_CUTOFF "Only one cut-off allowed when requesting all long" +#define PAIR_MISSING "Cut-offs missing in pair_style lj/long/dipole/long" +#define PAIR_COUL_CUT "Coulombic cut not supported in pair_style lj/long/dipole/long" +#define PAIR_LARGEST "Using largest cut-off for lj/long/dipole/long long long" +#define PAIR_MIX "Geometric mixing assumed for 1/r^6 coefficients" + +void PairLJLongDipoleLong::options(char **arg, int order) +{ + const char *option[] = {"long", "cut", "off", NULL}; + int i; + + if (!*arg) error->all(FLERR,PAIR_ILLEGAL); + for (i=0; option[i]&&strcmp(arg[0], option[i]); ++i); + switch (i) { + default: error->all(FLERR,PAIR_ILLEGAL); + case 0: ewald_order |= 1<all(FLERR,"Illegal pair_style command"); + + ewald_off = 0; + ewald_order = 0; + options(arg, 6); + options(++arg, 3); + options(arg, 1); + if (!comm->me && ewald_order&(1<<6)) + error->warning(FLERR,PAIR_MIX); + if (!comm->me && ewald_order==((1<<3)|(1<<6))) + error->warning(FLERR,PAIR_LARGEST); + if (!*(++arg)) + error->all(FLERR,PAIR_MISSING); + if (!((ewald_order^ewald_off)&(1<<3))) + error->all(FLERR,PAIR_COUL_CUT); + cut_lj_global = force->numeric(FLERR,*(arg++)); + if (narg == 4 && (ewald_order==74)) + error->all(FLERR,PAIR_CUTOFF); + if (narg == 4) cut_coul = force->numeric(FLERR,*(arg++)); + else cut_coul = cut_lj_global; + + if (allocated) { // reset explicit cuts + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i+1; j <= atom->ntypes; j++) + if (setflag[i][j]) cut_lj[i][j] = cut_lj_global; + } +} + +// ---------------------------------------------------------------------- +// free all arrays +// ---------------------------------------------------------------------- + +PairLJLongDipoleLong::~PairLJLongDipoleLong() +{ + if (allocated) { + memory->destroy(setflag); + memory->destroy(cutsq); + + memory->destroy(cut_lj_read); + memory->destroy(cut_lj); + memory->destroy(cut_ljsq); + memory->destroy(epsilon_read); + memory->destroy(epsilon); + memory->destroy(sigma_read); + memory->destroy(sigma); + memory->destroy(lj1); + memory->destroy(lj2); + memory->destroy(lj3); + memory->destroy(lj4); + memory->destroy(offset); + } + //if (ftable) free_tables(); +} + +/* ---------------------------------------------------------------------- + allocate all arrays +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::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"); + + memory->create(cut_lj_read,n+1,n+1,"pair:cut_lj_read"); + memory->create(cut_lj,n+1,n+1,"pair:cut_lj"); + memory->create(cut_ljsq,n+1,n+1,"pair:cut_ljsq"); + memory->create(epsilon_read,n+1,n+1,"pair:epsilon_read"); + memory->create(epsilon,n+1,n+1,"pair:epsilon"); + memory->create(sigma_read,n+1,n+1,"pair:sigma_read"); + memory->create(sigma,n+1,n+1,"pair:sigma"); + memory->create(lj1,n+1,n+1,"pair:lj1"); + memory->create(lj2,n+1,n+1,"pair:lj2"); + memory->create(lj3,n+1,n+1,"pair:lj3"); + memory->create(lj4,n+1,n+1,"pair:lj4"); + memory->create(offset,n+1,n+1,"pair:offset"); +} + +/* ---------------------------------------------------------------------- + extract protected data from object +------------------------------------------------------------------------- */ + +void *PairLJLongDipoleLong::extract(const char *id, int &dim) +{ + const char *ids[] = { + "B", "sigma", "epsilon", "ewald_order", "ewald_cut", "ewald_mix", + "cut_coul", "cut_vdwl", NULL}; + void *ptrs[] = { + lj4, sigma, epsilon, &ewald_order, &cut_coul, &mix_flag, &cut_coul, + &cut_lj_global, NULL}; + int i; + + for (i=0; ids[i]&&strcmp(ids[i], id); ++i); + if (i <= 2) dim = 2; + else dim = 0; + return ptrs[i]; +} + +/* ---------------------------------------------------------------------- + set coeffs for one or more type pairs +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::coeff(int narg, char **arg) +{ + if (narg < 4 || narg > 5) error->all(FLERR,"Incorrect args for pair coefficients"); + if (!allocated) allocate(); + + int ilo,ihi,jlo,jhi; + force->bounds(arg[0],atom->ntypes,ilo,ihi); + force->bounds(arg[1],atom->ntypes,jlo,jhi); + + double epsilon_one = force->numeric(FLERR,arg[2]); + double sigma_one = force->numeric(FLERR,arg[3]); + + double cut_lj_one = cut_lj_global; + if (narg == 5) cut_lj_one = force->numeric(FLERR,arg[4]); + + int count = 0; + for (int i = ilo; i <= ihi; i++) { + for (int j = MAX(jlo,i); j <= jhi; j++) { + epsilon_read[i][j] = epsilon_one; + sigma_read[i][j] = sigma_one; + cut_lj_read[i][j] = cut_lj_one; + setflag[i][j] = 1; + count++; + } + } + + if (count == 0) error->all(FLERR,"Incorrect args for pair coefficients"); +} + +/* ---------------------------------------------------------------------- + init specific to this pair style +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::init_style() +{ + const char *style3[] = {"ewald/disp", NULL}; + const char *style6[] = {"ewald/disp", NULL}; + int i; + + if (strcmp(update->unit_style,"electron") == 0) + error->all(FLERR,"Cannot (yet) use 'electron' units with dipoles"); + + // require an atom style with charge defined + + if (!atom->q_flag && (ewald_order&(1<<1))) + error->all(FLERR, + "Invoking coulombic in pair style lj/long/dipole/long requires atom attribute q"); + if (!atom->mu && (ewald_order&(1<<3))) + error->all(FLERR,"Pair lj/long/dipole/long requires atom attributes mu, torque"); + if (!atom->torque && (ewald_order&(1<<3))) + error->all(FLERR,"Pair lj/long/dipole/long requires atom attributes mu, torque"); + + neighbor->request(this); + + cut_coulsq = cut_coul * cut_coul; + + // ensure use of KSpace long-range solver, set g_ewald + + if (ewald_order&(1<<3)) { // r^-1 kspace + if (force->kspace == NULL) + error->all(FLERR,"Pair style is incompatible with KSpace style"); + for (i=0; style3[i]&&strcmp(force->kspace_style, style3[i]); ++i); + if (!style3[i]) + error->all(FLERR,"Pair style is incompatible with KSpace style"); + } + if (ewald_order&(1<<6)) { // r^-6 kspace + if (force->kspace == NULL) + error->all(FLERR,"Pair style is incompatible with KSpace style"); + for (i=0; style6[i]&&strcmp(force->kspace_style, style6[i]); ++i); + if (!style6[i]) + error->all(FLERR,"Pair style is incompatible with KSpace style"); + } + if (force->kspace) g_ewald = force->kspace->g_ewald; +} + +/* ---------------------------------------------------------------------- + neighbor callback to inform pair style of neighbor list to use + regular or rRESPA +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::init_list(int id, NeighList *ptr) +{ + if (id == 0) list = ptr; + else if (id == 1) listinner = ptr; + else if (id == 2) listmiddle = ptr; + else if (id == 3) listouter = ptr; + + if (id) + error->all(FLERR,"Pair style lj/long/dipole/long does not currently support respa"); +} + +/* ---------------------------------------------------------------------- + init for one type pair i,j and corresponding j,i +------------------------------------------------------------------------- */ + +double PairLJLongDipoleLong::init_one(int i, int j) +{ + if ((ewald_order&(1<<6))||(setflag[i][j] == 0)) { + epsilon[i][j] = mix_energy(epsilon_read[i][i],epsilon_read[j][j], + sigma_read[i][i],sigma_read[j][j]); + sigma[i][j] = mix_distance(sigma_read[i][i],sigma_read[j][j]); + if (ewald_order&(1<<6)) + cut_lj[i][j] = cut_lj_global; + else + cut_lj[i][j] = mix_distance(cut_lj_read[i][i],cut_lj_read[j][j]); + } + else { + sigma[i][j] = sigma_read[i][j]; + epsilon[i][j] = epsilon_read[i][j]; + cut_lj[i][j] = cut_lj_read[i][j]; + } + + double cut = MAX(cut_lj[i][j], cut_coul); + cutsq[i][j] = cut*cut; + cut_ljsq[i][j] = cut_lj[i][j] * cut_lj[i][j]; + + lj1[i][j] = 48.0 * epsilon[i][j] * pow(sigma[i][j],12.0); + lj2[i][j] = 24.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + lj3[i][j] = 4.0 * epsilon[i][j] * pow(sigma[i][j],12.0); + lj4[i][j] = 4.0 * epsilon[i][j] * pow(sigma[i][j],6.0); + + // check interior rRESPA cutoff + + //if (cut_respa && MIN(cut_lj[i][j],cut_coul) < cut_respa[3]) + //error->all(FLERR,"Pair cutoff < Respa interior cutoff"); + + if (offset_flag) { + double ratio = sigma[i][j] / cut_lj[i][j]; + offset[i][j] = 4.0 * epsilon[i][j] * (pow(ratio,12.0) - pow(ratio,6.0)); + } else offset[i][j] = 0.0; + + cutsq[j][i] = cutsq[i][j]; + cut_ljsq[j][i] = cut_ljsq[i][j]; + lj1[j][i] = lj1[i][j]; + lj2[j][i] = lj2[i][j]; + lj3[j][i] = lj3[i][j]; + lj4[j][i] = lj4[i][j]; + offset[j][i] = offset[i][j]; + + return cut; +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::write_restart(FILE *fp) +{ + write_restart_settings(fp); + + int i,j; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + fwrite(&setflag[i][j],sizeof(int),1,fp); + if (setflag[i][j]) { + fwrite(&epsilon_read[i][j],sizeof(double),1,fp); + fwrite(&sigma_read[i][j],sizeof(double),1,fp); + fwrite(&cut_lj_read[i][j],sizeof(double),1,fp); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::read_restart(FILE *fp) +{ + read_restart_settings(fp); + + allocate(); + + int i,j; + int me = comm->me; + for (i = 1; i <= atom->ntypes; i++) + for (j = i; j <= atom->ntypes; j++) { + if (me == 0) fread(&setflag[i][j],sizeof(int),1,fp); + MPI_Bcast(&setflag[i][j],1,MPI_INT,0,world); + if (setflag[i][j]) { + if (me == 0) { + fread(&epsilon_read[i][j],sizeof(double),1,fp); + fread(&sigma_read[i][j],sizeof(double),1,fp); + fread(&cut_lj_read[i][j],sizeof(double),1,fp); + } + MPI_Bcast(&epsilon_read[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&sigma_read[i][j],1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_lj_read[i][j],1,MPI_DOUBLE,0,world); + } + } +} + +/* ---------------------------------------------------------------------- + proc 0 writes to restart file +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::write_restart_settings(FILE *fp) +{ + fwrite(&cut_lj_global,sizeof(double),1,fp); + fwrite(&cut_coul,sizeof(double),1,fp); + fwrite(&offset_flag,sizeof(int),1,fp); + fwrite(&mix_flag,sizeof(int),1,fp); + fwrite(&ewald_order,sizeof(int),1,fp); +} + +/* ---------------------------------------------------------------------- + proc 0 reads from restart file, bcasts +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::read_restart_settings(FILE *fp) +{ + if (comm->me == 0) { + fread(&cut_lj_global,sizeof(double),1,fp); + fread(&cut_coul,sizeof(double),1,fp); + fread(&offset_flag,sizeof(int),1,fp); + fread(&mix_flag,sizeof(int),1,fp); + fread(&ewald_order,sizeof(int),1,fp); + } + MPI_Bcast(&cut_lj_global,1,MPI_DOUBLE,0,world); + MPI_Bcast(&cut_coul,1,MPI_DOUBLE,0,world); + MPI_Bcast(&offset_flag,1,MPI_INT,0,world); + MPI_Bcast(&mix_flag,1,MPI_INT,0,world); + MPI_Bcast(&ewald_order,1,MPI_INT,0,world); +} + +/* ---------------------------------------------------------------------- + compute pair interactions +------------------------------------------------------------------------- */ + +void PairLJLongDipoleLong::compute(int eflag, int vflag) +{ + double evdwl,ecoul,fpair; + evdwl = ecoul = 0.0; + + if (eflag || vflag) ev_setup(eflag,vflag); + else evflag = vflag_fdotr = 0; + + double **x = atom->x, *x0 = x[0]; + double **mu = atom->mu, *mu0 = mu[0], *imu, *jmu; + double **tq = atom->torque, *tq0 = tq[0], *tqi; + double **f = atom->f, *f0 = f[0], *fi = f0, fx, fy, fz; + double *q = atom->q, qi = 0, qj; + int *type = atom->type; + int nlocal = atom->nlocal; + double *special_coul = force->special_coul; + double *special_lj = force->special_lj; + int newton_pair = force->newton_pair; + double qqrd2e = force->qqrd2e; + + int i, j; + int order1 = ewald_order&(1<<1), order3 = ewald_order&(1<<3), + order6 = ewald_order&(1<<6); + int *ineigh, *ineighn, *jneigh, *jneighn, typei, typej, ni; + double *cutsqi, *cut_ljsqi, *lj1i, *lj2i, *lj3i, *lj4i, *offseti; + double rsq, r2inv, force_coul, force_lj; + double g2 = g_ewald*g_ewald, g6 = g2*g2*g2, g8 = g6*g2; + double B0, B1, B2, B3, G0, G1, G2, mudi, mudj, muij; + vector force_d = VECTOR_NULL, ti = VECTOR_NULL, tj = VECTOR_NULL; + vector mui, muj, xi, d; + + double C1 = 2.0 * g_ewald / MY_PIS; + double C2 = 2.0 * g2 * C1; + double C3 = 2.0 * g2 * C2; + + ineighn = (ineigh = list->ilist)+list->inum; + + for (; ineighfirstneigh[i])+list->numneigh[i]; + + for (; jneigh= cutsqi[typej = type[j]]) continue; + r2inv = 1.0/rsq; + + if (order3 && (rsq < cut_coulsq)) { // dipole + memcpy(muj, jmu = mu0+(j<<2), sizeof(vector)); + { // series real space + register double r = sqrt(rsq); + register double x = g_ewald*r; + register double f = exp(-x*x)*qqrd2e; + + B0 = 1.0/(1.0+EWALD_P*x); // eqn 2.8 + B0 *= ((((A5*B0+A4)*B0+A3)*B0+A2)*B0+A1)*f/r; + B1 = (B0 + C1 * f) * r2inv; + B2 = (3.0*B1 + C2 * f) * r2inv; + B3 = (5.0*B2 + C3 * f) * r2inv; + + mudi = mui[0]*d[0]+mui[1]*d[1]+mui[2]*d[2]; + mudj = muj[0]*d[0]+muj[1]*d[1]+muj[2]*d[2]; + muij = mui[0]*muj[0]+mui[1]*muj[1]+mui[2]*muj[2]; + G0 = qi*(qj = q[j]); // eqn 2.10 + G1 = qi*mudj-qj*mudi+muij; + G2 = -mudi*mudj; + force_coul = G0*B1+G1*B2+G2*B3; + + mudi *= B2; mudj *= B2; // torque contribs + ti[0] = mudj*d[0]+(qj*d[0]-muj[0])*B1; + ti[1] = mudj*d[1]+(qj*d[1]-muj[1])*B1; + ti[2] = mudj*d[2]+(qj*d[2]-muj[2])*B1; + + if (newton_pair || j < nlocal) { + tj[0] = mudi*d[0]-(qi*d[0]+mui[0])*B1; + tj[1] = mudi*d[1]-(qi*d[1]+mui[1])*B1; + tj[2] = mudi*d[2]-(qi*d[2]+mui[2])*B1; + } + + if (eflag) ecoul = G0*B0+G1*B1+G2*B2; + if (ni > 0) { // adj part, eqn 2.13 + force_coul -= (f = qqrd2e*(1.0-special_coul[ni])/r)*( + (3.0*G1+15.0*G2*r2inv)*r2inv+G0)*r2inv; + if (eflag) + ecoul -= f*((G1+3.0*G2*r2inv)*r2inv+G0); + B1 -= f*r2inv; + } + B0 = mudj+qj*B1; B3 = -qi*B1+mudi; // position independent + if (ni > 0) B0 -= f*3.0*mudj*r2inv*r2inv/B2; + if (ni > 0) B3 -= f*3.0*mudi*r2inv*r2inv/B2; + force_d[0] = B0*mui[0]+B3*muj[0]; // force contribs + force_d[1] = B0*mui[1]+B3*muj[1]; + force_d[2] = B0*mui[2]+B3*muj[2]; + if (ni > 0) { + ti[0] -= f*(3.0*mudj*r2inv*r2inv*d[0]/B2+(qj*r2inv*d[0]-muj[0]*r2inv)); + ti[1] -= f*(3.0*mudj*r2inv*r2inv*d[1]/B2+(qj*r2inv*d[1]-muj[1]*r2inv)); + ti[2] -= f*(3.0*mudj*r2inv*r2inv*d[2]/B2+(qj*r2inv*d[2]-muj[2]*r2inv)); + if (newton_pair || j < nlocal) { + tj[0] -= f*(3.0*mudi*r2inv*r2inv*d[0]/B2-(qi*r2inv*d[0]+mui[0]*r2inv)); + tj[1] -= f*(3.0*mudi*r2inv*r2inv*d[1]/B2-(qi*r2inv*d[1]+mui[1]*r2inv)); + tj[2] -= f*(3.0*mudi*r2inv*r2inv*d[2]/B2-(qi*r2inv*d[2]+mui[2]*r2inv)); + } + } + } // table real space + } else { + force_coul = ecoul = 0.0; + memset(force_d, 0, 3*sizeof(double)); + } + + if (rsq < cut_ljsqi[typej]) { // lj + if (order6) { // long-range lj + register double rn = r2inv*r2inv*r2inv; + register double x2 = g2*rsq, a2 = 1.0/x2; + x2 = a2*exp(-x2)*lj4i[typej]; + if (ni < 0) { + force_lj = + (rn*=rn)*lj1i[typej]-g8*(((6.0*a2+6.0)*a2+3.0)*a2+1.0)*x2*rsq; + if (eflag) evdwl = rn*lj3i[typej]-g6*((a2+1.0)*a2+0.5)*x2; + } + else { // special case + register double f = special_lj[ni], t = rn*(1.0-f); + force_lj = f*(rn *= rn)*lj1i[typej]- + g8*(((6.0*a2+6.0)*a2+3.0)*a2+1.0)*x2*rsq+t*lj2i[typej]; + if (eflag) evdwl = + f*rn*lj3i[typej]-g6*((a2+1.0)*a2+0.5)*x2+t*lj4i[typej]; + } + } + else { // cut lj + register double rn = r2inv*r2inv*r2inv; + if (ni < 0) { + force_lj = rn*(rn*lj1i[typej]-lj2i[typej]); + if (eflag) evdwl = rn*(rn*lj3i[typej]-lj4i[typej])-offseti[typej]; + } + else { // special case + register double f = special_lj[ni]; + force_lj = f*rn*(rn*lj1i[typej]-lj2i[typej]); + if (eflag) evdwl = f*( + rn*(rn*lj3i[typej]-lj4i[typej])-offseti[typej]); + } + } + force_lj *= r2inv; + } + else force_lj = evdwl = 0.0; + + fpair = force_coul+force_lj; // force + if (newton_pair || j < nlocal) { + register double *fj = f0+(j+(j<<1)); + fi[0] += fx = d[0]*fpair+force_d[0]; fj[0] -= fx; + fi[1] += fy = d[1]*fpair+force_d[1]; fj[1] -= fy; + fi[2] += fz = d[2]*fpair+force_d[2]; fj[2] -= fz; + tqi[0] += mui[1]*ti[2]-mui[2]*ti[1]; // torque + tqi[1] += mui[2]*ti[0]-mui[0]*ti[2]; + tqi[2] += mui[0]*ti[1]-mui[1]*ti[0]; + register double *tqj = tq0+(j+(j<<1)); + tqj[0] += muj[1]*tj[2]-muj[2]*tj[1]; + tqj[1] += muj[2]*tj[0]-muj[0]*tj[2]; + tqj[2] += muj[0]*tj[1]-muj[1]*tj[0]; + } + else { + fi[0] += fx = d[0]*fpair+force_d[0]; // force + fi[1] += fy = d[1]*fpair+force_d[1]; + fi[2] += fz = d[2]*fpair+force_d[2]; + tqi[0] += mui[1]*ti[2]-mui[2]*ti[1]; // torque + tqi[1] += mui[2]*ti[0]-mui[0]*ti[2]; + tqi[2] += mui[0]*ti[1]-mui[1]*ti[0]; + } + + if (evflag) ev_tally_xyz(i,j,nlocal,newton_pair, + evdwl,ecoul,fx,fy,fz,d[0],d[1],d[2]); + } + } + + if (vflag_fdotr) virial_fdotr_compute(); +} + +/* ---------------------------------------------------------------------- */ + +/* +double PairLJLongDipoleLong::single(int i, int j, int itype, int jtype, + double rsq, double factor_coul, double factor_lj, + double &fforce) +{ + double r6inv, force_coul, force_lj; + double g2 = g_ewald*g_ewald, g6 = g2*g2*g2, g8 = g6*g2, *q = atom->q; + + double eng = 0.0; + double r2inv = 1.0/rsq; + + if ((ewald_order&(1<<3)) && (rsq < cut_coulsq)) { // coulombic + double *mui = atom->mu[i], *muj = atom->mu[j]; + double *xi = atom->x[i], *xj = atom->x[j]; + double qi = q[i], qj = q[j]; + double G0, G1, G2, B0, B1, B2, B3, mudi, mudj, muij; + vector d = {xi[0]-xj[0], xi[1]-xj[1], xi[2]-xj[2]}; + { // series real space + register double r = sqrt(rsq); + register double x = g_ewald*r; + register double f = exp(-x*x)*qqrd2e; + + B0 = 1.0/(1.0+EWALD_P*x); // eqn 2.8 + B0 *= ((((A5*B0+A4)*B0+A3)*B0+A2)*B0+A1)*f/r; + B1 = (B0 + C1 * f) * r2inv; + B2 = (3.0*B1 + C2 * f) * r2inv; + B3 = (5.0*B2 + C3 * f) * r2inv; + + mudi = mui[0]*d[0]+mui[1]*d[1]+mui[2]*d[2]; + mudj = muj[0]*d[0]+muj[1]*d[1]+muj[2]*d[2]; + muij = mui[0]*muj[0]+mui[1]*muj[1]+mui[2]*muj[2]; + G0 = qi*(qj = q[j]); // eqn 2.10 + G1 = qi*mudj-qj*mudi+muij; + G2 = -mudi*mudj; + force_coul = G0*B1+G1*B2+G2*B3; + + eng += G0*B0+G1*B1+G2*B2; + if (factor_coul < 1.0) { // adj part, eqn 2.13 + force_coul -= (f = force->qqrd2e*(1.0-factor_coul)/r)*( + (3.0*G1+6.0*muij+15.0*G2*r2inv)*r2inv+G0); + eng -= f*((G1+3.0*G2*r2inv)*r2inv+G0); + B1 -= f*r2inv; + } + B0 = mudj*B2-qj*B1; B3 = qi*B1+mudi*B2; // position independent + //force_d[0] = B0*mui[0]+B3*muj[0]; // force contributions + //force_d[1] = B0*mui[1]+B3*muj[1]; + //force_d[2] = B0*mui[2]+B3*muj[2]; + } // table real space + } + else force_coul = 0.0; + + if (rsq < cut_ljsq[itype][jtype]) { // lennard-jones + r6inv = r2inv*r2inv*r2inv; + if (ewald_order&0x40) { // long-range + register double x2 = g2*rsq, a2 = 1.0/x2, t = r6inv*(1.0-factor_lj); + x2 = a2*exp(-x2)*lj4[itype][jtype]; + force_lj = factor_lj*(r6inv *= r6inv)*lj1[itype][jtype]- + g8*(((6.0*a2+6.0)*a2+3.0)*a2+a2)*x2*rsq+t*lj2[itype][jtype]; + eng += factor_lj*r6inv*lj3[itype][jtype]- + g6*((a2+1.0)*a2+0.5)*x2+t*lj4[itype][jtype]; + } + else { // cut + force_lj = factor_lj*r6inv*(lj1[itype][jtype]*r6inv-lj2[itype][jtype]); + eng += factor_lj*(r6inv*(r6inv*lj3[itype][jtype]- + lj4[itype][jtype])-offset[itype][jtype]); + } + } + else force_lj = 0.0; + + fforce = (force_coul+force_lj)*r2inv; + return eng; +} +*/ + diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index b33371fd84..5bacae48be 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -297,7 +297,6 @@ void PPPM::init() error->warning(FLERR,"Reducing PPPM order b/c stencil extends " "beyond nearest neighbor processor"); - if (stagger_flag && !differentiation_flag) compute_gf_denom(); set_grid_global(); set_grid_local(); if (overlap_allowed) break; @@ -781,7 +780,7 @@ void PPPM::allocate() // summation coeffs - if (!stagger_flag) memory->create(gf_b,order,"pppm:gf_b"); + memory->create(gf_b,order,"pppm:gf_b"); memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); @@ -867,7 +866,6 @@ void PPPM::deallocate() } memory->destroy(gf_b); - if (stagger_flag) gf_b = NULL; memory->destroy2d_offset(rho1d,-order/2); memory->destroy2d_offset(drho1d,-order/2); memory->destroy2d_offset(rho_coeff,(1-order)/2); @@ -984,7 +982,7 @@ void PPPM::set_grid_global() if (!gridflag) { - if (differentiation_flag == 1 || stagger_flag) { + if (differentiation_flag == 1) { h = h_x = h_y = h_z = 4.0/g_ewald; int count = 0; @@ -1133,7 +1131,7 @@ double PPPM::compute_df_kspace() double zprd_slab = zprd*slab_volfactor; bigint natoms = atom->natoms; double df_kspace = 0.0; - if (differentiation_flag == 1 || stagger_flag) { + if (differentiation_flag == 1) { double qopt = compute_qopt(); df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab); } else { @@ -1422,12 +1420,6 @@ void PPPM::set_grid_local() nzlo_out = nlo + nlower; nzhi_out = nhi + nupper; - if (stagger_flag) { - nxhi_out++; - nyhi_out++; - nzhi_out++; - } - // for slab PPPM, change the grid boundary for processors at +z end // to include the empty volume between periodically repeating slabs // for slab PPPM, want charge data communicated from -z proc to +z proc, diff --git a/src/KSPACE/pppm.h b/src/KSPACE/pppm.h index a9686487aa..7da6d80560 100644 --- a/src/KSPACE/pppm.h +++ b/src/KSPACE/pppm.h @@ -120,8 +120,8 @@ class PPPM : public KSpace { int factorable(int); double compute_df_kspace(); double estimate_ik_error(double, double, bigint); - virtual double compute_qopt(); - virtual void compute_gf_denom(); + double compute_qopt(); + void compute_gf_denom(); virtual void compute_gf_ik(); virtual void compute_gf_ad(); void compute_sf_precoeff(); From df08f3e483fad72734843a0a9979f194d4886399 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:54:17 +0000 Subject: [PATCH 35/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10137 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm.cpp | 14 +++++++++++--- src/KSPACE/pppm.h | 4 ++-- src/KSPACE/pppm_stagger.cpp | 6 ++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/KSPACE/pppm.cpp b/src/KSPACE/pppm.cpp index 5bacae48be..b33371fd84 100644 --- a/src/KSPACE/pppm.cpp +++ b/src/KSPACE/pppm.cpp @@ -297,6 +297,7 @@ void PPPM::init() error->warning(FLERR,"Reducing PPPM order b/c stencil extends " "beyond nearest neighbor processor"); + if (stagger_flag && !differentiation_flag) compute_gf_denom(); set_grid_global(); set_grid_local(); if (overlap_allowed) break; @@ -780,7 +781,7 @@ void PPPM::allocate() // summation coeffs - memory->create(gf_b,order,"pppm:gf_b"); + if (!stagger_flag) memory->create(gf_b,order,"pppm:gf_b"); memory->create2d_offset(rho1d,3,-order/2,order/2,"pppm:rho1d"); memory->create2d_offset(drho1d,3,-order/2,order/2,"pppm:drho1d"); memory->create2d_offset(rho_coeff,order,(1-order)/2,order/2,"pppm:rho_coeff"); @@ -866,6 +867,7 @@ void PPPM::deallocate() } memory->destroy(gf_b); + if (stagger_flag) gf_b = NULL; memory->destroy2d_offset(rho1d,-order/2); memory->destroy2d_offset(drho1d,-order/2); memory->destroy2d_offset(rho_coeff,(1-order)/2); @@ -982,7 +984,7 @@ void PPPM::set_grid_global() if (!gridflag) { - if (differentiation_flag == 1) { + if (differentiation_flag == 1 || stagger_flag) { h = h_x = h_y = h_z = 4.0/g_ewald; int count = 0; @@ -1131,7 +1133,7 @@ double PPPM::compute_df_kspace() double zprd_slab = zprd*slab_volfactor; bigint natoms = atom->natoms; double df_kspace = 0.0; - if (differentiation_flag == 1) { + if (differentiation_flag == 1 || stagger_flag) { double qopt = compute_qopt(); df_kspace = sqrt(qopt/natoms)*q2/(xprd*yprd*zprd_slab); } else { @@ -1420,6 +1422,12 @@ void PPPM::set_grid_local() nzlo_out = nlo + nlower; nzhi_out = nhi + nupper; + if (stagger_flag) { + nxhi_out++; + nyhi_out++; + nzhi_out++; + } + // for slab PPPM, change the grid boundary for processors at +z end // to include the empty volume between periodically repeating slabs // for slab PPPM, want charge data communicated from -z proc to +z proc, diff --git a/src/KSPACE/pppm.h b/src/KSPACE/pppm.h index 7da6d80560..a9686487aa 100644 --- a/src/KSPACE/pppm.h +++ b/src/KSPACE/pppm.h @@ -120,8 +120,8 @@ class PPPM : public KSpace { int factorable(int); double compute_df_kspace(); double estimate_ik_error(double, double, bigint); - double compute_qopt(); - void compute_gf_denom(); + virtual double compute_qopt(); + virtual void compute_gf_denom(); virtual void compute_gf_ik(); virtual void compute_gf_ad(); void compute_sf_precoeff(); diff --git a/src/KSPACE/pppm_stagger.cpp b/src/KSPACE/pppm_stagger.cpp index 996aa8e27d..2815f1e4e3 100755 --- a/src/KSPACE/pppm_stagger.cpp +++ b/src/KSPACE/pppm_stagger.cpp @@ -52,8 +52,7 @@ enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; /* ---------------------------------------------------------------------- */ -PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : - PPPM(lmp, narg, arg) +PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : PPPM(lmp, narg, arg) { if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm/stagger command"); stagger_flag = 1; @@ -108,8 +107,7 @@ void PPPMStagger::init() // error check if (domain->triclinic) - error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger " - "with triclinic systems"); + error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger with triclinic systems"); PPPM::init(); } From ede1d487733b140e4d9d4862048a6684693fd4ab Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 27 Jun 2013 23:55:04 +0000 Subject: [PATCH 36/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10138 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/KSPACE/pppm_stagger.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/KSPACE/pppm_stagger.cpp b/src/KSPACE/pppm_stagger.cpp index 2815f1e4e3..c8612f090f 100755 --- a/src/KSPACE/pppm_stagger.cpp +++ b/src/KSPACE/pppm_stagger.cpp @@ -52,7 +52,8 @@ enum{FORWARD_IK,FORWARD_AD,FORWARD_IK_PERATOM,FORWARD_AD_PERATOM}; /* ---------------------------------------------------------------------- */ -PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : PPPM(lmp, narg, arg) +PPPMStagger::PPPMStagger(LAMMPS *lmp, int narg, char **arg) : + PPPM(lmp, narg, arg) { if (narg < 1) error->all(FLERR,"Illegal kspace_style pppm/stagger command"); stagger_flag = 1; @@ -107,7 +108,8 @@ void PPPMStagger::init() // error check if (domain->triclinic) - error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger with triclinic systems"); + error->all(FLERR,"Cannot (yet) use kspace_style pppm/stagger " + "with triclinic systems"); PPPM::init(); } @@ -220,7 +222,8 @@ void PPPMStagger::compute(int eflag, int vflag) if (vflag_global) { double virial_all[6]; MPI_Allreduce(virial,virial_all,6,MPI_DOUBLE,MPI_SUM,world); - for (i = 0; i < 6; i++) virial[i] = 0.5*qscale*volume*virial_all[i]/float(nstagger); + for (i = 0; i < 6; i++) + virial[i] = 0.5*qscale*volume*virial_all[i]/float(nstagger); } // per-atom energy/virial From 89fc0b42d167d375b36bf9a5ea9467f644bcf5dd Mon Sep 17 00:00:00 2001 From: sjplimp Date: Fri, 28 Jun 2013 00:00:57 +0000 Subject: [PATCH 37/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10139 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- src/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.h b/src/version.h index 2122e86ca9..73004f2a10 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define LAMMPS_VERSION "25 Jun 2013" +#define LAMMPS_VERSION "26 Jun 2013" From a92e8c271bcb7add8245f5a32f2920fbe35bacc9 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Fri, 28 Jun 2013 00:00:58 +0000 Subject: [PATCH 38/38] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@10140 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- doc/Manual.html | 2 +- doc/Manual.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/Manual.html b/doc/Manual.html index 8e419019b9..fda404efda 100644 --- a/doc/Manual.html +++ b/doc/Manual.html @@ -22,7 +22,7 @@

        LAMMPS Documentation

        -

        25 Jun 2013 version +

        26 Jun 2013 version

        Version info:

        diff --git a/doc/Manual.txt b/doc/Manual.txt index 5b03883408..d64c8cf48d 100644 --- a/doc/Manual.txt +++ b/doc/Manual.txt @@ -18,7 +18,7 @@

        LAMMPS Documentation :c,h3 -25 Jun 2013 version :c,h4 +26 Jun 2013 version :c,h4 Version info: :h4

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