Demonstrates kinetic Cauchy stress for an ideal gas with nonzero bulk velocity. The test verifies that MDStressLab subtracts the continuum velocity before forming the kinetic stress and writes the Cauchy-only momentum and mass density fields. It compares MethodSphere, MethodLdadConstant, and MethodLdadTrigonometric to the instantaneous ideal-gas pressure and to the expected mean mass density.
14void validateVoxelGridFile(
const std::string& filename,
15 const std::string& expectedGridCellsHeader,
16 const int expectedNumberOfGridPoints,
17 const int expectedDimension)
19 std::ifstream file(filename);
20 if (!file)
MY_ERROR(
"ERROR: " + filename +
" could not be opened for reading.");
23 bool foundGridSize=
false;
24 bool foundGridCells=
false;
25 bool foundDimension=
false;
27 while (std::getline(file,line))
29 if (line ==
"ITEM: DIMENSION")
32 if (!(file >> dimension))
33 MY_ERROR(
"ERROR: Could not read voxel grid dimension from " + filename);
34 file.ignore(32767,
'\n');
35 if (dimension != expectedDimension)
36 MY_ERROR(
"ERROR: Voxel grid dimension does not match expected dimension in " + filename);
39 else if (line ==
"ITEM: GRID SIZE nx ny nz")
42 if (!(file >> nx >> ny >> nz))
43 MY_ERROR(
"ERROR: Could not read voxel grid dimensions from " + filename);
44 file.ignore(32767,
'\n');
45 if (nx*ny*nz != expectedNumberOfGridPoints)
46 MY_ERROR(
"ERROR: Voxel grid dimensions do not match expected grid size in " + filename);
49 else if (line == expectedGridCellsHeader)
57 MY_ERROR(
"ERROR: Missing voxel grid dimension header in " + filename);
59 MY_ERROR(
"ERROR: Missing voxel grid size header in " + filename);
61 MY_ERROR(
"ERROR: Missing expected voxel grid cells header in " + filename);
63 while (std::getline(file,line))
67 if (dataLineCount != expectedNumberOfGridPoints)
68 MY_ERROR(
"ERROR: Voxel grid data line count does not match grid size in " + filename);
71template<
typename TMethod>
75 const double& expectedMassDensity,
76 const double& meanStressTolerance,
77 const double& pointwiseStressTolerance,
78 const double& densityTolerance)
80 Matrix3d meanStress= Matrix3d::Zero();
81 Matrix3d maxAbsDeviation= Matrix3d::Zero();
82 double meanMassDensity= 0.0;
84 for (
int i_grid=0; i_grid<kineticStress.
field.size(); ++i_grid)
86 const auto& stress= kineticStress.
field[i_grid];
88 maxAbsDeviation= maxAbsDeviation.cwiseMax((stress-expectedStress).cwiseAbs());
92 MY_ERROR(
"Mass density should be nonnegative.");
98 if ((kineticStress.
velocityField[i_grid]-expectedVelocity).norm() > 1e-12)
99 MY_ERROR(
"Continuum velocity does not equal momentum density divided by mass density.");
104 MY_ERROR(
"Continuum velocity should be zero where mass density is zero.");
108 const double numberOfGridPoints=
static_cast<double>(kineticStress.
field.size());
109 meanStress/= numberOfGridPoints;
110 meanMassDensity/= numberOfGridPoints;
112 if ((rawVelocityStress-expectedStress).cwiseAbs().maxCoeff() < 10.0*meanStressTolerance)
113 MY_ERROR(
"Bulk velocity is too small to distinguish raw-velocity stress from relative-velocity stress.");
115 if (std::abs(meanMassDensity-expectedMassDensity) > densityTolerance)
117 std::cout <<
"Expected mass density = " << expectedMassDensity <<
" amu/A^3" << std::endl;
118 std::cout <<
"Mean computed mass density = " << meanMassDensity <<
" amu/A^3" << std::endl;
119 MY_ERROR(
"Ideal gas mass density mean does not match the normalized-kernel value.");
122 if ((meanStress-expectedStress).cwiseAbs().maxCoeff() > meanStressTolerance)
124 std::cout <<
"Expected stress:\n" << expectedStress << std::endl;
125 std::cout <<
"Mean computed stress:\n" << meanStress << std::endl;
126 MY_ERROR(
"Ideal gas kinetic stress mean does not match the instantaneous ideal gas value.");
129 if (maxAbsDeviation.diagonal().maxCoeff() > pointwiseStressTolerance)
131 std::cout <<
"Expected stress:\n" << expectedStress << std::endl;
132 std::cout <<
"Maximum pointwise absolute deviation:\n" << maxAbsDeviation << std::endl;
133 MY_ERROR(
"Ideal gas kinetic stress field is too far from the expected uniform field.");
136 std::cout << kineticStress.
name <<
" mean mass density = "
137 << meanMassDensity <<
" amu/A^3" << std::endl;
138 std::cout << kineticStress.
name <<
" mean computed stress:\n" << meanStress << std::endl;
144 const std::string configFileName=
"idealGas.lmp";
145 std::ifstream file(configFileName);
146 if(!file)
MY_ERROR(
"ERROR: idealGas.lmp could not be opened for reading.");
148 int numberOfParticles= 0;
150 while (std::getline(file,line))
152 std::string loweredLine= line;
153 std::transform(loweredLine.begin(),loweredLine.end(),loweredLine.begin(),::tolower);
154 if (loweredLine.find(
"atoms") != std::string::npos && (std::stringstream(line) >> numberOfParticles))
157 if (numberOfParticles <= 0)
MY_ERROR(
"ERROR: Could not read number of particles.");
163 const double volume= body.box.determinant();
164 const double boltzmannConstantEvPerK= 8.617333262145e-5;
165 double totalMass= 0.0;
166 Vector3d totalMomentum= Vector3d::Zero();
167 for (
int i=0; i<body.numberOfParticles; ++i)
169 Vector3d velocity= body.velocities.row(i);
170 totalMass+= body.masses(i);
171 totalMomentum+= body.masses(i)*velocity;
173 Vector3d averageVelocity= totalMomentum/totalMass;
175 Matrix3d expectedStress= Matrix3d::Zero();
176 Matrix3d rawVelocityStress= Matrix3d::Zero();
177 double thermalKineticEnergyFactor= 0.0;
178 for (
int i=0; i<body.numberOfParticles; ++i)
180 Vector3d velocity= body.velocities.row(i);
181 Vector3d relativeVelocity= velocity - averageVelocity;
183 body.masses(i)*relativeVelocity.transpose()*relativeVelocity/volume;
185 body.masses(i)*velocity.transpose()*velocity/volume;
186 thermalKineticEnergyFactor+= body.masses(i)*relativeVelocity.squaredNorm();
188 const double instantaneousTemperature=
190 (3.0*body.numberOfParticles*boltzmannConstantEvPerK);
195 const Vector3d lowerLimit(0.0,0.0,0.0);
196 const Vector3d upperLimit(60.0,60.0,60.0);
200 ldadVectors << 20.0, 0.0, 0.0,
211 kineticStressLdadConstant,
212 kineticStressLdadTrigonometric));
213 kineticStressSphere.write();
214 kineticStressLdadConstant.write();
215 kineticStressLdadTrigonometric.write();
216 kineticStressSphere.write_voxel_grid(nx,ny,nz,lowerLimit,upperLimit);
217 kineticStressLdadConstant.write_voxel_grid(nx,ny,nz,lowerLimit,upperLimit);
218 kineticStressLdadTrigonometric.write_voxel_grid(nx,ny,nz,lowerLimit,upperLimit);
220 const int numberOfGridPoints= nx*ny*nz;
221 const int voxelGridDimension= 3;
222 validateVoxelGridFile(
"idealGasKineticSphere.voxel_grid_stress",
223 "ITEM: GRID CELLS SXX SYY SZZ SYZ SXZ SXY",
226 validateVoxelGridFile(
"idealGasKineticSphere.voxel_grid_momentum_density",
227 "ITEM: GRID CELLS PX PY PZ",
230 validateVoxelGridFile(
"idealGasKineticSphere.voxel_grid_mass_density",
231 "ITEM: GRID CELLS RHO",
234 validateVoxelGridFile(
"idealGasKineticLdadConstant.voxel_grid_stress",
235 "ITEM: GRID CELLS SXX SYY SZZ SYZ SXZ SXY",
238 validateVoxelGridFile(
"idealGasKineticLdadConstant.voxel_grid_momentum_density",
239 "ITEM: GRID CELLS PX PY PZ",
242 validateVoxelGridFile(
"idealGasKineticLdadConstant.voxel_grid_mass_density",
243 "ITEM: GRID CELLS RHO",
246 validateVoxelGridFile(
"idealGasKineticLdadTrigonometric.voxel_grid_stress",
247 "ITEM: GRID CELLS SXX SYY SZZ SYZ SXZ SXY",
250 validateVoxelGridFile(
"idealGasKineticLdadTrigonometric.voxel_grid_momentum_density",
251 "ITEM: GRID CELLS PX PY PZ",
254 validateVoxelGridFile(
"idealGasKineticLdadTrigonometric.voxel_grid_mass_density",
255 "ITEM: GRID CELLS RHO",
259 const double pressure= -expectedStress.trace()/3.0;
260 const double analyticalPressure=
261 body.numberOfParticles*boltzmannConstantEvPerK*instantaneousTemperature/volume;
262 const double expectedMassDensity= totalMass/volume;
263 const double meanTolerance= 0.20*pressure;
264 const double pointwiseTolerance= 0.75*pressure;
265 const double densityTolerance= 0.20*expectedMassDensity;
267 validateIdealGasStress(kineticStressSphere,expectedStress,rawVelocityStress,
268 expectedMassDensity,meanTolerance,pointwiseTolerance,densityTolerance);
269 validateIdealGasStress(kineticStressLdadConstant,expectedStress,rawVelocityStress,
270 expectedMassDensity,meanTolerance,pointwiseTolerance,densityTolerance);
271 validateIdealGasStress(kineticStressLdadTrigonometric,expectedStress,rawVelocityStress,
272 expectedMassDensity,meanTolerance,pointwiseTolerance,densityTolerance);
274 std::cout <<
"Mass-weighted average velocity = " << averageVelocity <<
" A/ps" << std::endl;
275 std::cout <<
"Instantaneous ideal-gas temperature = " << instantaneousTemperature <<
" K" << std::endl;
276 std::cout <<
"Instantaneous ideal-gas pressure from stress = " << pressure <<
" eV/A^3" << std::endl;
277 std::cout <<
"Analytical ideal-gas pressure NkBT/V = " << analyticalPressure <<
" eV/A^3" << std::endl;
278 std::cout <<
"Raw-velocity stress before subtracting bulk motion:\n" << rawVelocityStress << std::endl;
279 std::cout <<
"Expected stress:\n" << expectedStress << std::endl;
int calculateKineticStress(const BoxConfiguration &body, std::tuple<> cauchyStress)
Represents a particle configuration including simulation box information.
void readLMP(const std::string &, const ConfigType &configType)
Reads a configuration from a LAMMPS data file.
Lattice-dependent averaging-domain method.
Implements radially symmetric weighting functions (Hardy, Virial) and its associated bond function fo...
Three-dimensional stress field on a grid.
std::vector< Matrix3d > field
A three-dimensional stress field.
std::string name
The prefix of the filename that will be outputted when the stress field is written.
std::vector< Vector3d > momentumDensityField
Cauchy-grid momentum density .
std::vector< Vector3d > velocityField
Internal Cauchy-grid continuum velocity.
std::vector< double > massDensityField
Cauchy-grid mass density .
const double amuAngstromSquaredPerPicosecondSquaredToEv
#define MY_ERROR(message)
Eigen::Matrix< double, DIM, DIM, Eigen::RowMajor > Matrix3d
Eigen::Matrix< double, 1, DIM, Eigen::RowMajor > Vector3d
Eigen::Matrix< int, 1, DIM, Eigen::RowMajor > Vector3i