Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
tsoc
openmm
Commits
74912095
Unverified
Commit
74912095
authored
Nov 11, 2025
by
Peter Eastman
Committed by
GitHub
Nov 11, 2025
Browse files
Refactor reference PME (#5131)
parent
c24c619e
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
413 additions
and
766 deletions
+413
-766
platforms/cpu/src/CpuNonbondedForce.cpp
platforms/cpu/src/CpuNonbondedForce.cpp
+5
-9
platforms/reference/include/ReferenceConstantPotential.h
platforms/reference/include/ReferenceConstantPotential.h
+10
-10
platforms/reference/include/ReferencePME.h
platforms/reference/include/ReferencePME.h
+101
-85
platforms/reference/src/SimTKReference/ReferenceConstantPotential.cpp
...ference/src/SimTKReference/ReferenceConstantPotential.cpp
+21
-35
platforms/reference/src/SimTKReference/ReferenceLJCoulombIxn.cpp
...ms/reference/src/SimTKReference/ReferenceLJCoulombIxn.cpp
+4
-9
platforms/reference/src/SimTKReference/ReferencePME.cpp
platforms/reference/src/SimTKReference/ReferencePME.cpp
+261
-600
platforms/reference/tests/TestReferenceEwald.cpp
platforms/reference/tests/TestReferenceEwald.cpp
+7
-10
plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceHippoNonbondedForce.cpp
...src/SimTKReference/AmoebaReferenceHippoNonbondedForce.cpp
+2
-4
plugins/cpupme/tests/TestCpuPme.cpp
plugins/cpupme/tests/TestCpuPme.cpp
+2
-4
No files found.
platforms/cpu/src/CpuNonbondedForce.cpp
View file @
74912095
...
...
@@ -255,28 +255,26 @@ void CpuNonbondedForce::calculateReciprocalIxn(int numberOfAtoms, float* posq, c
float
recipCoeff
=
(
float
)(
ONE_4PI_EPS0
*
4
*
PI_M
/
(
periodicBoxVectors
[
0
][
0
]
*
periodicBoxVectors
[
1
][
1
]
*
periodicBoxVectors
[
2
][
2
])
/
epsilon
);
if
(
pme
)
{
pme_t
pmedata
;
pme_init
(
&
pmedata
,
alphaEwald
,
numberOfAtoms
,
meshDim
,
5
,
1
);
ReferencePME
pme
(
alphaEwald
,
numberOfAtoms
,
meshDim
,
5
,
1
);
vector
<
double
>
charges
(
numberOfAtoms
);
for
(
int
i
=
0
;
i
<
numberOfAtoms
;
i
++
)
charges
[
i
]
=
posq
[
4
*
i
+
3
];
double
recipEnergy
=
0.0
;
pme
_
exec
(
pmedata
,
atomCoordinates
,
forces
,
charges
,
periodicBoxVectors
,
&
recipEnergy
);
pme
.
exec
(
atomCoordinates
,
forces
,
charges
,
periodicBoxVectors
,
recipEnergy
);
if
(
totalEnergy
)
*
totalEnergy
+=
recipEnergy
;
pme_destroy
(
pmedata
);
if
(
ljpme
)
{
// Dispersion reciprocal space terms
pme_init
(
&
pmedata
,
alphaDispersionEwald
,
numberOfAtoms
,
dispersionMeshDim
,
5
,
1
);
ReferencePME
ljpme
(
alphaDispersionEwald
,
numberOfAtoms
,
dispersionMeshDim
,
5
,
1
);
std
::
vector
<
Vec3
>
dpmeforces
;
vector
<
Vec3
>
dpmeforces
;
for
(
int
i
=
0
;
i
<
numberOfAtoms
;
i
++
){
charges
[
i
]
=
C6params
[
i
];
dpmeforces
.
push_back
(
Vec3
());
}
double
recipDispersionEnergy
=
0.0
;
pme
_
exec_dpme
(
pmedata
,
atomCoordinates
,
dpmeforces
,
charges
,
periodicBoxVectors
,
&
recipDispersionEnergy
);
lj
pme
.
exec_dpme
(
atomCoordinates
,
dpmeforces
,
charges
,
periodicBoxVectors
,
recipDispersionEnergy
);
for
(
int
i
=
0
;
i
<
numberOfAtoms
;
i
++
){
forces
[
i
][
0
]
+=
dpmeforces
[
i
][
0
];
forces
[
i
][
1
]
+=
dpmeforces
[
i
][
1
];
...
...
@@ -284,8 +282,6 @@ void CpuNonbondedForce::calculateReciprocalIxn(int numberOfAtoms, float* posq, c
}
if
(
totalEnergy
)
*
totalEnergy
+=
recipDispersionEnergy
;
pme_destroy
(
pmedata
);
}
}
...
...
platforms/reference/include/ReferenceConstantPotential.h
View file @
74912095
...
...
@@ -90,14 +90,14 @@ public:
* @param elecToSys mapping from electrode particle indices to system particle indices
* @param electrodeParamArray electrode particle parameters
* @param conp constant potential derivative evaluation class
* @param pme
Data
reference PME solver
* @param pme
reference PME solver
*/
virtual
void
solve
(
int
numParticles
,
int
numElectrodeParticles
,
const
std
::
vector
<
Vec3
>&
posData
,
std
::
vector
<
double
>&
charges
,
const
std
::
vector
<
std
::
set
<
int
>>&
exclusions
,
const
std
::
vector
<
int
>&
sysToElec
,
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
ReferenceConstantPotential
&
conp
,
pme_t
pmeData
)
=
0
;
ReferenceConstantPotential
&
conp
,
ReferencePME
&
pme
)
=
0
;
};
/**
...
...
@@ -149,14 +149,14 @@ public:
* @param elecToSys mapping from electrode particle indices to system particle indices
* @param electrodeParamArray electrode particle parameters
* @param conp constant potential derivative evaluation class
* @param pme
Data
reference PME solver
* @param pme
reference PME solver
*/
void
solve
(
int
numParticles
,
int
numElectrodeParticles
,
const
std
::
vector
<
Vec3
>&
posData
,
std
::
vector
<
double
>&
charges
,
const
std
::
vector
<
std
::
set
<
int
>>&
exclusions
,
const
std
::
vector
<
int
>&
sysToElec
,
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
ReferenceConstantPotential
&
conp
,
pme_t
pmeData
);
ReferenceConstantPotential
&
conp
,
ReferencePME
&
pme
);
};
/**
...
...
@@ -208,14 +208,14 @@ public:
* @param elecToSys mapping from electrode particle indices to system particle indices
* @param electrodeParamArray electrode particle parameters
* @param conp constant potential derivative evaluation class
* @param pme
Data
reference PME solver
* @param pme
reference PME solver
*/
void
solve
(
int
numParticles
,
int
numElectrodeParticles
,
const
std
::
vector
<
Vec3
>&
posData
,
std
::
vector
<
double
>&
charges
,
const
std
::
vector
<
std
::
set
<
int
>>&
exclusions
,
const
std
::
vector
<
int
>&
sysToElec
,
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
ReferenceConstantPotential
&
conp
,
pme_t
pmeData
);
ReferenceConstantPotential
&
conp
,
ReferencePME
&
pme
);
};
/**
...
...
@@ -313,7 +313,7 @@ private:
* @param elecToSys mapping from electrode particle indices to system particle indices
* @param electrodeParamArray electrode particle parameters
* @param energy output system energy
* @param pme
Data
reference PME solver
* @param pme
reference PME solver
*/
void
getEnergyForces
(
int
numParticles
,
int
numElectrodeParticles
,
const
std
::
vector
<
Vec3
>&
posData
,
std
::
vector
<
Vec3
>&
forceData
,
...
...
@@ -321,7 +321,7 @@ private:
const
std
::
vector
<
std
::
set
<
int
>>&
exclusions
,
const
std
::
vector
<
int
>&
sysToElec
,
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
double
*
energy
,
pme_t
pmeData
);
double
*
energy
,
ReferencePME
&
pme
);
/**
* Computes energy derivatives with respect to charges.
*
...
...
@@ -334,14 +334,14 @@ private:
* @param elecToSys mapping from electrode particle indices to system particle indices
* @param electrodeParamArray electrode particle parameters
* @param chargeDerivatives output charge derivatives
* @param pme
Data
reference PME solver
* @param pme
reference PME solver
*/
void
getDerivatives
(
int
numParticles
,
int
numElectrodeParticles
,
const
std
::
vector
<
Vec3
>&
posData
,
std
::
vector
<
double
>&
charges
,
const
std
::
vector
<
std
::
set
<
int
>>&
exclusions
,
const
std
::
vector
<
int
>&
sysToElec
,
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
std
::
vector
<
double
>&
chargeDerivatives
,
pme_t
pmeData
);
std
::
vector
<
double
>&
chargeDerivatives
,
ReferencePME
&
pme
);
};
}
// namespace OpenMM
...
...
platforms/reference/include/ReferencePME.h
View file @
74912095
/*
* Reference implementation of PME reciprocal space interactions.
*
* Copyright (c) 2009
,
Erik Lindahl, Rossen Apostolov, Szilard Pall
* Copyright (c) 2009
-2025
Erik Lindahl, Rossen Apostolov, Szilard Pall
, Peter Eastman
* All rights reserved.
* Contact: lindahl@cbr.su.se Stockholm University, Sweden.
*
...
...
@@ -34,101 +34,117 @@
#include "openmm/Vec3.h"
#include "openmm/internal/windowsExport.h"
#include <array>
#include <complex>
#include <vector>
namespace
OpenMM
{
typedef
double
rvec
[
3
];
class
OPENMM_EXPORT
ReferencePME
{
public:
/*
* Initialize a PME calculation and set up data structures
*
* Arguments:
*
* ewaldcoeff Coefficient derived from the beta factor to participate
* direct/reciprocal space. See gromacs code for documentation!
* We assume that you are using nm units...
* natoms Number of atoms to set up data structure sof
* ngrid Size of the full pme grid
* pme_order Interpolation order, almost always 4
* epsilon_r Dielectric coefficient, typically 1.0.
*/
ReferencePME
(
double
ewaldcoeff
,
int
natoms
,
const
int
ngrid
[
3
],
int
pme_order
,
double
epsilon_r
);
/*
* Evaluate reciprocal space PME energy and forces.
*
* Args:
*
* atomCoordinates Pointer to coordinate data array (nm)
* forces Pointer to force data array (will be written as kJ/mol/nm)
* charges Array of charges (units of e)
* periodicBoxVectors Simulation cell dimensions (nm)
* energy Total energy (will be written in units of kJ/mol)
*/
void
exec
(
const
std
::
vector
<
OpenMM
::
Vec3
>&
atomCoordinates
,
std
::
vector
<
OpenMM
::
Vec3
>&
forces
,
const
std
::
vector
<
double
>&
charges
,
const
OpenMM
::
Vec3
periodicBoxVectors
[
3
],
double
&
energy
);
typedef
struct
pme
*
pme_t
;
/*
* Evaluate reciprocal space PME energy and charge derivatives.
*
* Args:
*
* atomCoordinates Pointer to coordinate data array (nm)
* chargeDerivatives Pointer to charge derivative data array (will be written as kJ/mol/e)
* chargeIndices Pointer to array of indices of particles to compute charge derivatives for
* charges Array of charges (units of e)
* periodicBoxVectors Simulation cell dimensions (nm)
*/
void
exec_charge_derivatives
(
const
std
::
vector
<
OpenMM
::
Vec3
>&
atomCoordinates
,
std
::
vector
<
double
>&
chargeDerivatives
,
const
std
::
vector
<
int
>&
chargeIndices
,
const
std
::
vector
<
double
>&
charges
,
const
OpenMM
::
Vec3
periodicBoxVectors
[
3
]);
/*
* Initialize a PME calculation and set up data structures
*
* Arguments:
*
* ppme Pointer to an opaque pme_t object
* ewaldcoeff Coefficient derived from the beta factor to participate
* direct/reciprocal space. See gromacs code for documentation!
* We assume that you are using nm units...
* natoms Number of atoms to set up data structure sof
* ngrid Size of the full pme grid
* pme_order Interpolation order, almost always 4
* epsilon_r Dielectric coefficient, typically 1.0.
*/
int
OPENMM_EXPORT
pme_init
(
pme_t
*
ppme
,
double
ewaldcoeff
,
int
natoms
,
const
int
ngrid
[
3
],
int
pme_order
,
double
epsilon_r
);
/**
* Evaluate reciprocal space PME dispersion energy and forces.
*
* Args:
*
* atomCoordinates Pointer to coordinate data array (nm)
* forces Pointer to force data array (will be written as kJ/mol/nm)
* c6s Array of c6 coefficients (units of sqrt(kJ/mol).nm^3 )
* periodicBoxVectors Simulation cell dimensions (nm)
* energy Total energy (will be written in units of kJ/mol)
*/
void
exec_dpme
(
const
std
::
vector
<
OpenMM
::
Vec3
>&
atomCoordinates
,
std
::
vector
<
OpenMM
::
Vec3
>&
forces
,
const
std
::
vector
<
double
>&
c6s
,
const
OpenMM
::
Vec3
periodicBoxVectors
[
3
],
double
&
energy
);
private:
void
calculate_bsplines_moduli
();
void
update_grid_index_and_fraction
(
const
std
::
vector
<
Vec3
>&
atomCoordinates
,
const
Vec3
recipBoxVectors
[
3
]);
void
update_bsplines
();
void
grid_spread_charge
(
const
std
::
vector
<
double
>&
charges
);
void
pme_reciprocal_convolution
(
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
],
double
&
energy
);
void
dpme_reciprocal_convolution
(
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
],
double
&
energy
);
void
grid_interpolate_force
(
const
Vec3
recipBoxVectors
[
3
],
const
std
::
vector
<
double
>&
charges
,
std
::
vector
<
Vec3
>&
forces
);
void
grid_interpolate_charge_derivatives
(
const
Vec3
recipBoxVectors
[
3
],
const
std
::
vector
<
double
>&
charges
,
std
::
vector
<
double
>&
chargeDerivatives
,
const
std
::
vector
<
int
>&
chargeIndices
);
int
natoms
;
double
ewaldcoeff
;
std
::
vector
<
std
::
complex
<
double
>
>
grid
;
/* Memory for the grid we spread charges on.
* Element (i,j,k) is accessed as:
* grid[i*ngrid[1]*ngrid[2] + j*ngrid[2] + k] */
int
ngrid
[
3
];
/* Total grid dimensions (all data is complex!) */
int
order
;
/* PME interpolation order. Almost always 4 */
/*
* Evaluate reciprocal space PME energy and forces.
*
* Args:
*
* pme Opaque pme_t object, must have been initialized with pme_init()
* atomCoordinates Pointer to coordinate data array (nm)
* forces Pointer to force data array (will be written as kJ/mol/nm)
* charges Array of charges (units of e)
* periodicBoxVectors Simulation cell dimensions (nm)
* energy Total energy (will be written in units of kJ/mol)
*/
int
OPENMM_EXPORT
pme_exec
(
pme_t
pme
,
const
std
::
vector
<
OpenMM
::
Vec3
>&
atomCoordinates
,
std
::
vector
<
OpenMM
::
Vec3
>&
forces
,
const
std
::
vector
<
double
>&
charges
,
const
OpenMM
::
Vec3
periodicBoxVectors
[
3
],
double
*
energy
);
/* Data for bspline interpolation, see the Essman PME paper */
std
::
vector
<
double
>
bsplines_moduli
[
3
];
/* 3 pointers, to x/y/z bspline moduli, each of length ngrid[x/y/z] */
std
::
vector
<
double
>
bsplines_theta
[
3
];
/* each of x/y/z has length order*natoms */
std
::
vector
<
double
>
bsplines_dtheta
[
3
];
/* each of x/y/z has length order*natoms */
/*
* Evaluate reciprocal space PME energy and charge derivatives.
*
* Args:
*
* pme Opaque pme_t object, must have been initialized with pme_init()
* atomCoordinates Pointer to coordinate data array (nm)
* chargeDerivatives Pointer to charge derivative data array (will be written as kJ/mol/e)
* chargeIndices Pointer to array of indices of particles to compute charge derivatives for
* charges Array of charges (units of e)
* periodicBoxVectors Simulation cell dimensions (nm)
*/
int
OPENMM_EXPORT
pme_exec_charge_derivatives
(
pme_t
pme
,
const
std
::
vector
<
OpenMM
::
Vec3
>&
atomCoordinates
,
std
::
vector
<
double
>&
chargeDerivatives
,
const
std
::
vector
<
int
>&
chargeIndices
,
const
std
::
vector
<
double
>&
charges
,
const
OpenMM
::
Vec3
periodicBoxVectors
[
3
]);
std
::
vector
<
std
::
array
<
int
,
3
>
>
particleindex
;
/* Array of length natoms. Each element is
* three ints that specify the grid
* indices for that particular atom. Updated every step! */
std
::
vector
<
Vec3
>
particlefraction
;
/* Array of length natoms. Fractional offset in the grid for
* each atom in all three dimensions. */
/**
* Evaluate reciprocal space PME dispersion energy and forces.
*
* Args:
*
* pme Opaque pme_t object, must have been initialized with pme_init()
* atomCoordinates Pointer to coordinate data array (nm)
* forces Pointer to force data array (will be written as kJ/mol/nm)
* c6s Array of c6 coefficients (units of sqrt(kJ/mol).nm^3 )
* periodicBoxVectors Simulation cell dimensions (nm)
* energy Total energy (will be written in units of kJ/mol)
*/
int
OPENMM_EXPORT
pme_exec_dpme
(
pme_t
pme
,
const
std
::
vector
<
OpenMM
::
Vec3
>&
atomCoordinates
,
std
::
vector
<
OpenMM
::
Vec3
>&
forces
,
const
std
::
vector
<
double
>&
c6s
,
const
OpenMM
::
Vec3
periodicBoxVectors
[
3
],
double
*
energy
);
/* Further explanation of index/fraction:
*
* Assume we have a cell of size 10*10*10nm, and a total grid dimension of 100*100*100 cells.
* In other words, each cell is 0.1*0.1*0.1 nm.
*
* If particle i has coordinates { 0.543 , 6.235 , -0.73 }, we will get:
*
* particleindex[i] = { 5 , 62 , 92 } (-0.73 + 10 = 9.27, we always apply PBC for grid calculations!)
* particlefraction[i] = { 0.43 , 0.35 , 0.7 } (this is the fraction of the cell length where the atom is)
*
* (The reason for precaculating / storing these is that it gets a bit more complex for triclinic cells :-)
*
* In the current code version we might assume that a particle is not more than a whole box length away from
* the central cell, i.e., in this case we would assume all coordinates fall in -10 nm < x,y,z < 20 nm.
*/
/* Release all memory in pme structure */
int
OPENMM_EXPORT
pme_destroy
(
pme_t
pme
);
double
epsilon_r
;
/* Dielectric coefficient to use, typically 1.0 */
};
}
// namespace OpenMM
...
...
platforms/reference/src/SimTKReference/ReferenceConstantPotential.cpp
View file @
74912095
...
...
@@ -29,7 +29,6 @@
#include "ReferenceConstantPotential.h"
#include "ReferenceForce.h"
#include "ReferencePME.h"
#include "SimTKOpenMMUtilities.h"
#include "openmm/OpenMMException.h"
#include "openmm/internal/MSVC_erfc.h"
...
...
@@ -97,22 +96,21 @@ void ReferenceConstantPotentialMatrixSolver::update(
std
::
vector
<
double
>
dUdQ0
(
numElectrodeParticles
);
std
::
vector
<
double
>
dUdQ
(
numElectrodeParticles
);
pme_t
pmeData
;
pme_init
(
&
pmeData
,
conp
.
ewaldAlpha
,
numParticles
,
conp
.
gridSize
,
5
,
1
);
ReferencePME
pme
(
conp
.
ewaldAlpha
,
numParticles
,
conp
.
gridSize
,
5
,
1
);
// Get derivatives when all electrode charges are zeroed.
std
::
vector
<
double
>
q
(
charges
);
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
q
[
elecToSys
[
ii
]]
=
0.0
;
}
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
q
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
dUdQ0
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
q
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
dUdQ0
,
pme
);
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
int
i
=
elecToSys
[
ii
];
// Get derivatives when one electrode charge is set.
q
[
i
]
=
1.0
;
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
q
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
dUdQ
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
q
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
dUdQ
,
pme
);
q
[
i
]
=
0.0
;
// Set matrix elements, subtracting zero charge derivatives so that the
...
...
@@ -123,8 +121,6 @@ void ReferenceConstantPotentialMatrixSolver::update(
A
[
ii
][
ii
]
=
dUdQ
[
ii
]
-
dUdQ0
[
ii
];
}
pme_destroy
(
pmeData
);
// Compute Cholesky decomposition representation of the inverse.
capacitance
=
JAMA
::
Cholesky
<
double
>
(
A
);
if
(
!
capacitance
.
is_spd
())
{
...
...
@@ -162,7 +158,7 @@ void ReferenceConstantPotentialMatrixSolver::solve(
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
ReferenceConstantPotential
&
conp
,
pme_t
pmeData
ReferencePME
&
pme
)
{
// Solves for charges using the matrix method.
...
...
@@ -171,7 +167,7 @@ void ReferenceConstantPotentialMatrixSolver::solve(
charges
[
elecToSys
[
ii
]]
=
0.0
;
}
std
::
vector
<
double
>
b
(
numElectrodeParticles
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
b
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
b
,
pme
);
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
b
[
ii
]
=
-
b
[
ii
];
}
...
...
@@ -236,14 +232,12 @@ void ReferenceConstantPotentialCGSolver::update(
// calculation. This will actually vary slightly with position but only due
// to finite accuracy of the PME splines, so it is fine to assume it will be
// constant for the preconditioner.
pme_t
pmeData
;
pme_init
(
&
pmeData
,
conp
.
ewaldAlpha
,
1
,
conp
.
gridSize
,
5
,
1
);
ReferencePME
pme
(
conp
.
ewaldAlpha
,
1
,
conp
.
gridSize
,
5
,
1
);
std
::
vector
<
Vec3
>
pmePosData
(
1
);
std
::
vector
<
double
>
pmeChargeDerivatives
(
1
);
std
::
vector
<
int
>
pmeElectrodeIndices
(
1
);
std
::
vector
<
double
>
pmeCharges
(
1
,
1.0
);
pme_exec_charge_derivatives
(
pmeData
,
pmePosData
,
pmeChargeDerivatives
,
pmeElectrodeIndices
,
pmeCharges
,
boxVectors
);
pme_destroy
(
pmeData
);
pme
.
exec_charge_derivatives
(
pmePosData
,
pmeChargeDerivatives
,
pmeElectrodeIndices
,
pmeCharges
,
boxVectors
);
// The diagonal has a contribution from reciprocal space, Ewald
// self-interaction, Ewald neutralizing plasma, Gaussian self-interaction,
...
...
@@ -274,7 +268,7 @@ void ReferenceConstantPotentialCGSolver::solve(
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
ReferenceConstantPotential
&
conp
,
pme_t
pmeData
ReferencePME
&
pme
)
{
// Solves for charges using the conjugate gradient method.
...
...
@@ -302,7 +296,7 @@ void ReferenceConstantPotentialCGSolver::solve(
}
// Evaluate the initial gradient Aq - b.
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
grad
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
grad
,
pme
);
// Project the initial gradient without preconditioning.
offset
=
0.0
;
...
...
@@ -332,7 +326,7 @@ void ReferenceConstantPotentialCGSolver::solve(
q
[
ii
]
=
charges
[
i
];
charges
[
i
]
=
0.0
;
}
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
grad0
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
grad0
,
pme
);
// Project the initial gradient with preconditioning.
if
(
precond
)
{
...
...
@@ -362,7 +356,7 @@ void ReferenceConstantPotentialCGSolver::solve(
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
charges
[
elecToSys
[
ii
]]
=
qStep
[
ii
];
}
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
gradStep
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
gradStep
,
pme
);
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
gradStep
[
ii
]
-=
grad0
[
ii
];
}
...
...
@@ -417,7 +411,7 @@ void ReferenceConstantPotentialCGSolver::solve(
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
charges
[
elecToSys
[
ii
]]
=
q
[
ii
];
}
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
grad
,
pme
Data
);
conp
.
getDerivatives
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
grad
,
pme
);
}
else
{
for
(
int
ii
=
0
;
ii
<
numElectrodeParticles
;
ii
++
)
{
...
...
@@ -542,13 +536,9 @@ void ReferenceConstantPotential::execute(
double
*
energy
,
ReferenceConstantPotentialSolver
*
solver
)
{
pme_t
pmeData
;
pme_init
(
&
pmeData
,
ewaldAlpha
,
numParticles
,
gridSize
,
5
,
1
);
solver
->
solve
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
*
this
,
pmeData
);
getEnergyForces
(
numParticles
,
numElectrodeParticles
,
posData
,
forceData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
energy
,
pmeData
);
pme_destroy
(
pmeData
);
ReferencePME
pme
(
ewaldAlpha
,
numParticles
,
gridSize
,
5
,
1
);
solver
->
solve
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
*
this
,
pme
);
getEnergyForces
(
numParticles
,
numElectrodeParticles
,
posData
,
forceData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
energy
,
pme
);
}
void
ReferenceConstantPotential
::
getCharges
(
...
...
@@ -562,12 +552,8 @@ void ReferenceConstantPotential::getCharges(
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
ReferenceConstantPotentialSolver
*
solver
)
{
pme_t
pmeData
;
pme_init
(
&
pmeData
,
ewaldAlpha
,
numParticles
,
gridSize
,
5
,
1
);
solver
->
solve
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
*
this
,
pmeData
);
pme_destroy
(
pmeData
);
ReferencePME
pme
(
ewaldAlpha
,
numParticles
,
gridSize
,
5
,
1
);
solver
->
solve
(
numParticles
,
numElectrodeParticles
,
posData
,
charges
,
exclusions
,
sysToElec
,
elecToSys
,
electrodeParamArray
,
*
this
,
pme
);
}
void
ReferenceConstantPotential
::
getEnergyForces
(
...
...
@@ -581,7 +567,7 @@ void ReferenceConstantPotential::getEnergyForces(
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
double
*
energy
,
pme_t
pmeData
ReferencePME
&
pme
)
{
const
double
SQRT_PI
=
sqrt
(
PI_M
);
const
double
TWO_OVER_SQRT_PI
=
2.0
/
SQRT_PI
;
...
...
@@ -671,7 +657,7 @@ void ReferenceConstantPotential::getEnergyForces(
// Reciprocal space.
double
pmeEnergy
=
0.0
;
pme
_
exec
(
pmeData
,
posData
,
forceData
,
charges
,
boxVectors
,
&
pmeEnergy
);
pme
.
exec
(
posData
,
forceData
,
charges
,
boxVectors
,
pmeEnergy
);
energyAccum
+=
pmeEnergy
;
// Ewald self-interaction and external field contributions (all particles).
...
...
@@ -710,7 +696,7 @@ void ReferenceConstantPotential::getDerivatives(
const
std
::
vector
<
int
>&
elecToSys
,
const
std
::
vector
<
std
::
array
<
double
,
3
>
>&
electrodeParamArray
,
std
::
vector
<
double
>&
chargeDerivatives
,
pme_t
pmeData
ReferencePME
&
pme
)
{
const
double
SQRT_PI
=
sqrt
(
PI_M
);
const
double
SELF_ALPHA_SCALE
=
2.0
*
ONE_4PI_EPS0
/
SQRT_PI
;
...
...
@@ -757,7 +743,7 @@ void ReferenceConstantPotential::getDerivatives(
}
// Reciprocal space.
pme
_
exec_charge_derivatives
(
pmeData
,
posData
,
chargeDerivatives
,
elecToSys
,
charges
,
boxVectors
);
pme
.
exec_charge_derivatives
(
posData
,
chargeDerivatives
,
elecToSys
,
charges
,
boxVectors
);
// Ewald neutralizing plasma precalculation.
double
qTotal
=
0.0
;
...
...
platforms/reference/src/SimTKReference/ReferenceLJCoulombIxn.cpp
View file @
74912095
...
...
@@ -243,33 +243,28 @@ void ReferenceLJCoulombIxn::calculateEwaldIxn(int numberOfAtoms, vector<Vec3>& a
// PME
if
(
pme
&&
includeReciprocal
)
{
pme_t
pmedata
;
/* abstract handle for PME data */
pme_init
(
&
pmedata
,
alphaEwald
,
numberOfAtoms
,
meshDim
,
5
,
1
);
ReferencePME
pme
(
alphaEwald
,
numberOfAtoms
,
meshDim
,
5
,
1
);
vector
<
double
>
charges
(
numberOfAtoms
);
for
(
int
i
=
0
;
i
<
numberOfAtoms
;
i
++
)
charges
[
i
]
=
atomParameters
[
i
][
QIndex
];
pme
_
exec
(
pmedata
,
atomCoordinates
,
forces
,
charges
,
periodicBoxVectors
,
&
recipEnergy
);
pme
.
exec
(
atomCoordinates
,
forces
,
charges
,
periodicBoxVectors
,
recipEnergy
);
if
(
totalEnergy
)
*
totalEnergy
+=
recipEnergy
;
pme_destroy
(
pmedata
);
if
(
ljpme
)
{
// Dispersion reciprocal space terms
pme_init
(
&
pmedata
,
alphaDispersionEwald
,
numberOfAtoms
,
dispersionMeshDim
,
5
,
1
);
ReferencePME
ljpme
(
alphaDispersionEwald
,
numberOfAtoms
,
dispersionMeshDim
,
5
,
1
);
std
::
vector
<
Vec3
>
dpmeforces
(
numberOfAtoms
);
for
(
int
i
=
0
;
i
<
numberOfAtoms
;
i
++
)
charges
[
i
]
=
8.0
*
pow
(
atomParameters
[
i
][
SigIndex
],
3.0
)
*
atomParameters
[
i
][
EpsIndex
];
pme
_
exec_dpme
(
pmedata
,
atomCoordinates
,
dpmeforces
,
charges
,
periodicBoxVectors
,
&
recipDispersionEnergy
);
lj
pme
.
exec_dpme
(
atomCoordinates
,
dpmeforces
,
charges
,
periodicBoxVectors
,
recipDispersionEnergy
);
for
(
int
i
=
0
;
i
<
numberOfAtoms
;
i
++
)
forces
[
i
]
+=
dpmeforces
[
i
];
if
(
totalEnergy
)
*
totalEnergy
+=
recipDispersionEnergy
;
pme_destroy
(
pmedata
);
}
}
// Ewald method
...
...
platforms/reference/src/SimTKReference/ReferencePME.cpp
View file @
74912095
...
...
@@ -29,10 +29,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <complex>
#include <cmath>
#include <vector>
#include "ReferencePME.h"
#include "ReferenceForce.h"
...
...
@@ -45,171 +43,72 @@
using
namespace
std
;
typedef
int
ivec
[
3
];
namespace
OpenMM
{
struct
pme
{
int
natoms
;
double
ewaldcoeff
;
complex
<
double
>*
grid
;
/* Memory for the grid we spread charges on.
* Element (i,j,k) is accessed as:
* grid[i*ngrid[1]*ngrid[2] + j*ngrid[2] + k]
*/
int
ngrid
[
3
];
/* Total grid dimensions (all data is complex!) */
int
order
;
/* PME interpolation order. Almost always 4 */
/* Data for bspline interpolation, see the Essman PME paper */
double
*
bsplines_moduli
[
3
];
/* 3 pointers, to x/y/z bspline moduli, each of length ngrid[x/y/z] */
double
*
bsplines_theta
[
3
];
/* each of x/y/z has length order*natoms */
double
*
bsplines_dtheta
[
3
];
/* each of x/y/z has length order*natoms */
ivec
*
particleindex
;
/* Array of length natoms. Each element is
* an ivec (3 ints) that specify the grid
* indices for that particular atom. Updated every step!
*/
rvec
*
particlefraction
;
/* Array of length natoms. Fractional offset in the grid for
* each atom in all three dimensions.
*/
/* Further explanation of index/fraction:
*
* Assume we have a cell of size 10*10*10nm, and a total grid dimension of 100*100*100 cells.
* In other words, each cell is 0.1*0.1*0.1 nm.
*
* If particle i has coordinates { 0.543 , 6.235 , -0.73 }, we will get:
*
* particleindex[i] = { 5 , 62 , 92 } (-0.73 + 10 = 9.27, we always apply PBC for grid calculations!)
* particlefraction[i] = { 0.43 , 0.35 , 0.7 } (this is the fraction of the cell length where the atom is)
*
* (The reason for precaculating / storing these is that it gets a bit more complex for triclinic cells :-)
*
* In the current code version we might assume that a particle is not more than a whole box length away from
* the central cell, i.e., in this case we would assume all coordinates fall in -10 nm < x,y,z < 20 nm.
*/
double
epsilon_r
;
/* Dielectric coefficient to use, typically 1.0 */
};
/* Internal setup routines */
/* Only called once from init_pme(), performance does not matter! */
static
void
pme_calculate_bsplines_moduli
(
pme_t
pme
)
{
int
nmax
;
int
i
,
j
,
k
,
l
,
d
;
int
order
;
int
ndata
;
double
*
data
;
double
*
ddata
;
double
*
bsplines_data
;
double
div
;
double
sc
,
ss
,
arg
;
nmax
=
0
;
for
(
d
=
0
;
d
<
3
;
d
++
)
{
nmax
=
(
pme
->
ngrid
[
d
]
>
nmax
)
?
pme
->
ngrid
[
d
]
:
nmax
;
pme
->
bsplines_moduli
[
d
]
=
(
double
*
)
malloc
(
sizeof
(
double
)
*
pme
->
ngrid
[
d
]);
/* Only called once from constructor, performance does not matter! */
void
ReferencePME
::
calculate_bsplines_moduli
()
{
int
nmax
=
0
;
for
(
int
d
=
0
;
d
<
3
;
d
++
)
{
nmax
=
(
ngrid
[
d
]
>
nmax
)
?
ngrid
[
d
]
:
nmax
;
bsplines_moduli
[
d
].
resize
(
ngrid
[
d
]);
}
order
=
pme
->
order
;
/* temp storage in this routine */
data
=
(
double
*
)
malloc
(
sizeof
(
double
)
*
order
);
ddata
=
(
double
*
)
malloc
(
sizeof
(
double
)
*
order
);
bsplines_data
=
(
double
*
)
malloc
(
sizeof
(
double
)
*
nmax
);
vector
<
double
>
data
(
order
);
vector
<
double
>
ddata
(
order
);
vector
<
double
>
bsplines_data
(
nmax
);
data
[
order
-
1
]
=
0
;
data
[
1
]
=
0
;
data
[
0
]
=
1
;
for
(
k
=
3
;
k
<
order
;
k
++
)
{
div
=
1.0
/
(
k
-
1.0
);
for
(
int
k
=
3
;
k
<
order
;
k
++
)
{
double
div
=
1.0
/
(
k
-
1.0
);
data
[
k
-
1
]
=
0
;
for
(
l
=
1
;
l
<
(
k
-
1
);
l
++
)
{
for
(
int
l
=
1
;
l
<
(
k
-
1
);
l
++
)
data
[
k
-
l
-
1
]
=
div
*
(
l
*
data
[
k
-
l
-
2
]
+
(
k
-
l
)
*
data
[
k
-
l
-
1
]);
}
data
[
0
]
=
div
*
data
[
0
];
}
/* differentiate */
ddata
[
0
]
=-
data
[
0
];
for
(
k
=
1
;
k
<
order
;
k
++
)
{
for
(
int
k
=
1
;
k
<
order
;
k
++
)
ddata
[
k
]
=
data
[
k
-
1
]
-
data
[
k
];
}
div
=
1.0
/
(
order
-
1
);
double
div
=
1.0
/
(
order
-
1
);
data
[
order
-
1
]
=
0
;
for
(
l
=
1
;
l
<
(
order
-
1
);
l
++
)
{
for
(
int
l
=
1
;
l
<
(
order
-
1
);
l
++
)
data
[
order
-
l
-
1
]
=
div
*
(
l
*
data
[
order
-
l
-
2
]
+
(
order
-
l
)
*
data
[
order
-
l
-
1
]);
}
data
[
0
]
=
div
*
data
[
0
];
for
(
i
=
0
;
i
<
nmax
;
i
++
)
{
for
(
int
i
=
0
;
i
<
nmax
;
i
++
)
bsplines_data
[
i
]
=
0
;
}
for
(
i
=
1
;
i
<=
order
;
i
++
)
{
for
(
int
i
=
1
;
i
<=
order
;
i
++
)
bsplines_data
[
i
]
=
data
[
i
-
1
];
}
/* Evaluate the actual bspline moduli for X/Y/Z */
for
(
d
=
0
;
d
<
3
;
d
++
)
{
ndata
=
pme
->
ngrid
[
d
];
for
(
i
=
0
;
i
<
ndata
;
i
++
)
{
sc
=
ss
=
0
;
for
(
j
=
0
;
j
<
ndata
;
j
++
)
{
arg
=
(
2.0
*
M_PI
*
i
*
j
)
/
ndata
;
for
(
int
d
=
0
;
d
<
3
;
d
++
)
{
int
ndata
=
ngrid
[
d
];
for
(
int
i
=
0
;
i
<
ndata
;
i
++
)
{
double
sc
=
0
;
double
ss
=
0
;
for
(
int
j
=
0
;
j
<
ndata
;
j
++
)
{
double
arg
=
(
2.0
*
M_PI
*
i
*
j
)
/
ndata
;
sc
+=
bsplines_data
[
j
]
*
cos
(
arg
);
ss
+=
bsplines_data
[
j
]
*
sin
(
arg
);
}
pme
->
bsplines_moduli
[
d
][
i
]
=
sc
*
sc
+
ss
*
ss
;
bsplines_moduli
[
d
][
i
]
=
sc
*
sc
+
ss
*
ss
;
}
for
(
i
=
0
;
i
<
ndata
;
i
++
)
{
if
(
pme
->
bsplines_moduli
[
d
][
i
]
<
1.0e-7
)
{
pme
->
bsplines_moduli
[
d
][
i
]
=
(
pme
->
bsplines_moduli
[
d
][(
i
-
1
+
ndata
)
%
ndata
]
+
pme
->
bsplines_moduli
[
d
][(
i
+
1
)
%
ndata
])
/
2
;
}
for
(
int
i
=
0
;
i
<
ndata
;
i
++
)
{
if
(
bsplines_moduli
[
d
][
i
]
<
1.0e-7
)
bsplines_moduli
[
d
][
i
]
=
(
bsplines_moduli
[
d
][(
i
-
1
+
ndata
)
%
ndata
]
+
bsplines_moduli
[
d
][(
i
+
1
)
%
ndata
])
/
2
;
}
}
/* Release temp storage */
free
(
data
);
free
(
ddata
);
free
(
bsplines_data
);
}
static
void
pme_update_grid_index_and_fraction
(
pme_t
pme
,
const
vector
<
Vec3
>&
atomCoordinates
,
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
])
{
int
i
;
int
d
;
double
t
;
int
ti
;
for
(
i
=
0
;
i
<
pme
->
natoms
;
i
++
)
{
void
ReferencePME
::
update_grid_index_and_fraction
(
const
vector
<
Vec3
>&
atomCoordinates
,
const
Vec3
recipBoxVectors
[
3
])
{
for
(
int
i
=
0
;
i
<
natoms
;
i
++
)
{
/* Index calculation (Look mom, no conditionals!):
*
* Both for Cuda and modern CPUs it is nice to avoid conditionals, but we still need to apply periodic boundary conditions.
...
...
@@ -247,14 +146,13 @@ pme_update_grid_index_and_fraction(pme_t pme,
* (And, by adding 100.0 box lengths, we would lose a bit of numerical accuracy here!)
*/
Vec3
coord
=
atomCoordinates
[
i
];
for
(
d
=
0
;
d
<
3
;
d
++
)
{
t
=
coord
[
0
]
*
recipBoxVectors
[
0
][
d
]
+
coord
[
1
]
*
recipBoxVectors
[
1
][
d
]
+
coord
[
2
]
*
recipBoxVectors
[
2
][
d
];
t
=
(
t
-
floor
(
t
))
*
pme
->
ngrid
[
d
];
ti
=
(
int
)
t
;
pme
->
particlefraction
[
i
][
d
]
=
t
-
ti
;
pme
->
particleindex
[
i
][
d
]
=
ti
%
pme
->
ngrid
[
d
];
for
(
int
d
=
0
;
d
<
3
;
d
++
)
{
double
t
=
coord
[
0
]
*
recipBoxVectors
[
0
][
d
]
+
coord
[
1
]
*
recipBoxVectors
[
1
][
d
]
+
coord
[
2
]
*
recipBoxVectors
[
2
][
d
];
t
=
(
t
-
floor
(
t
))
*
ngrid
[
d
];
int
ti
=
(
int
)
t
;
particlefraction
[
i
][
d
]
=
t
-
ti
;
particleindex
[
i
][
d
]
=
ti
%
ngrid
[
d
];
}
}
}
...
...
@@ -265,97 +163,60 @@ pme_update_grid_index_and_fraction(pme_t pme,
*
* In practice, it might help to require order=4 for the cuda port.
*/
static
void
pme_update_bsplines
(
pme_t
pme
)
{
int
i
,
j
,
k
,
l
;
int
order
;
double
dr
,
div
;
double
*
data
;
double
*
ddata
;
order
=
pme
->
order
;
for
(
i
=
0
;
(
i
<
pme
->
natoms
);
i
++
)
{
for
(
j
=
0
;
j
<
3
;
j
++
)
{
void
ReferencePME
::
update_bsplines
()
{
for
(
int
i
=
0
;
i
<
natoms
;
i
++
)
{
for
(
int
j
=
0
;
j
<
3
;
j
++
)
{
/* dr is relative offset from lower cell limit */
d
r
=
pme
->
particlefraction
[
i
][
j
];
d
ouble
dr
=
particlefraction
[
i
][
j
];
data
=
&
(
pme
->
bsplines_theta
[
j
][
i
*
order
]);
ddata
=
&
(
pme
->
bsplines_dtheta
[
j
][
i
*
order
]);
double
*
data
=
&
(
bsplines_theta
[
j
][
i
*
order
]);
double
*
ddata
=
&
(
bsplines_dtheta
[
j
][
i
*
order
]);
data
[
order
-
1
]
=
0
;
data
[
1
]
=
dr
;
data
[
0
]
=
1
-
dr
;
for
(
k
=
3
;
k
<
order
;
k
++
)
{
div
=
1.0
/
(
k
-
1.0
);
for
(
int
k
=
3
;
k
<
order
;
k
++
)
{
double
div
=
1.0
/
(
k
-
1.0
);
data
[
k
-
1
]
=
div
*
dr
*
data
[
k
-
2
];
for
(
l
=
1
;
l
<
(
k
-
1
);
l
++
)
{
for
(
int
l
=
1
;
l
<
(
k
-
1
);
l
++
)
data
[
k
-
l
-
1
]
=
div
*
((
dr
+
l
)
*
data
[
k
-
l
-
2
]
+
(
k
-
l
-
dr
)
*
data
[
k
-
l
-
1
]);
}
data
[
0
]
=
div
*
(
1
-
dr
)
*
data
[
0
];
}
/* differentiate */
ddata
[
0
]
=
-
data
[
0
];
for
(
k
=
1
;
k
<
order
;
k
++
)
{
for
(
int
k
=
1
;
k
<
order
;
k
++
)
ddata
[
k
]
=
data
[
k
-
1
]
-
data
[
k
];
}
d
iv
=
1.0
/
(
order
-
1
);
d
ouble
div
=
1.0
/
(
order
-
1
);
data
[
order
-
1
]
=
div
*
dr
*
data
[
order
-
2
];
for
(
l
=
1
;
l
<
(
order
-
1
);
l
++
)
{
for
(
int
l
=
1
;
l
<
(
order
-
1
);
l
++
)
data
[
order
-
l
-
1
]
=
div
*
((
dr
+
l
)
*
data
[
order
-
l
-
2
]
+
(
order
-
l
-
dr
)
*
data
[
order
-
l
-
1
]);
}
data
[
0
]
=
div
*
(
1
-
dr
)
*
data
[
0
];
}
}
}
static
void
pme_grid_spread_charge
(
pme_t
pme
,
const
vector
<
double
>&
charges
)
{
int
order
;
int
i
;
int
ix
,
iy
,
iz
;
int
x0index
,
y0index
,
z0index
;
int
xindex
,
yindex
,
zindex
;
int
index
;
double
q
;
double
*
thetax
;
double
*
thetay
;
double
*
thetaz
;
order
=
pme
->
order
;
void
ReferencePME
::
grid_spread_charge
(
const
vector
<
double
>&
charges
)
{
/* Reset the grid */
for
(
i
=
0
;
i
<
pme
->
ngrid
[
0
]
*
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
];
i
++
)
{
pme
->
grid
[
i
]
=
complex
<
double
>
(
0
,
0
);
}
for
(
int
i
=
0
;
i
<
ngrid
[
0
]
*
ngrid
[
1
]
*
ngrid
[
2
];
i
++
)
grid
[
i
]
=
complex
<
double
>
(
0
,
0
);
for
(
i
=
0
;
i
<
pme
->
natoms
;
i
++
)
{
q
=
charges
[
i
];
for
(
int
i
=
0
;
i
<
natoms
;
i
++
)
{
double
q
=
charges
[
i
];
/* Grid index for the actual atom position */
x0index
=
pme
->
particleindex
[
i
][
0
];
y0index
=
pme
->
particleindex
[
i
][
1
];
z0index
=
pme
->
particleindex
[
i
][
2
];
int
x0index
=
particleindex
[
i
][
0
];
int
y0index
=
particleindex
[
i
][
1
];
int
z0index
=
particleindex
[
i
][
2
];
/* Bspline factors for this atom in each dimension , calculated from fractional coordinates */
thetax
=
&
(
pme
->
bsplines_theta
[
0
][
i
*
order
]);
thetay
=
&
(
pme
->
bsplines_theta
[
1
][
i
*
order
]);
thetaz
=
&
(
pme
->
bsplines_theta
[
2
][
i
*
order
]);
double
*
thetax
=
&
(
bsplines_theta
[
0
][
i
*
order
]);
double
*
thetay
=
&
(
bsplines_theta
[
1
][
i
*
order
]);
double
*
thetaz
=
&
(
bsplines_theta
[
2
][
i
*
order
]);
/* Loop over norder*norder*norder (typically 5*5*5) neighbor cells.
*
...
...
@@ -375,23 +236,20 @@ pme_grid_spread_charge(pme_t pme, const vector<double>& charges)
* 3) When we parallelize things, we only need to communicate in one direction instead of two!
*/
for
(
ix
=
0
;
ix
<
order
;
ix
++
)
{
for
(
int
ix
=
0
;
ix
<
order
;
ix
++
)
{
/* Calculate index, apply PBC so we spread to index 0/1/2 when a particle is close to the upper limit of the grid */
xindex
=
(
x0index
+
ix
)
%
pme
->
ngrid
[
0
];
int
xindex
=
(
x0index
+
ix
)
%
ngrid
[
0
];
for
(
iy
=
0
;
iy
<
order
;
iy
++
)
{
yindex
=
(
y0index
+
iy
)
%
pme
->
ngrid
[
1
];
for
(
int
iy
=
0
;
iy
<
order
;
iy
++
)
{
int
yindex
=
(
y0index
+
iy
)
%
ngrid
[
1
];
for
(
iz
=
0
;
iz
<
order
;
iz
++
)
{
for
(
int
iz
=
0
;
iz
<
order
;
iz
++
)
{
/* Can be optimized, but we keep it simple here */
zindex
=
(
z0index
+
iz
)
%
pme
->
ngrid
[
2
];
int
zindex
=
(
z0index
+
iz
)
%
ngrid
[
2
];
/* Calculate index in the charge grid */
in
dex
=
xindex
*
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
]
+
yindex
*
pme
->
ngrid
[
2
]
+
zindex
;
in
t
index
=
xindex
*
ngrid
[
1
]
*
ngrid
[
2
]
+
yindex
*
ngrid
[
2
]
+
zindex
;
/* Add the charge times the bspline spread/interpolation factors to this grid position */
pme
->
grid
[
index
]
+=
q
*
thetax
[
ix
]
*
thetay
[
iy
]
*
thetaz
[
iz
];
grid
[
index
]
+=
q
*
thetax
[
ix
]
*
thetay
[
iy
]
*
thetaz
[
iz
];
}
}
}
...
...
@@ -400,67 +258,36 @@ pme_grid_spread_charge(pme_t pme, const vector<double>& charges)
static
void
pme_reciprocal_convolution
(
pme_t
pme
,
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
],
double
*
energy
)
{
int
kx
,
ky
,
kz
;
int
nx
,
ny
,
nz
;
double
mx
,
my
,
mz
;
double
mhx
,
mhy
,
mhz
,
m2
;
double
one_4pi_eps
;
double
virxx
,
virxy
,
virxz
,
viryy
,
viryz
,
virzz
;
double
bx
,
by
,
bz
;
double
d1
,
d2
;
double
eterm
,
vfactor
,
struct2
,
ets2
;
double
esum
;
double
factor
;
double
denom
;
double
boxfactor
;
double
maxkx
,
maxky
,
maxkz
;
complex
<
double
>
*
ptr
;
nx
=
pme
->
ngrid
[
0
];
ny
=
pme
->
ngrid
[
1
];
nz
=
pme
->
ngrid
[
2
];
one_4pi_eps
=
ONE_4PI_EPS0
/
pme
->
epsilon_r
;
factor
=
M_PI
*
M_PI
/
(
pme
->
ewaldcoeff
*
pme
->
ewaldcoeff
);
boxfactor
=
M_PI
*
periodicBoxVectors
[
0
][
0
]
*
periodicBoxVectors
[
1
][
1
]
*
periodicBoxVectors
[
2
][
2
];
esum
=
0
;
virxx
=
0
;
virxy
=
0
;
virxz
=
0
;
viryy
=
0
;
viryz
=
0
;
virzz
=
0
;
maxkx
=
(
nx
+
1
)
/
2
;
maxky
=
(
ny
+
1
)
/
2
;
maxkz
=
(
nz
+
1
)
/
2
;
for
(
kx
=
0
;
kx
<
nx
;
kx
++
)
{
void
ReferencePME
::
pme_reciprocal_convolution
(
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
],
double
&
energy
)
{
int
nx
=
ngrid
[
0
];
int
ny
=
ngrid
[
1
];
int
nz
=
ngrid
[
2
];
double
one_4pi_eps
=
ONE_4PI_EPS0
/
epsilon_r
;
double
factor
=
M_PI
*
M_PI
/
(
ewaldcoeff
*
ewaldcoeff
);
double
boxfactor
=
M_PI
*
periodicBoxVectors
[
0
][
0
]
*
periodicBoxVectors
[
1
][
1
]
*
periodicBoxVectors
[
2
][
2
];
double
esum
=
0
;
double
maxkx
=
(
nx
+
1
)
/
2
;
double
maxky
=
(
ny
+
1
)
/
2
;
double
maxkz
=
(
nz
+
1
)
/
2
;
for
(
int
kx
=
0
;
kx
<
nx
;
kx
++
)
{
/* Calculate frequency. Grid indices in the upper half correspond to negative frequencies! */
mx
=
(
kx
<
maxkx
)
?
kx
:
(
kx
-
nx
);
mhx
=
mx
*
recipBoxVectors
[
0
][
0
];
bx
=
boxfactor
*
pme
->
bsplines_moduli
[
0
][
kx
];
double
mx
=
(
kx
<
maxkx
)
?
kx
:
(
kx
-
nx
);
double
mhx
=
mx
*
recipBoxVectors
[
0
][
0
];
double
bx
=
boxfactor
*
bsplines_moduli
[
0
][
kx
];
for
(
ky
=
0
;
ky
<
ny
;
ky
++
)
{
for
(
int
ky
=
0
;
ky
<
ny
;
ky
++
)
{
/* Calculate frequency. Grid indices in the upper half correspond to negative frequencies! */
my
=
(
ky
<
maxky
)
?
ky
:
(
ky
-
ny
);
mhy
=
mx
*
recipBoxVectors
[
1
][
0
]
+
my
*
recipBoxVectors
[
1
][
1
];
by
=
pme
->
bsplines_moduli
[
1
][
ky
];
double
my
=
(
ky
<
maxky
)
?
ky
:
(
ky
-
ny
);
double
mhy
=
mx
*
recipBoxVectors
[
1
][
0
]
+
my
*
recipBoxVectors
[
1
][
1
];
double
by
=
bsplines_moduli
[
1
][
ky
];
for
(
kz
=
0
;
kz
<
nz
;
kz
++
)
{
for
(
int
kz
=
0
;
kz
<
nz
;
kz
++
)
{
/* Pointer to the grid cell in question */
ptr
=
pme
->
grid
+
kx
*
ny
*
nz
+
ky
*
nz
+
kz
;
complex
<
double
>&
ptr
=
grid
[
kx
*
ny
*
nz
+
ky
*
nz
+
kz
]
;
/* The zero frequency term is undefined due to division by the frequency below. Set this term to zero;
* in the case that the net charge of the system is non-zero, this is equivalent to applying a uniform
...
...
@@ -468,324 +295,234 @@ pme_reciprocal_convolution(pme_t pme,
* this neutralizing plasma is applied elsewhere. If this term is not zeroed, however, energies and
* forces will be unaffected but charge derivatives for non-neutral systems will be incorrect!
*/
if
(
kx
==
0
&&
ky
==
0
&&
kz
==
0
)
{
*
ptr
=
0
;
if
(
kx
==
0
&&
ky
==
0
&&
kz
==
0
)
{
ptr
=
0
;
continue
;
}
/* Calculate frequency. Grid indices in the upper half correspond to negative frequencies! */
mz
=
(
kz
<
maxkz
)
?
kz
:
(
kz
-
nz
);
mhz
=
mx
*
recipBoxVectors
[
2
][
0
]
+
my
*
recipBoxVectors
[
2
][
1
]
+
mz
*
recipBoxVectors
[
2
][
2
];
double
mz
=
(
kz
<
maxkz
)
?
kz
:
(
kz
-
nz
);
double
mhz
=
mx
*
recipBoxVectors
[
2
][
0
]
+
my
*
recipBoxVectors
[
2
][
1
]
+
mz
*
recipBoxVectors
[
2
][
2
];
/* Get grid data for this frequency */
d1
=
ptr
->
real
();
d2
=
ptr
->
imag
();
double
d1
=
ptr
.
real
();
double
d2
=
ptr
.
imag
();
/* Calculate the convolution - see the Essman/Darden paper for the equation! */
m2
=
mhx
*
mhx
+
mhy
*
mhy
+
mhz
*
mhz
;
bz
=
pme
->
bsplines_moduli
[
2
][
kz
];
denom
=
m2
*
bx
*
by
*
bz
;
double
m2
=
mhx
*
mhx
+
mhy
*
mhy
+
mhz
*
mhz
;
double
bz
=
bsplines_moduli
[
2
][
kz
];
double
denom
=
m2
*
bx
*
by
*
bz
;
eterm
=
one_4pi_eps
*
exp
(
-
factor
*
m2
)
/
denom
;
double
eterm
=
one_4pi_eps
*
exp
(
-
factor
*
m2
)
/
denom
;
/* write back convolution data to grid */
ptr
->
real
(
d1
*
eterm
);
ptr
->
imag
(
d2
*
eterm
);
ptr
.
real
(
d1
*
eterm
);
ptr
.
imag
(
d2
*
eterm
);
struct2
=
(
d1
*
d1
+
d2
*
d2
);
double
struct2
=
(
d1
*
d1
+
d2
*
d2
);
/* Long-range PME contribution to the energy for this frequency */
ets2
=
eterm
*
struct2
;
double
ets2
=
eterm
*
struct2
;
esum
+=
ets2
;
}
}
}
/* The factor 0.5 is nothing special, but it is better to have it here than inside the loop :-) */
*
energy
=
0.5
*
esum
;
energy
=
0.5
*
esum
;
}
static
void
dpme_reciprocal_convolution
(
pme_t
pme
,
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
],
double
*
energy
)
{
int
kx
,
ky
,
kz
;
int
nx
,
ny
,
nz
;
double
mx
,
my
,
mz
;
double
mhx
,
mhy
,
mhz
,
m2
;
double
bx
,
by
,
bz
;
double
d1
,
d2
;
double
eterm
,
struct2
,
ets2
;
double
esum
;
double
denom
;
double
boxfactor
;
double
maxkx
,
maxky
,
maxkz
;
complex
<
double
>
*
ptr
;
nx
=
pme
->
ngrid
[
0
];
ny
=
pme
->
ngrid
[
1
];
nz
=
pme
->
ngrid
[
2
];
void
ReferencePME
::
dpme_reciprocal_convolution
(
const
Vec3
periodicBoxVectors
[
3
],
const
Vec3
recipBoxVectors
[
3
],
double
&
energy
)
{
int
nx
=
ngrid
[
0
];
int
ny
=
ngrid
[
1
];
int
nz
=
ngrid
[
2
];
boxfactor
=
-
2
*
M_PI
*
sqrt
(
M_PI
)
/
(
6.0
*
periodicBoxVectors
[
0
][
0
]
*
periodicBoxVectors
[
1
][
1
]
*
periodicBoxVectors
[
2
][
2
]);
double
boxfactor
=
-
2
*
M_PI
*
sqrt
(
M_PI
)
/
(
6.0
*
periodicBoxVectors
[
0
][
0
]
*
periodicBoxVectors
[
1
][
1
]
*
periodicBoxVectors
[
2
][
2
]);
esum
=
0
;
double
esum
=
0
;
maxkx
=
(
nx
+
1
)
/
2
;
maxky
=
(
ny
+
1
)
/
2
;
maxkz
=
(
nz
+
1
)
/
2
;
double
maxkx
=
(
nx
+
1
)
/
2
;
double
maxky
=
(
ny
+
1
)
/
2
;
double
maxkz
=
(
nz
+
1
)
/
2
;
double
bfac
=
M_PI
/
pme
->
ewaldcoeff
;
double
bfac
=
M_PI
/
ewaldcoeff
;
double
fac1
=
2.0
*
M_PI
*
M_PI
*
M_PI
*
sqrt
(
M_PI
);
double
fac2
=
pme
->
ewaldcoeff
*
pme
->
ewaldcoeff
*
pme
->
ewaldcoeff
;
double
fac3
=
-
2.0
*
pme
->
ewaldcoeff
*
M_PI
*
M_PI
;
double
b
,
m
,
m3
,
expfac
,
expterm
,
erfcterm
;
double
fac2
=
ewaldcoeff
*
ewaldcoeff
*
ewaldcoeff
;
double
fac3
=
-
2.0
*
ewaldcoeff
*
M_PI
*
M_PI
;
for
(
kx
=
0
;
kx
<
nx
;
kx
++
)
{
for
(
int
kx
=
0
;
kx
<
nx
;
kx
++
)
{
/* Calculate frequency. Grid indices in the upper half correspond to negative frequencies! */
mx
=
((
kx
<
maxkx
)
?
kx
:
(
kx
-
nx
));
mhx
=
mx
*
recipBoxVectors
[
0
][
0
];
bx
=
pme
->
bsplines_moduli
[
0
][
kx
];
double
mx
=
((
kx
<
maxkx
)
?
kx
:
(
kx
-
nx
));
double
mhx
=
mx
*
recipBoxVectors
[
0
][
0
];
double
bx
=
bsplines_moduli
[
0
][
kx
];
for
(
ky
=
0
;
ky
<
ny
;
ky
++
)
{
for
(
int
ky
=
0
;
ky
<
ny
;
ky
++
)
{
/* Calculate frequency. Grid indices in the upper half correspond to negative frequencies! */
my
=
((
ky
<
maxky
)
?
ky
:
(
ky
-
ny
));
mhy
=
mx
*
recipBoxVectors
[
1
][
0
]
+
my
*
recipBoxVectors
[
1
][
1
];
by
=
pme
->
bsplines_moduli
[
1
][
ky
];
double
my
=
((
ky
<
maxky
)
?
ky
:
(
ky
-
ny
));
double
mhy
=
mx
*
recipBoxVectors
[
1
][
0
]
+
my
*
recipBoxVectors
[
1
][
1
];
double
by
=
bsplines_moduli
[
1
][
ky
];
for
(
kz
=
0
;
kz
<
nz
;
kz
++
)
{
for
(
int
kz
=
0
;
kz
<
nz
;
kz
++
)
{
/*
* Unlike the Coulombic case, there's an m=0 term so all terms are considered here.
*/
/* Calculate frequency. Grid indices in the upper half correspond to negative frequencies! */
mz
=
((
kz
<
maxkz
)
?
kz
:
(
kz
-
nz
));
mhz
=
mx
*
recipBoxVectors
[
2
][
0
]
+
my
*
recipBoxVectors
[
2
][
1
]
+
mz
*
recipBoxVectors
[
2
][
2
];
double
mz
=
((
kz
<
maxkz
)
?
kz
:
(
kz
-
nz
));
double
mhz
=
mx
*
recipBoxVectors
[
2
][
0
]
+
my
*
recipBoxVectors
[
2
][
1
]
+
mz
*
recipBoxVectors
[
2
][
2
];
/* Pointer to the grid cell in question */
ptr
=
pme
->
grid
+
kx
*
ny
*
nz
+
ky
*
nz
+
kz
;
complex
<
double
>&
ptr
=
grid
[
kx
*
ny
*
nz
+
ky
*
nz
+
kz
]
;
/* Get grid data for this frequency */
d
1
=
ptr
->
real
();
d
2
=
ptr
->
imag
();
d
ouble
d1
=
ptr
.
real
();
d
ouble
d2
=
ptr
.
imag
();
/* Calculate the convolution - see the Essman/Darden paper for the equation! */
m2
=
mhx
*
mhx
+
mhy
*
mhy
+
mhz
*
mhz
;
bz
=
pme
->
bsplines_moduli
[
2
][
kz
];
denom
=
boxfactor
/
(
bx
*
by
*
bz
);
double
m2
=
mhx
*
mhx
+
mhy
*
mhy
+
mhz
*
mhz
;
double
bz
=
bsplines_moduli
[
2
][
kz
];
d
ouble
d
enom
=
boxfactor
/
(
bx
*
by
*
bz
);
m
=
sqrt
(
m2
);
m3
=
m
*
m2
;
b
=
bfac
*
m
;
expfac
=
-
b
*
b
;
erfcterm
=
erfc
(
b
);
expterm
=
exp
(
expfac
);
double
m
=
sqrt
(
m2
);
double
m3
=
m
*
m2
;
double
b
=
bfac
*
m
;
double
expfac
=
-
b
*
b
;
double
erfcterm
=
erfc
(
b
);
double
expterm
=
exp
(
expfac
);
eterm
=
(
fac1
*
erfcterm
*
m3
+
expterm
*
(
fac2
+
fac3
*
m2
))
*
denom
;
double
eterm
=
(
fac1
*
erfcterm
*
m3
+
expterm
*
(
fac2
+
fac3
*
m2
))
*
denom
;
/* write back convolution data to grid */
ptr
->
real
(
d1
*
eterm
);
ptr
->
imag
(
d2
*
eterm
);
ptr
.
real
(
d1
*
eterm
);
ptr
.
imag
(
d2
*
eterm
);
struct2
=
(
d1
*
d1
+
d2
*
d2
);
double
struct2
=
(
d1
*
d1
+
d2
*
d2
);
/* Long-range PME contribution to the energy for this frequency */
ets2
=
eterm
*
struct2
;
esum
+=
ets2
;
double
ets2
=
eterm
*
struct2
;
esum
+=
ets2
;
}
}
}
// Remember the C6 energy is attractive, hence the negative sign.
*
energy
=
0.5
*
esum
;
energy
=
0.5
*
esum
;
}
static
void
pme_grid_interpolate_force
(
pme_t
pme
,
const
Vec3
recipBoxVectors
[
3
],
const
vector
<
double
>&
charges
,
vector
<
Vec3
>&
forces
)
{
int
i
;
int
ix
,
iy
,
iz
;
int
x0index
,
y0index
,
z0index
;
int
xindex
,
yindex
,
zindex
;
int
index
;
int
order
;
double
q
;
double
*
thetax
;
double
*
thetay
;
double
*
thetaz
;
double
*
dthetax
;
double
*
dthetay
;
double
*
dthetaz
;
double
tx
,
ty
,
tz
;
double
dtx
,
dty
,
dtz
;
double
fx
,
fy
,
fz
;
double
gridvalue
;
int
nx
,
ny
,
nz
;
nx
=
pme
->
ngrid
[
0
];
ny
=
pme
->
ngrid
[
1
];
nz
=
pme
->
ngrid
[
2
];
order
=
pme
->
order
;
void
ReferencePME
::
grid_interpolate_force
(
const
Vec3
recipBoxVectors
[
3
],
const
vector
<
double
>&
charges
,
vector
<
Vec3
>&
forces
)
{
/* This is almost identical to the charge spreading routine! */
for
(
i
=
0
;
i
<
pme
->
natoms
;
i
++
)
{
fx
=
fy
=
fz
=
0
;
for
(
int
i
=
0
;
i
<
natoms
;
i
++
)
{
double
fx
=
0
,
fy
=
0
,
fz
=
0
;
q
=
charges
[
i
];
double
q
=
charges
[
i
];
/* Grid index for the actual atom position */
x0index
=
pme
->
particleindex
[
i
][
0
];
y0index
=
pme
->
particleindex
[
i
][
1
];
z0index
=
pme
->
particleindex
[
i
][
2
];
int
x0index
=
particleindex
[
i
][
0
];
int
y0index
=
particleindex
[
i
][
1
];
int
z0index
=
particleindex
[
i
][
2
];
/* Bspline factors for this atom in each dimension , calculated from fractional coordinates */
thetax
=
&
(
pme
->
bsplines_theta
[
0
][
i
*
order
]);
thetay
=
&
(
pme
->
bsplines_theta
[
1
][
i
*
order
]);
thetaz
=
&
(
pme
->
bsplines_theta
[
2
][
i
*
order
]);
dthetax
=
&
(
pme
->
bsplines_dtheta
[
0
][
i
*
order
]);
dthetay
=
&
(
pme
->
bsplines_dtheta
[
1
][
i
*
order
]);
dthetaz
=
&
(
pme
->
bsplines_dtheta
[
2
][
i
*
order
]);
double
*
thetax
=
&
(
bsplines_theta
[
0
][
i
*
order
]);
double
*
thetay
=
&
(
bsplines_theta
[
1
][
i
*
order
]);
double
*
thetaz
=
&
(
bsplines_theta
[
2
][
i
*
order
]);
double
*
dthetax
=
&
(
bsplines_dtheta
[
0
][
i
*
order
]);
double
*
dthetay
=
&
(
bsplines_dtheta
[
1
][
i
*
order
]);
double
*
dthetaz
=
&
(
bsplines_dtheta
[
2
][
i
*
order
]);
/* See
pme_
grid_spread_charge() for comments about the order here, and only interpolation in one direction */
/* See grid_spread_charge() for comments about the order here, and only interpolation in one direction */
/* Since we will add order^3 (typically 5*5*5=125) terms to the force on each particle, we use temporary fx/fy/fz
* variables, and only add it to memory forces[] at the end.
*/
for
(
ix
=
0
;
ix
<
order
;
ix
++
)
{
xindex
=
(
x0index
+
ix
)
%
pme
->
ngrid
[
0
];
for
(
int
ix
=
0
;
ix
<
order
;
ix
++
)
{
int
xindex
=
(
x0index
+
ix
)
%
ngrid
[
0
];
/* Get both the bspline factor and its derivative with respect to the x coordinate! */
tx
=
thetax
[
ix
];
d
tx
=
dthetax
[
ix
];
double
tx
=
thetax
[
ix
];
d
ouble
dtx
=
dthetax
[
ix
];
for
(
iy
=
0
;
iy
<
order
;
iy
++
)
{
yindex
=
(
y0index
+
iy
)
%
pme
->
ngrid
[
1
];
for
(
int
iy
=
0
;
iy
<
order
;
iy
++
)
{
int
yindex
=
(
y0index
+
iy
)
%
ngrid
[
1
];
/* bspline + derivative wrt y */
ty
=
thetay
[
iy
];
d
ty
=
dthetay
[
iy
];
double
ty
=
thetay
[
iy
];
d
ouble
dty
=
dthetay
[
iy
];
for
(
iz
=
0
;
iz
<
order
;
iz
++
)
{
for
(
int
iz
=
0
;
iz
<
order
;
iz
++
)
{
/* Can be optimized, but we keep it simple here */
zindex
=
(
z0index
+
iz
)
%
pme
->
ngrid
[
2
];
int
zindex
=
(
z0index
+
iz
)
%
ngrid
[
2
];
/* bspline + derivative wrt z */
tz
=
thetaz
[
iz
];
d
tz
=
dthetaz
[
iz
];
index
=
xindex
*
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
]
+
yindex
*
pme
->
ngrid
[
2
]
+
zindex
;
double
tz
=
thetaz
[
iz
];
d
ouble
dtz
=
dthetaz
[
iz
];
int
index
=
xindex
*
ngrid
[
1
]
*
ngrid
[
2
]
+
yindex
*
ngrid
[
2
]
+
zindex
;
/* Get the fft+convoluted+ifft:d data from the grid, which must be real by definition */
/* Checking that the imaginary part is indeed zero might be a good check :-) */
gridvalue
=
pme
->
grid
[
index
].
real
();
double
gridvalue
=
grid
[
index
].
real
();
/* The d component of the force is calculated by taking the derived bspline in dimension d, normal bsplines in the other two */
fx
+=
dtx
*
ty
*
tz
*
gridvalue
;
fy
+=
tx
*
dty
*
tz
*
gridvalue
;
fz
+=
tx
*
ty
*
dtz
*
gridvalue
;
fx
+=
dtx
*
ty
*
tz
*
gridvalue
;
fy
+=
tx
*
dty
*
tz
*
gridvalue
;
fz
+=
tx
*
ty
*
dtz
*
gridvalue
;
}
}
}
/* Update memory force, note that we multiply by charge and some box stuff */
forces
[
i
][
0
]
-=
q
*
(
fx
*
n
x
*
recipBoxVectors
[
0
][
0
]);
forces
[
i
][
1
]
-=
q
*
(
fx
*
n
x
*
recipBoxVectors
[
1
][
0
]
+
fy
*
n
y
*
recipBoxVectors
[
1
][
1
]);
forces
[
i
][
2
]
-=
q
*
(
fx
*
n
x
*
recipBoxVectors
[
2
][
0
]
+
fy
*
n
y
*
recipBoxVectors
[
2
][
1
]
+
fz
*
n
z
*
recipBoxVectors
[
2
][
2
]);
forces
[
i
][
0
]
-=
q
*
(
fx
*
n
grid
[
0
]
*
recipBoxVectors
[
0
][
0
]);
forces
[
i
][
1
]
-=
q
*
(
fx
*
n
grid
[
0
]
*
recipBoxVectors
[
1
][
0
]
+
fy
*
n
grid
[
1
]
*
recipBoxVectors
[
1
][
1
]);
forces
[
i
][
2
]
-=
q
*
(
fx
*
n
grid
[
0
]
*
recipBoxVectors
[
2
][
0
]
+
fy
*
n
grid
[
1
]
*
recipBoxVectors
[
2
][
1
]
+
fz
*
n
grid
[
2
]
*
recipBoxVectors
[
2
][
2
]);
}
}
static
void
pme_grid_interpolate_charge_derivatives
(
pme_t
pme
,
const
Vec3
recipBoxVectors
[
3
],
const
vector
<
double
>&
charges
,
vector
<
double
>&
chargeDerivatives
,
const
vector
<
int
>&
chargeIndices
)
{
int
nderiv
,
ideriv
,
i
;
int
ix
,
iy
,
iz
;
int
x0index
,
y0index
,
z0index
;
int
xindex
,
yindex
,
zindex
;
int
index
;
int
order
;
double
q
;
double
*
thetax
;
double
*
thetay
;
double
*
thetaz
;
double
tx
,
ty
,
tz
;
double
dq
;
double
gridvalue
;
int
nx
,
ny
,
nz
;
nx
=
pme
->
ngrid
[
0
];
ny
=
pme
->
ngrid
[
1
];
nz
=
pme
->
ngrid
[
2
];
order
=
pme
->
order
;
/* This is similar to pme_grid_interpolate_force() */
void
ReferencePME
::
grid_interpolate_charge_derivatives
(
const
Vec3
recipBoxVectors
[
3
],
const
vector
<
double
>&
charges
,
vector
<
double
>&
chargeDerivatives
,
const
vector
<
int
>&
chargeIndices
)
{
/* This is similar to grid_interpolate_force() */
nderiv
=
chargeIndices
.
size
();
for
(
ideriv
=
0
;
ideriv
<
nderiv
;
ideriv
++
)
{
i
=
chargeIndices
[
ideriv
];
dq
=
0
;
q
=
charges
[
i
];
int
nderiv
=
chargeIndices
.
size
();
for
(
int
ideriv
=
0
;
ideriv
<
nderiv
;
ideriv
++
)
{
int
i
=
chargeIndices
[
ideriv
];
double
dq
=
0
;
/* Grid index for the actual atom position */
x0index
=
pme
->
particleindex
[
i
][
0
];
y0index
=
pme
->
particleindex
[
i
][
1
];
z0index
=
pme
->
particleindex
[
i
][
2
];
int
x0index
=
particleindex
[
i
][
0
];
int
y0index
=
particleindex
[
i
][
1
];
int
z0index
=
particleindex
[
i
][
2
];
/* Bspline factors for this atom in each dimension , calculated from fractional coordinates */
thetax
=
&
(
pme
->
bsplines_theta
[
0
][
i
*
order
]);
thetay
=
&
(
pme
->
bsplines_theta
[
1
][
i
*
order
]);
thetaz
=
&
(
pme
->
bsplines_theta
[
2
][
i
*
order
]);
double
*
thetax
=
&
(
bsplines_theta
[
0
][
i
*
order
]);
double
*
thetay
=
&
(
bsplines_theta
[
1
][
i
*
order
]);
double
*
thetaz
=
&
(
bsplines_theta
[
2
][
i
*
order
]);
/* See
pme_
grid_spread_charge() for comments about the order here, and only interpolation in one direction */
/* See grid_spread_charge() for comments about the order here, and only interpolation in one direction */
/* Since we will add order^3 (typically 5*5*5=125) terms to the charge
* derivative on each particle, we use a temporary dq variable, and only
* add it to memory forces[] at the end.
*/
for
(
ix
=
0
;
ix
<
order
;
ix
++
)
{
xindex
=
(
x0index
+
ix
)
%
pme
->
ngrid
[
0
];
for
(
int
ix
=
0
;
ix
<
order
;
ix
++
)
{
int
xindex
=
(
x0index
+
ix
)
%
ngrid
[
0
];
/* Get the bspline factor with respect to the x coordinate */
tx
=
thetax
[
ix
];
double
tx
=
thetax
[
ix
];
for
(
iy
=
0
;
iy
<
order
;
iy
++
)
{
yindex
=
(
y0index
+
iy
)
%
pme
->
ngrid
[
1
];
for
(
int
iy
=
0
;
iy
<
order
;
iy
++
)
{
int
yindex
=
(
y0index
+
iy
)
%
ngrid
[
1
];
/* bspline wrt y */
ty
=
thetay
[
iy
];
double
ty
=
thetay
[
iy
];
for
(
iz
=
0
;
iz
<
order
;
iz
++
)
{
for
(
int
iz
=
0
;
iz
<
order
;
iz
++
)
{
/* Can be optimized, but we keep it simple here */
zindex
=
(
z0index
+
iz
)
%
pme
->
ngrid
[
2
];
int
zindex
=
(
z0index
+
iz
)
%
ngrid
[
2
];
/* bspline wrt z */
tz
=
thetaz
[
iz
];
index
=
xindex
*
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
]
+
yindex
*
pme
->
ngrid
[
2
]
+
zindex
;
double
tz
=
thetaz
[
iz
];
in
t
in
dex
=
xindex
*
ngrid
[
1
]
*
ngrid
[
2
]
+
yindex
*
ngrid
[
2
]
+
zindex
;
/* Get the fft+convoluted+ifft:d data from the grid, which must be real by definition */
/* Checking that the imaginary part is indeed zero might be a good check :-) */
gridvalue
=
pme
->
grid
[
index
].
real
();
double
gridvalue
=
grid
[
index
].
real
();
/* The d component of the force is calculated by taking the derived bspline in dimension d, normal bsplines in the other two */
dq
+=
tx
*
ty
*
tz
*
gridvalue
;
dq
+=
tx
*
ty
*
tz
*
gridvalue
;
}
}
}
...
...
@@ -794,222 +531,146 @@ pme_grid_interpolate_charge_derivatives(pme_t pme,
}
}
/* EXPORTED ROUTINES */
int
pme_init
(
pme_t
*
ppme
,
double
ewaldcoeff
,
int
natoms
,
const
int
ngrid
[
3
],
int
pme_order
,
double
epsilon_r
)
{
pme_t
pme
;
int
d
;
pme
=
(
pme_t
)
malloc
(
sizeof
(
struct
pme
));
pme
->
order
=
pme_order
;
pme
->
epsilon_r
=
epsilon_r
;
pme
->
ewaldcoeff
=
ewaldcoeff
;
pme
->
natoms
=
natoms
;
for
(
d
=
0
;
d
<
3
;
d
++
)
{
pme
->
ngrid
[
d
]
=
ngrid
[
d
];
pme
->
bsplines_theta
[
d
]
=
(
double
*
)
malloc
(
sizeof
(
double
)
*
pme_order
*
natoms
);
pme
->
bsplines_dtheta
[
d
]
=
(
double
*
)
malloc
(
sizeof
(
double
)
*
pme_order
*
natoms
);
ReferencePME
::
ReferencePME
(
double
ewaldcoeff
,
int
natoms
,
const
int
ngrid
[
3
],
int
pme_order
,
double
epsilon_r
)
:
order
(
pme_order
),
epsilon_r
(
epsilon_r
),
ewaldcoeff
(
ewaldcoeff
),
natoms
(
natoms
)
{
for
(
int
d
=
0
;
d
<
3
;
d
++
)
{
this
->
ngrid
[
d
]
=
ngrid
[
d
];
bsplines_theta
[
d
].
resize
(
pme_order
*
natoms
);
bsplines_dtheta
[
d
].
resize
(
pme_order
*
natoms
);
}
pme
->
particlefraction
=
(
rvec
*
)
malloc
(
sizeof
(
rvec
)
*
natoms
);
pme
->
particleindex
=
(
ivec
*
)
malloc
(
sizeof
(
ivec
)
*
natoms
);
particlefraction
.
resize
(
natoms
);
particleindex
.
resize
(
natoms
);
/* Allocate charge grid storage */
pme
->
grid
=
(
complex
<
double
>
*
)
malloc
(
sizeof
(
complex
<
double
>
)
*
ngrid
[
0
]
*
ngrid
[
1
]
*
ngrid
[
2
]);
grid
.
resize
(
ngrid
[
0
]
*
ngrid
[
1
]
*
ngrid
[
2
]);
/* Setup bspline moduli (see Essman paper) */
pme_calculate_bsplines_moduli
(
pme
);
*
ppme
=
pme
;
return
0
;
calculate_bsplines_moduli
();
}
int
pme_exec
(
pme_t
pme
,
const
vector
<
Vec3
>&
atomCoordinates
,
vector
<
Vec3
>&
forces
,
const
vector
<
double
>&
charges
,
const
Vec3
periodicBoxVectors
[
3
],
double
*
energy
)
{
void
ReferencePME
::
exec
(
const
vector
<
Vec3
>&
atomCoordinates
,
vector
<
Vec3
>&
forces
,
const
vector
<
double
>&
charges
,
const
Vec3
periodicBoxVectors
[
3
],
double
&
energy
)
{
/* Routine is called with coordinates in x, a box, and charges in q */
Vec3
recipBoxVectors
[
3
];
ReferenceForce
::
invertBoxVectors
(
periodicBoxVectors
,
recipBoxVectors
);
/* Before we can do the actual interpolation, we need to recalculate and update
* the indices for each particle in the charge grid (initialized in
pme_init()
),
* the indices for each particle in the charge grid (initialized in
constructor
),
* and what its fractional offset in this grid cell is.
*/
/* Update charge grid indices and fractional offsets for each atom.
* The indices/fractions are stored internally in the pme datatype
*/
pme_
update_grid_index_and_fraction
(
pme
,
atomCoordinates
,
periodicBoxVectors
,
recipBoxVectors
);
update_grid_index_and_fraction
(
atomCoordinates
,
recipBoxVectors
);
/* Calculate bsplines (and their differentials) from current fractional coordinates, store in pme structure */
pme_
update_bsplines
(
pme
);
update_bsplines
();
/* Spread the charges on grid (using newly calculated bsplines in the pme structure) */
pme_
grid_spread_charge
(
pme
,
charges
);
grid_spread_charge
(
charges
);
/* do 3d-fft */
vector
<
size_t
>
shape
=
{(
size_t
)
pme
->
ngrid
[
0
],
(
size_t
)
pme
->
ngrid
[
1
],
(
size_t
)
pme
->
ngrid
[
2
]};
vector
<
size_t
>
shape
=
{(
size_t
)
ngrid
[
0
],
(
size_t
)
ngrid
[
1
],
(
size_t
)
ngrid
[
2
]};
vector
<
size_t
>
axes
=
{
0
,
1
,
2
};
vector
<
ptrdiff_t
>
stride
=
{(
ptrdiff_t
)
(
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
(
pme
->
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
vector
<
ptrdiff_t
>
stride
=
{(
ptrdiff_t
)
(
ngrid
[
1
]
*
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
(
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
sizeof
(
complex
<
double
>
)};
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
true
,
pme
->
grid
,
pme
->
grid
,
1.0
,
0
);
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
true
,
grid
.
data
(),
grid
.
data
()
,
1.0
,
0
);
/* solve in k-space */
pme_reciprocal_convolution
(
pme
,
periodicBoxVectors
,
recipBoxVectors
,
energy
);
pme_reciprocal_convolution
(
periodicBoxVectors
,
recipBoxVectors
,
energy
);
/* do 3d-invfft */
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
false
,
pme
->
grid
,
pme
->
grid
,
1.0
,
0
);
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
false
,
grid
.
data
(),
grid
.
data
()
,
1.0
,
0
);
/* Get the particle forces from the grid and bsplines in the pme structure */
pme_grid_interpolate_force
(
pme
,
recipBoxVectors
,
charges
,
forces
);
return
0
;
grid_interpolate_force
(
recipBoxVectors
,
charges
,
forces
);
}
int
pme_exec_charge_derivatives
(
pme_t
pme
,
const
vector
<
Vec3
>&
atomCoordinates
,
vector
<
double
>&
chargeDerivatives
,
const
vector
<
int
>&
chargeIndices
,
const
vector
<
double
>&
charges
,
const
Vec3
periodicBoxVectors
[
3
])
{
void
ReferencePME
::
exec_charge_derivatives
(
const
vector
<
Vec3
>&
atomCoordinates
,
vector
<
double
>&
chargeDerivatives
,
const
vector
<
int
>&
chargeIndices
,
const
vector
<
double
>&
charges
,
const
Vec3
periodicBoxVectors
[
3
])
{
/* Routine is called with coordinates in x, a box, and charges in q */
Vec3
recipBoxVectors
[
3
];
ReferenceForce
::
invertBoxVectors
(
periodicBoxVectors
,
recipBoxVectors
);
/* Before we can do the actual interpolation, we need to recalculate and update
* the indices for each particle in the charge grid (initialized in
pme_init()
),
* the indices for each particle in the charge grid (initialized in
constructor
),
* and what its fractional offset in this grid cell is.
*/
/* Update charge grid indices and fractional offsets for each atom.
* The indices/fractions are stored internally in the pme datatype
*/
pme_
update_grid_index_and_fraction
(
pme
,
atomCoordinates
,
periodicBoxVectors
,
recipBoxVectors
);
update_grid_index_and_fraction
(
atomCoordinates
,
recipBoxVectors
);
/* Calculate bsplines (and their differentials) from current fractional coordinates, store in pme structure */
pme_
update_bsplines
(
pme
);
update_bsplines
();
/* Spread the charges on grid (using newly calculated bsplines in the pme structure) */
pme_
grid_spread_charge
(
pme
,
charges
);
grid_spread_charge
(
charges
);
/* do 3d-fft */
vector
<
size_t
>
shape
=
{(
size_t
)
pme
->
ngrid
[
0
],
(
size_t
)
pme
->
ngrid
[
1
],
(
size_t
)
pme
->
ngrid
[
2
]};
vector
<
size_t
>
shape
=
{(
size_t
)
ngrid
[
0
],
(
size_t
)
ngrid
[
1
],
(
size_t
)
ngrid
[
2
]};
vector
<
size_t
>
axes
=
{
0
,
1
,
2
};
vector
<
ptrdiff_t
>
stride
=
{(
ptrdiff_t
)
(
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
(
pme
->
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
vector
<
ptrdiff_t
>
stride
=
{(
ptrdiff_t
)
(
ngrid
[
1
]
*
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
(
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
sizeof
(
complex
<
double
>
)};
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
true
,
pme
->
grid
,
pme
->
grid
,
1.0
,
0
);
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
true
,
grid
.
data
(),
grid
.
data
()
,
1.0
,
0
);
/* solve in k-space */
double
energy
;
pme_reciprocal_convolution
(
pme
,
periodicBoxVectors
,
recipBoxVectors
,
&
energy
);
pme_reciprocal_convolution
(
periodicBoxVectors
,
recipBoxVectors
,
energy
);
/* do 3d-invfft */
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
false
,
pme
->
grid
,
pme
->
grid
,
1.0
,
0
);
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
false
,
grid
.
data
(),
grid
.
data
()
,
1.0
,
0
);
/* Get the charge derivatives from the grid and bsplines in the pme structure */
pme_grid_interpolate_charge_derivatives
(
pme
,
recipBoxVectors
,
charges
,
chargeDerivatives
,
chargeIndices
);
return
0
;
grid_interpolate_charge_derivatives
(
recipBoxVectors
,
charges
,
chargeDerivatives
,
chargeIndices
);
}
int
pme_exec_dpme
(
pme_t
pme
,
const
vector
<
Vec3
>&
atomCoordinates
,
vector
<
Vec3
>&
forces
,
const
vector
<
double
>&
c6s
,
const
Vec3
periodicBoxVectors
[
3
],
double
*
energy
)
{
void
ReferencePME
::
exec_dpme
(
const
vector
<
Vec3
>&
atomCoordinates
,
vector
<
Vec3
>&
forces
,
const
vector
<
double
>&
c6s
,
const
Vec3
periodicBoxVectors
[
3
],
double
&
energy
)
{
/* Routine is called with coordinates in x, a box, and charges in q */
Vec3
recipBoxVectors
[
3
];
ReferenceForce
::
invertBoxVectors
(
periodicBoxVectors
,
recipBoxVectors
);
/* Before we can do the actual interpolation, we need to recalculate and update
* the indices for each particle in the charge grid (initialized in
pme_init()
),
* the indices for each particle in the charge grid (initialized in
constructor
),
* and what its fractional offset in this grid cell is.
*/
/* Update charge grid indices and fractional offsets for each atom.
* The indices/fractions are stored internally in the pme datatype
*/
pme_
update_grid_index_and_fraction
(
pme
,
atomCoordinates
,
periodicBoxVectors
,
recipBoxVectors
);
update_grid_index_and_fraction
(
atomCoordinates
,
recipBoxVectors
);
/* Calculate bsplines (and their differentials) from current fractional coordinates, store in pme structure */
pme_
update_bsplines
(
pme
);
update_bsplines
();
/* Spread the charges on grid (using newly calculated bsplines in the pme structure) */
pme_
grid_spread_charge
(
pme
,
c6s
);
grid_spread_charge
(
c6s
);
/* do 3d-fft */
vector
<
size_t
>
shape
=
{(
size_t
)
pme
->
ngrid
[
0
],
(
size_t
)
pme
->
ngrid
[
1
],
(
size_t
)
pme
->
ngrid
[
2
]};
vector
<
size_t
>
shape
=
{(
size_t
)
ngrid
[
0
],
(
size_t
)
ngrid
[
1
],
(
size_t
)
ngrid
[
2
]};
vector
<
size_t
>
axes
=
{
0
,
1
,
2
};
vector
<
ptrdiff_t
>
stride
=
{(
ptrdiff_t
)
(
pme
->
ngrid
[
1
]
*
pme
->
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
(
pme
->
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
vector
<
ptrdiff_t
>
stride
=
{(
ptrdiff_t
)
(
ngrid
[
1
]
*
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
(
ngrid
[
2
]
*
sizeof
(
complex
<
double
>
)),
(
ptrdiff_t
)
sizeof
(
complex
<
double
>
)};
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
true
,
pme
->
grid
,
pme
->
grid
,
1.0
,
0
);
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
true
,
grid
.
data
(),
grid
.
data
()
,
1.0
,
0
);
/* solve in k-space */
dpme_reciprocal_convolution
(
pme
,
periodicBoxVectors
,
recipBoxVectors
,
energy
);
dpme_reciprocal_convolution
(
periodicBoxVectors
,
recipBoxVectors
,
energy
);
/* do 3d-invfft */
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
false
,
pme
->
grid
,
pme
->
grid
,
1.0
,
0
);
pocketfft
::
c2c
(
shape
,
stride
,
stride
,
axes
,
false
,
grid
.
data
(),
grid
.
data
()
,
1.0
,
0
);
/* Get the particle forces from the grid and bsplines in the pme structure */
pme_grid_interpolate_force
(
pme
,
recipBoxVectors
,
c6s
,
forces
);
return
0
;
}
int
pme_destroy
(
pme_t
pme
)
{
int
d
;
free
(
pme
->
grid
);
for
(
d
=
0
;
d
<
3
;
d
++
)
{
free
(
pme
->
bsplines_moduli
[
d
]);
free
(
pme
->
bsplines_theta
[
d
]);
free
(
pme
->
bsplines_dtheta
[
d
]);
}
free
(
pme
->
particlefraction
);
free
(
pme
->
particleindex
);
/* destroy structure itself */
free
(
pme
);
return
0
;
grid_interpolate_force
(
recipBoxVectors
,
c6s
,
forces
);
}
}
// namespace OpenMM
platforms/reference/tests/TestReferenceEwald.cpp
View file @
74912095
...
...
@@ -59,14 +59,13 @@ void testReferencePmeDerivatives() {
double
ewaldAlpha
=
1.752
;
int
gridSize
[
3
]
=
{
54
,
49
,
43
};
pme_t
pme
;
pme_init
(
&
pme
,
ewaldAlpha
,
numParticles
,
gridSize
,
5
,
1
);
ReferencePME
pme
(
ewaldAlpha
,
numParticles
,
gridSize
,
5
,
1
);
double
dummyEnergy
=
0
;
vector
<
Vec3
>
dummyForces
(
numParticles
);
vector
<
Vec3
>
testForces
(
numParticles
);
pme
_
exec
(
pme
,
positions
,
testForces
,
charges
,
boxVectors
,
&
dummyEnergy
);
pme
.
exec
(
positions
,
testForces
,
charges
,
boxVectors
,
dummyEnergy
);
for
(
int
i
=
0
;
i
<
numParticles
;
i
++
)
{
for
(
int
j
=
0
;
j
<
3
;
j
++
)
{
...
...
@@ -74,11 +73,11 @@ void testReferencePmeDerivatives() {
double
energyLess
=
0.0
;
positions
[
i
][
j
]
=
referencePosition
-
DELTA
;
pme
_
exec
(
pme
,
positions
,
dummyForces
,
charges
,
boxVectors
,
&
energyLess
);
pme
.
exec
(
positions
,
dummyForces
,
charges
,
boxVectors
,
energyLess
);
double
energyMore
=
0.0
;
positions
[
i
][
j
]
=
referencePosition
+
DELTA
;
pme
_
exec
(
pme
,
positions
,
dummyForces
,
charges
,
boxVectors
,
&
energyMore
);
pme
.
exec
(
positions
,
dummyForces
,
charges
,
boxVectors
,
energyMore
);
positions
[
i
][
j
]
=
referencePosition
;
...
...
@@ -87,25 +86,23 @@ void testReferencePmeDerivatives() {
}
vector
<
double
>
testDerivatives
(
numParticles
);
pme
_
exec_charge_derivatives
(
pme
,
positions
,
testDerivatives
,
indices
,
charges
,
boxVectors
);
pme
.
exec_charge_derivatives
(
positions
,
testDerivatives
,
indices
,
charges
,
boxVectors
);
for
(
int
i
=
0
;
i
<
numParticles
;
i
++
)
{
double
referenceCharge
=
charges
[
i
];
double
energyLess
=
0.0
;
charges
[
i
]
=
referenceCharge
-
DELTA
;
pme
_
exec
(
pme
,
positions
,
dummyForces
,
charges
,
boxVectors
,
&
energyLess
);
pme
.
exec
(
positions
,
dummyForces
,
charges
,
boxVectors
,
energyLess
);
double
energyMore
=
0.0
;
charges
[
i
]
=
referenceCharge
+
DELTA
;
pme
_
exec
(
pme
,
positions
,
dummyForces
,
charges
,
boxVectors
,
&
energyMore
);
pme
.
exec
(
positions
,
dummyForces
,
charges
,
boxVectors
,
energyMore
);
charges
[
i
]
=
referenceCharge
;
ASSERT_EQUAL_TOL
((
energyMore
-
energyLess
)
/
(
2
*
DELTA
),
testDerivatives
[
i
],
EPSILON
);
}
pme_destroy
(
pme
);
}
void
runPlatformTests
()
{
...
...
plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceHippoNonbondedForce.cpp
View file @
74912095
...
...
@@ -2873,8 +2873,7 @@ double AmoebaReferencePmeHippoNonbondedForce::calculateDispersionPairIxn(const M
}
double
AmoebaReferencePmeHippoNonbondedForce
::
computeReciprocalSpaceDispersionForceAndEnergy
(
const
vector
<
MultipoleParticleData
>&
particleData
,
vector
<
Vec3
>&
forces
)
const
{
pme_t
pmedata
;
pme_init
(
&
pmedata
,
_dalphaEwald
,
_numParticles
,
_dpmeGridDimensions
,
5
,
1
);
ReferencePME
pme
(
_dalphaEwald
,
_numParticles
,
_dpmeGridDimensions
,
5
,
1
);
vector
<
double
>
charges
(
_numParticles
);
vector
<
Vec3
>
dpmeforces
(
_numParticles
,
Vec3
()),
coords
;
for
(
int
i
=
0
;
i
<
_numParticles
;
i
++
)
{
...
...
@@ -2882,8 +2881,7 @@ double AmoebaReferencePmeHippoNonbondedForce::computeReciprocalSpaceDispersionFo
coords
.
push_back
(
particleData
[
i
].
position
);
}
double
recipDispersionEnergy
;
pme_exec_dpme
(
pmedata
,
coords
,
dpmeforces
,
charges
,
_periodicBoxVectors
,
&
recipDispersionEnergy
);
pme_destroy
(
pmedata
);
pme
.
exec_dpme
(
coords
,
dpmeforces
,
charges
,
_periodicBoxVectors
,
recipDispersionEnergy
);
for
(
int
i
=
0
;
i
<
_numParticles
;
i
++
)
forces
[
i
]
+=
dpmeforces
[
i
];
return
recipDispersionEnergy
;
...
...
plugins/cpupme/tests/TestCpuPme.cpp
View file @
74912095
...
...
@@ -650,12 +650,10 @@ void testPME(bool triclinic, bool nonNeutral) {
// Get charge derivatives from the reference PME implementation.
pme_t
referencePme
;
int
gridSize
[
3
]
=
{
gridx
,
gridy
,
gridz
};
pme_init
(
&
referencePme
,
alpha
,
numParticles
,
gridSize
,
5
,
1
);
ReferencePME
referencePme
(
alpha
,
numParticles
,
gridSize
,
5
,
1
);
vector
<
double
>
testDerivatives
(
numParticles
);
pme_exec_charge_derivatives
(
referencePme
,
positions
,
testDerivatives
,
testIndices
,
testCharges
,
boxVectors
);
pme_destroy
(
referencePme
);
referencePme
.
exec_charge_derivatives
(
positions
,
testDerivatives
,
testIndices
,
testCharges
,
boxVectors
);
// See if they match.
for
(
int
i
=
0
;
i
<
numParticles
;
i
++
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment