OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
Hxy.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48/* Calculates the undulation spectrum of the lipid membrance. */
49
50#include "applications/staticProps/Hxy.hpp"
51
52#include <algorithm>
53#include <cmath>
54#include <cstdio>
55#include <cstdlib>
56#include <cstring>
57#include <fstream>
58
59#include "io/DumpReader.hpp"
61#include "types/LennardJonesAdapter.hpp"
62#include "utils/Accumulator.hpp"
63#include "utils/AccumulatorView.hpp"
64#include "utils/BaseAccumulator.hpp"
65#include "utils/Utility.hpp"
66#include "utils/simError.h"
67
68using namespace OpenMD::Utils;
69
70namespace OpenMD {
71
72 Hxy::Hxy(SimInfo* info, const std::string& filename, const std::string& sele,
73 int nbins_x, int nbins_y, int nbins_z, int nrbins) :
74 StaticAnalyser(info, filename, nrbins),
75 selectionScript_(sele), evaluator_(info), seleMan_(info),
76 nBinsX_(nbins_x), nBinsY_(nbins_y), nBinsZ_(nbins_z) {
77 evaluator_.loadScriptString(sele);
78 if (!evaluator_.isDynamic()) {
79 seleMan_.setSelectionSet(evaluator_.evaluate());
80 }
81
82 // dens_ stores the local density, rho(x,y,z) on a 3-D grid
83 dens_.resize(nBinsX_);
84 // bin stores the upper and lower surface cutoff locations (z) for
85 // a column through grid location x,y
86 minHeight_.resize(nBinsX_);
87 maxHeight_.resize(nBinsX_);
88
89 for (unsigned int i = 0; i < nBinsX_; i++) {
90 dens_[i].resize(nBinsY_);
91 minHeight_[i].resize(nBinsY_);
92 maxHeight_[i].resize(nBinsY_);
93 for (unsigned int j = 0; j < nBinsY_; j++) {
94 dens_[i][j].resize(nBinsZ_);
95 }
96 }
97
98 mag1.resize(nBinsX_ * nBinsY_);
99 newmag1.resize(nBinsX_ * nBinsY_);
100 mag2.resize(nBinsX_ * nBinsY_);
101 newmag2.resize(nBinsX_ * nBinsY_);
102
103 // Pre-load the OutputData
104 data_.resize(Hxy::ENDINDEX);
105
106 OutputData freq;
107 freq.units = "Angstroms^-1";
108 freq.title = "Spatial Frequency";
109 freq.dataHandling = DataHandling::Average;
110 for (unsigned int i = 0; i < nBins_; i++)
111 freq.accumulator.push_back(
112 std::make_unique<AccumulatorView<RealAccumulator>>());
113 data_[FREQUECY] = std::move(freq);
114
115 OutputData top;
116 top.units = "Angstroms";
117 top.title = "Hxy (Upper surface)";
118 freq.dataHandling = DataHandling::Max;
119 for (unsigned int i = 0; i < nBins_; i++)
120 top.accumulator.push_back(
121 std::make_unique<AccumulatorView<RealAccumulator>>());
122 data_[TOP] = std::move(top);
123
124 OutputData bottom;
125 bottom.units = "Angstroms";
126 bottom.title = "Hxy (Lower surface)";
127 freq.dataHandling = DataHandling::Max;
128 for (unsigned int i = 0; i < nBins_; i++)
129 bottom.accumulator.push_back(
130 std::make_unique<AccumulatorView<RealAccumulator>>());
131 data_[BOTTOM] = std::move(bottom);
132
133 setOutputName(getPrefix(filename) + ".Hxy");
134 }
135
136 void Hxy::process() {
137#if defined(HAVE_FFTW_H) || defined(HAVE_DFFTW_H) || defined(HAVE_FFTW3_H)
138 StuntDouble* sd;
139 int ii;
140 bool usePeriodicBoundaryConditions_ =
141 info_->getSimParams()->getUsePeriodicBoundaryConditions();
142 std::cerr << "usePeriodicBoundaryConditions_ = "
143 << usePeriodicBoundaryConditions_ << "\n";
144
145 DumpReader reader(info_, dumpFilename_);
146 int nFrames = reader.getNFrames();
147 nProcessed_ = nFrames / step_;
148
149 for (int istep = 0; istep < nFrames; istep += step_) {
150 for (unsigned int i = 0; i < nBinsX_; i++) {
151 std::fill(minHeight_[i].begin(), minHeight_[i].end(), 0.0);
152 std::fill(maxHeight_[i].begin(), maxHeight_[i].end(), 0.0);
153 for (unsigned int j = 0; j < nBinsY_; j++) {
154 std::fill(dens_[i][j].begin(), dens_[i][j].end(), 0.0);
155 }
156 }
157
158 reader.readFrame(istep);
159 currentSnapshot_ = info_->getSnapshotManager()->getCurrentSnapshot();
160 if (evaluator_.isDynamic()) {
161 seleMan_.setSelectionSet(evaluator_.evaluate());
162 }
163
164#ifdef HAVE_FFTW3_H
165 fftw_plan p1, p2;
166#else
167 fftwnd_plan p1, p2;
168#endif
169 fftw_complex *in1, *in2, *out1, *out2;
170
171 in1 = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) *
172 (nBinsX_ * nBinsY_));
173 out1 = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) *
174 (nBinsX_ * nBinsY_));
175 in2 = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) *
176 (nBinsX_ * nBinsY_));
177 out2 = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) *
178 (nBinsX_ * nBinsY_));
179
180#ifdef HAVE_FFTW3_H
181 p1 = fftw_plan_dft_2d(nBinsX_, nBinsY_, in1, out1, FFTW_FORWARD,
182 FFTW_ESTIMATE);
183 p2 = fftw_plan_dft_2d(nBinsX_, nBinsY_, in2, out2, FFTW_FORWARD,
184 FFTW_ESTIMATE);
185#else
186 p1 = fftw2d_create_plan(nBinsX_, nBinsY_, FFTW_FORWARD, FFTW_ESTIMATE);
187 p2 = fftw2d_create_plan(nBinsX_, nBinsY_, FFTW_FORWARD, FFTW_ESTIMATE);
188#endif
189
190 Mat3x3d hmat = currentSnapshot_->getHmat();
191 Mat3x3d invBox = currentSnapshot_->getInvHmat();
192 RealType lenX_ = hmat(0, 0);
193 RealType lenY_ = hmat(1, 1);
194 RealType lenZ_ = hmat(2, 2);
195
196 RealType x, y, z, dx, dy, dz;
197 RealType sigma, rcut;
198 int di, dj, dk, ibin, jbin, kbin;
199 int igrid, jgrid, kgrid;
200 Vector3d scaled;
201
202 dx = lenX_ / nBinsX_;
203 dy = lenY_ / nBinsY_;
204 dz = lenZ_ / nBinsZ_;
205
206 for (sd = seleMan_.beginSelected(ii); sd != NULL;
207 sd = seleMan_.nextSelected(ii)) {
208 if (sd->isAtom()) {
209 Atom* atom = static_cast<Atom*>(sd);
210 Vector3d pos = sd->getPos();
212 // For SPC/E water, this yields the Willard-Chandler
213 // distance of 2.4 Angstroms:
214 sigma = lja.getSigma() * 0.758176459;
215 rcut = 3.0 * sigma;
216
217 // scaled positions relative to the box vectors
218 // -> the atom's position in numbers of box lengths (more accurately
219 // box vectors)
220 scaled = invBox * pos;
221
222 // wrap the vector back into the unit box by subtracting
223 // integer box numbers
224 for (int j = 0; j < 3; j++) {
225 scaled[j] -= roundMe(scaled[j]);
226 scaled[j] += 0.5;
227 // Handle the special case when an object is exactly on
228 // the boundary (a scaled coordinate of 1.0 is the same as
229 // scaled coordinate of 0.0)
230 if (scaled[j] >= 1.0) scaled[j] -= 1.0;
231 }
232 // find ijk-indices of voxel that atom is in.
233 ibin = nBinsX_ * scaled.x();
234 jbin = nBinsY_ * scaled.y();
235 kbin = nBinsZ_ * scaled.z();
236
237 di = (int)(rcut / dx);
238 dj = (int)(rcut / dy);
239 dk = (int)(rcut / dz);
240
241 for (int i = -di; i <= di; i++) {
242 igrid = ibin + i;
243 while (igrid >= int(nBinsX_)) {
244 igrid -= int(nBinsX_);
245 }
246 while (igrid < 0) {
247 igrid += int(nBinsX_);
248 }
249
250 x = lenX_ * (RealType(i) / RealType(nBinsX_));
251
252 for (int j = -dj; j <= dj; j++) {
253 jgrid = jbin + j;
254 while (jgrid >= int(nBinsY_)) {
255 jgrid -= int(nBinsY_);
256 }
257 while (jgrid < 0) {
258 jgrid += int(nBinsY_);
259 }
260
261 y = lenY_ * (RealType(j) / RealType(nBinsY_));
262
263 for (int k = -dk; k <= dk; k++) {
264 kgrid = kbin + k;
265 while (kgrid >= int(nBinsZ_)) {
266 kgrid -= int(nBinsZ_);
267 }
268 while (kgrid < 0) {
269 kgrid += int(nBinsZ_);
270 }
271
272 z = lenZ_ * (RealType(k) / RealType(nBinsZ_));
273
274 RealType dist = sqrt(x * x + y * y + z * z);
275
276 dens_[igrid][jgrid][kgrid] += getDensity(dist, sigma, rcut);
277 }
278 }
279 }
280 }
281 }
282
283 RealType maxDens(0.0);
284 for (unsigned int i = 0; i < nBinsX_; i++) {
285 for (unsigned int j = 0; j < nBinsY_; j++) {
286 for (unsigned int k = 0; k < nBinsZ_; k++) {
287 if (dens_[i][j][k] > maxDens) maxDens = dens_[i][j][k];
288 }
289 }
290 }
291
292 RealType threshold = maxDens / 2.0;
293 RealType z0, z1, h0, h1;
294 std::cerr << "maxDens = "
295 << "\t" << maxDens << "\n";
296 std::cerr << "threshold value = "
297 << "\t" << threshold << "\n";
298
299 for (unsigned int i = 0; i < nBinsX_; i++) {
300 for (unsigned int j = 0; j < nBinsY_; j++) {
301 // There are two cases if we are periodic in z and the
302 // density is localized in z. Either we're starting below
303 // the isodensity, or above it:
304 // ______ _______ ______
305 // ____/ \_____ or: \______/
306 //
307 // In either case, there are two crossings of the
308 // isodensity.
309
310 bool minFound = false;
311 bool maxFound = false;
312
313 if (dens_[i][j][0] < threshold) {
314 for (unsigned int k = 0; k < nBinsZ_ - 1; k++) {
315 z0 = lenZ_ * (RealType(k) / RealType(nBinsZ_));
316 z1 = lenZ_ * (RealType(k + 1) / RealType(nBinsZ_));
317 h0 = dens_[i][j][k];
318 h1 = dens_[i][j][k + 1];
319
320 if (h0 < threshold && h1 > threshold && !minFound) {
321 // simple linear interpolation to find the height:
322 minHeight_[i][j] =
323 z0 + (z1 - z0) * (threshold - h0) / (h1 - h0);
324 minFound = true;
325 }
326 if (h0 > threshold && h1 < threshold && minFound) {
327 // simple linear interpolation to find the height:
328 maxHeight_[i][j] =
329 z0 + (z1 - z0) * (threshold - h0) / (h1 - h0);
330 maxFound = true;
331 }
332 }
333
334 } else {
335 for (unsigned int k = 0; k < nBinsZ_ - 1; k++) {
336 z0 = lenZ_ * (RealType(k) / RealType(nBinsZ_));
337 z1 = lenZ_ * (RealType(k + 1) / RealType(nBinsZ_));
338 h0 = dens_[i][j][k];
339 h1 = dens_[i][j][k + 1];
340
341 if (h0 > threshold && h1 < threshold && !maxFound) {
342 // simple linear interpolation to find the height:
343 maxHeight_[i][j] =
344 z0 + (z1 - z0) * (threshold - h0) / (h1 - h0);
345 maxFound = true;
346 }
347 if (h0 < threshold && h1 > threshold && maxFound) {
348 // simple linear interpolation to find the height:
349 minHeight_[i][j] =
350 z0 + (z1 - z0) * (threshold - h0) / (h1 - h0);
351 }
352 }
353 }
354 }
355 }
356
357 RealType minBar = 0.0;
358 RealType maxBar = 0.0;
359 int count = 0;
360 for (unsigned int i = 0; i < nBinsX_; i++) {
361 for (unsigned int j = 0; j < nBinsY_; j++) {
362 // if (minHeight_[i][j] < 0.0) std::cerr << "minHeight[i][j] = " <<
363 // "\t"
364 // << minHeight_[i][j] << "\n";
365 minBar += minHeight_[i][j];
366 maxBar += maxHeight_[i][j];
367 count++;
368 }
369 }
370 minBar /= count;
371 maxBar /= count;
372
373 std::cerr << "bottomSurf = " << minBar << "\ttopSurf = " << maxBar
374 << "\n";
375 int newindex;
376 // RealType Lx = 10.0;
377 // RealType Ly = 10.0;
378 for (unsigned int i = 0; i < nBinsX_; i++) {
379 for (unsigned int j = 0; j < nBinsY_; j++) {
380 newindex = i * nBinsY_ + j;
381 // if(minHeight_[i][j] < 0.0) std::cerr << minHeight_[i][j] << "\t";
382 c_re(in1[newindex]) = maxHeight_[i][j] - maxBar;
383 // c_re(in1[newindex]) = 2.0*cos(2.0*Constants::PI*i/Lx/Ly);
384 c_im(in1[newindex]) = 0.0;
385 c_re(in2[newindex]) =
386 minHeight_[i][j] - minBar; // this was the original line.
387 // c_re(in2[newindex]) = 1.0*cos(2.0*Constants::PI*i/Lx/Ly);
388 c_im(in2[newindex]) = 0.0;
389 }
390 }
391
392#ifdef HAVE_FFTW3_H
393 fftw_execute(p1);
394 fftw_execute(p2);
395#else
396 fftwnd_one(p1, in1, out1);
397 fftwnd_one(p2, in2, out2);
398#endif
399
400 for (unsigned int i = 0; i < nBinsX_; i++) {
401 for (unsigned int j = 0; j < nBinsY_; j++) {
402 newindex = i * nBinsY_ + j;
403 mag1[newindex] = sqrt(pow(c_re(out1[newindex]), 2) +
404 pow(c_im(out1[newindex]), 2)) /
405 (RealType(nBinsX_ * nBinsY_));
406 mag2[newindex] = sqrt(pow(c_re(out2[newindex]), 2) +
407 pow(c_im(out2[newindex]), 2)) /
408 (RealType(nBinsX_ * nBinsY_));
409 }
410 }
411
412#ifdef HAVE_FFTW3_H
413 fftw_destroy_plan(p1);
414 fftw_destroy_plan(p2);
415#else
416 fftwnd_destroy_plan(p1);
417 fftwnd_destroy_plan(p2);
418#endif
419 fftw_free(out1);
420 fftw_free(in1);
421 fftw_free(out2);
422 fftw_free(in2);
423
424 int index, new_i, new_j, new_index;
425 for (unsigned int i = 0; i < (nBinsX_ / 2); i++) {
426 for (unsigned int j = 0; j < (nBinsY_ / 2); j++) {
427 index = i * nBinsY_ + j;
428 new_i = i + (nBinsX_ / 2);
429 new_j = j + (nBinsY_ / 2);
430 new_index = new_i * nBinsY_ + new_j;
431 newmag1[new_index] = mag1[index];
432 newmag2[new_index] = mag2[index];
433 }
434 }
435
436 for (unsigned int i = (nBinsX_ / 2); i < nBinsX_; i++) {
437 for (unsigned int j = 0; j < (nBinsY_ / 2); j++) {
438 index = i * nBinsY_ + j;
439 new_i = i - (nBinsX_ / 2);
440 new_j = j + (nBinsY_ / 2);
441 new_index = new_i * nBinsY_ + new_j;
442 newmag1[new_index] = mag1[index];
443 newmag2[new_index] = mag2[index];
444 }
445 }
446
447 for (unsigned int i = 0; i < (nBinsX_ / 2); i++) {
448 for (unsigned int j = (nBinsY_ / 2); j < nBinsY_; j++) {
449 index = i * nBinsY_ + j;
450 new_i = i + (nBinsX_ / 2);
451 new_j = j - (nBinsY_ / 2);
452 new_index = new_i * nBinsY_ + new_j;
453 newmag1[new_index] = mag1[index];
454 newmag2[new_index] = mag2[index];
455 }
456 }
457
458 for (unsigned int i = (nBinsX_ / 2); i < nBinsX_; i++) {
459 for (unsigned int j = (nBinsY_ / 2); j < nBinsY_; j++) {
460 index = i * nBinsY_ + j;
461 new_i = i - (nBinsX_ / 2);
462 new_j = j - (nBinsY_ / 2);
463 new_index = new_i * nBinsY_ + new_j;
464 newmag1[new_index] = mag1[index];
465 newmag2[new_index] = mag2[index];
466 }
467 }
468
469 /*
470 for (unsigned int i=0; i< nBinsX_; i++) {
471 for(unsigned int j=0; j< nBinsY_; j++) {
472 newindex = i*nBinsY_ + j;
473 std::cout << newmag1[newindex] << "\t";
474 }
475 std::cout << "\n";
476 }
477 */
478
479 RealType maxfreqx = RealType(nBinsX_) / lenX_;
480 RealType maxfreqy = RealType(nBinsY_) / lenY_;
481
482 RealType maxfreq = sqrt(maxfreqx * maxfreqx + maxfreqy * maxfreqy);
483 dfreq_ = maxfreq / (RealType)(nBins_ - 1);
484
485 int zero_freq_x = nBinsX_ / 2;
486 int zero_freq_y = nBinsY_ / 2;
487
488 for (int i = 0; i < (int)nBinsX_; i++) {
489 for (int j = 0; j < (int)nBinsY_; j++) {
490 RealType freq_x =
491 (RealType)(i - zero_freq_x) * maxfreqx / (RealType)nBinsX_;
492 RealType freq_y =
493 (RealType)(j - zero_freq_y) * maxfreqy / (RealType)nBinsY_;
494
495 RealType freq = sqrt(freq_x * freq_x + freq_y * freq_y);
496
497 unsigned int whichbin = (unsigned int)(freq / dfreq_);
498
499 newindex = i * nBinsY_ + j;
500
501 data_[FREQUECY].accumulator[whichbin]->add(freq);
502 data_[TOP].accumulator[whichbin]->add(newmag1[newindex]);
503 data_[BOTTOM].accumulator[whichbin]->add(newmag2[newindex]);
504 }
505 }
506 }
507
508 writeOutput();
509
510#else
511 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
512 "Hxy: FFTW support was not compiled in!\n");
513 painCave.isFatal = 1;
514 simError();
515
516#endif
517 }
518
519 RealType Hxy::getDensity(RealType r, RealType sigma, RealType rcut) {
520 RealType sigma2 = sigma * sigma;
521 RealType dens =
522 exp(-r * r / (sigma2 * 2.0)) / (pow(2.0 * Constants::PI * sigma2, 3));
523 RealType dcut = exp(-rcut * rcut / (sigma2 * 2.0)) /
524 (pow(2.0 * Constants::PI * sigma2, 3));
525 if (r < rcut)
526 return dens - dcut;
527 else
528 return 0.0;
529 }
530} // namespace OpenMD
AtomType * getAtomType()
Returns the AtomType of this Atom.
Definition Atom.hpp:86
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
"Don't move, or you're dead! Stand up! Captain, we've got them!"
Vector3d getPos()
Returns the current position of this stuntDouble.
bool isAtom()
Tests if this stuntDouble is an atom.
Real & z()
Returns reference of the third element of Vector3.
Definition Vector3.hpp:123
Real & x()
Returns reference of the first element of Vector3.
Definition Vector3.hpp:99
Real & y()
Returns reference of the second element of Vector3.
Definition Vector3.hpp:111
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
std::string getPrefix(const std::string &str)