32#include "applications/dynamicProps/SFG.hpp"
42#include "math/Eigenvalue.hpp"
44#include "utils/Revision.hpp"
45#include "utils/simError.h"
47#if defined(HAVE_FFTW3_H)
52#if defined(HAVE_LAPACK)
54 void dsyevd_(
const char* jobz,
const char* uplo,
const int* n,
55 double* a,
const int* lda,
double* w,
56 double* work,
const int* lwork,
57 int* iwork,
const int* liwork,
int* info);
62#if defined(HAVE_CBLAS)
78 static constexpr double HBAR_CM_PS = 5.3088373;
83 SFG::SFG(
SimInfo* info,
const std::string& filename,
84 const std::string& sele1,
const std::string& sele2,
85 const std::string& polarization,
int privilegedAxis,
86 RealType t_apod, RealType t_zerofill,
88 SystemACF<RealType>(info, filename, sele1, sele2),
89 polarization_(polarization),
90 privilegedAxis_(privilegedAxis),
91 s1_((privilegedAxis + 1) % 3),
92 s2_((privilegedAxis + 2) % 3),
93 t_apod_ps_(t_apod * 1e-3),
94 t_zerofill_ps_(t_zerofill * 1e-3),
97 setCorrFuncType(
"SFG");
98 setOutputName(
getPrefix(dumpFilename_) +
".sfg");
99 setLabelString(
"Im chi2(omega)");
124 w10_[
"H_SPCE"] = std::make_tuple(3761.6, -5060.4, -86225.0);
125 muPrime_[
"H_SPCE"] = std::make_tuple(0.1333, 14.17, 0.0);
126 x10_[
"H_SPCE"] = std::make_pair(0.1934, -1.75e-5);
127 p10_[
"H_SPCE"] = std::make_pair(1.611, 5.893e-4);
128 wintra_[
"H_SPCE"] = std::make_tuple(-1789.0, 23852.0, -1.966);
129 alphaMap_[
"H_SPCE"] = std::make_pair(0.185, 0.033);
130 tdcLoc_[
"H_SPCE"] = 0.58;
133 w10_[
"H_TIP4P"] = std::make_tuple(3760.2, -3541.7, -152677.0);
134 muPrime_[
"H_TIP4P"] = std::make_tuple(0.1646, 11.39, 63.41);
135 x10_[
"H_TIP4P"] = std::make_pair(0.19285, -1.7261e-5);
136 p10_[
"H_TIP4P"] = std::make_pair(1.6466, 5.7692e-4);
137 wintra_[
"H_TIP4P"] = std::make_tuple(-1361.0, 27165.0, -1.887);
138 alphaMap_[
"H_TIP4P"] = std::make_pair(0.185, 0.033);
139 tdcLoc_[
"H_TIP4P"] = 0.67;
142 w10_[
"H_TIP4P-Ice"] = w10_[
"H_TIP4P"];
143 muPrime_[
"H_TIP4P-Ice"] = muPrime_[
"H_TIP4P"];
144 x10_[
"H_TIP4P-Ice"] = x10_[
"H_TIP4P"];
145 p10_[
"H_TIP4P-Ice"] = p10_[
"H_TIP4P"];
146 wintra_[
"H_TIP4P-Ice"] = wintra_[
"H_TIP4P"];
147 alphaMap_[
"H_TIP4P-Ice"] = alphaMap_[
"H_TIP4P"];
148 tdcLoc_[
"H_TIP4P-Ice"] = tdcLoc_[
"H_TIP4P"];
163 wb01_[
"TIP4P"] = std::make_pair(1581.46, 2938.51);
164 wb12_[
"TIP4P"] = std::make_pair(1551.32, 3147.80);
165 wb01_[
"TIP4P-Ice"] = wb01_[
"TIP4P"];
166 wb12_[
"TIP4P-Ice"] = wb12_[
"TIP4P"];
171 wb01_[
"SPCE"] = wb01_[
"TIP4P"];
172 wb12_[
"SPCE"] = wb12_[
"TIP4P"];
180 const RealType chrgToKcal = 23.060548;
181 std::vector<RealType> ef;
183 if (info_->getSimParams()->haveElectricField()) {
184 efSpec =
true; ef = info_->getSimParams()->getElectricField();
186 if (info_->getSimParams()->haveUniformField()) {
187 efSpec =
true; ef = info_->getSimParams()->getUniformField();
190 if (ef.size() != 3) {
191 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
192 "SFG: uniformField must have 3 components (%zu given).\n",
194 painCave.isFatal = 1; simError();
196 EF_.x() = ef[0] * chrgToKcal;
197 EF_.y() = ef[1] * chrgToKcal;
198 EF_.z() = ef[2] * chrgToKcal;
204 dumpHasElectricFields_ = info_->getSimParams()->getOutputElectricField();
206 if (!dumpHasElectricFields_) {
207 int asl = info_->getAtomStorageLayout() | DataStorage::dslElectricField;
208 int rbsl = info_->getRigidBodyStorageLayout() | DataStorage::dslElectricField;
209 int cgsl = info_->getCutoffGroupStorageLayout();
210 info_->setAtomStorageLayout(asl);
211 info_->setRigidBodyStorageLayout(rbsl);
212 info_->setCutoffGroupStorageLayout(cgsl);
213 info_->setSnapshotManager(
new SimSnapshotManager(info_, asl, rbsl, cgsl));
214 forceMan_ =
new ForceManager(info_);
215 forceMan_->setDoElectricField(
true);
216 forceMan_->initialize();
218 info_->getSimParams()->setOutputElectricField(
true);
221 SFG::~SFG() {
delete forceMan_; }
231 void SFG::computeProperty1(
int frame) {
235 if (
static_cast<int>(allFrames_.size()) <= frame)
236 allFrames_.resize(frame + 1);
238 if (!dumpHasElectricFields_) {
239 forceMan_->setDoElectricField(
true);
240 forceMan_->calcForces();
243 std::vector<Vector3d> ohPos;
244 std::vector<int> molIndex;
245 std::vector<RealType> intraJ;
246 allFrames_[frame] = extractFrame(ohPos, molIndex, intraJ);
247 buildHamiltonian(allFrames_[frame], ohPos, molIndex, intraJ);
254 const FrameData& fd = allFrames_[frame];
255 for (
int i = 0; i < fd.nStretch; ++i) {
256 wAvgSum_ += fd.H(i, i);
281 void SFG::correlation() {
282 const std::complex<double> czero(0.0, 0.0);
284 int ncor =
static_cast<int>(nTimeBins_);
285 RealType dt_ps = dtMean_ * 1.0e-3;
291 wAvg_ = (wAvgCount_ > 0)
292 ? wAvgSum_ /
static_cast<RealType
>(wAvgCount_)
297 for (
auto& fd : allFrames_) {
298 for (
int i = 0; i < fd.N; ++i)
302 tcf_ssp_.assign(ncor, czero);
303 tcf_ppp_.assign(ncor, czero);
304 tcf_sps_.assign(ncor, czero);
306 progressBar_->clear();
318 int completedWindows = 0;
323 std::vector<std::complex<double>> loc_ssp(ncor, czero);
324 std::vector<std::complex<double>> loc_ppp(ncor, czero);
325 std::vector<std::complex<double>> loc_sps(ncor, czero);
327#pragma omp for schedule(dynamic)
328 for (
int iwin = 0; iwin < navg_; ++iwin) {
329 int istart = nStart_ + iwin * nStride_;
330 if (istart >= nFrames_)
continue;
332 const FrameData& fd0raw = allFrames_[istart];
334 if (N == 0)
continue;
336 const std::vector<int>& refIDs = fd0raw.globalIDs;
338 std::vector<RealType> refFreqs(N);
339 for (
int i = 0; i < N; ++i)
340 refFreqs[i] = fd0raw.H(i, i);
343 std::vector<std::complex<double>> F(N * N, czero);
344 for (
int i = 0; i < N; ++i) F[i*N+i] = std::complex<double>(1.0, 0.0);
347 remapFrame(fd0raw, refIDs, refFreqs, fd0,
348 fd0raw.nStretch, fd0raw.nBend);
350 for (
int tt = 0; tt < ncor; ++tt) {
351 int iframe = istart + tt;
352 if (iframe >= nFrames_)
break;
355 if (!remapFrame(allFrames_[iframe], refIDs, refFreqs, fdt,
356 fd0raw.nStretch, fd0raw.nBend))
359 for (
int i = 0; i < N; ++i) {
360 auto it = allFrames_[iframe].idToIndex.find(refIDs[i]);
361 if (it != allFrames_[iframe].idToIndex.end())
362 refFreqs[i] = allFrames_[iframe].H(it->second, it->second);
366 propagateF(F, fdt.H, dt_ps, N);
368 RealType exptc = (T1_ps_ > 0.0)
369 ? std::exp(-
static_cast<RealType
>(tt) * dt_ps / (2.0 * T1_ps_))
372 accumulateTCF(tt, F, fd0, fdt, N, exptc,
373 loc_ssp, loc_ppp, loc_sps);
380 if (omp_get_thread_num() == 0) {
381 progressBar_->setStatus(completedWindows, navg_);
382 progressBar_->update();
389 for (
int tt = 0; tt < ncor; ++tt) {
390 tcf_ssp_[tt] += loc_ssp[tt];
391 tcf_ppp_[tt] += loc_ppp[tt];
392 tcf_sps_[tt] += loc_sps[tt];
401 for (
int iwin = 0; iwin < navg_; ++iwin) {
402 int istart = nStart_ + iwin * nStride_;
403 if (istart >= nFrames_)
break;
405 progressBar_->setStatus(iwin + 1, navg_);
406 progressBar_->update();
408 const FrameData& fd0raw = allFrames_[istart];
410 if (N == 0)
continue;
412 const std::vector<int>& refIDs = fd0raw.globalIDs;
414 std::vector<RealType> refFreqs(N);
415 for (
int i = 0; i < N; ++i)
416 refFreqs[i] = fd0raw.H(i, i);
419 std::vector<std::complex<double>> F(N * N, czero);
420 for (
int i = 0; i < N; ++i) F[i*N+i] = std::complex<double>(1.0, 0.0);
423 remapFrame(fd0raw, refIDs, refFreqs, fd0,
424 fd0raw.nStretch, fd0raw.nBend);
426 for (
int tt = 0; tt < ncor; ++tt) {
427 int iframe = istart + tt;
428 if (iframe >= nFrames_)
break;
431 if (!remapFrame(allFrames_[iframe], refIDs, refFreqs, fdt,
432 fd0raw.nStretch, fd0raw.nBend))
435 for (
int i = 0; i < N; ++i) {
436 auto it = allFrames_[iframe].idToIndex.find(refIDs[i]);
437 if (it != allFrames_[iframe].idToIndex.end())
438 refFreqs[i] = allFrames_[iframe].H(it->second, it->second);
442 propagateF(F, fdt.H, dt_ps, N);
444 RealType exptc = (T1_ps_ > 0.0)
445 ? std::exp(-
static_cast<RealType
>(tt) * dt_ps / (2.0 * T1_ps_))
448 accumulateTCF(tt, F, fd0, fdt, N, exptc,
449 tcf_ssp_, tcf_ppp_, tcf_sps_);
456 RealType inv = 1.0 /
static_cast<RealType
>(navg_);
457 for (
int tt = 0; tt < ncor; ++tt) {
474 SFG::FrameData SFG::extractFrame(std::vector<Vector3d>& ohPos,
475 std::vector<int>& molIndex,
476 std::vector<RealType>& intraJ) {
477 const RealType kcalToAU = 0.0008432975573;
484 std::vector<RealType> freqs;
488 std::vector<RealType> eFields;
489 std::vector<RealType> x10vals;
490 std::vector<RealType> p10vals;
491 std::vector<RealType> mxvals;
492 std::vector<RealType> intraJ_k0;
493 std::vector<RealType> intraJ_kp;
499 Vector3d ohUnit1, ohUnit2;
500 Vector3d Efield1, Efield2;
501 RealType rOH1 = 0.0, rOH2 = 0.0;
504 std::string modelName;
506 std::map<int, MolData> molMap;
511 for (mol = seleMan1_.beginSelectedMolecule(ii); mol !=
nullptr;
512 mol = seleMan1_.nextSelectedMolecule(ii)) {
514 int molID = mol->getGlobalIndex();
520 std::vector<Atom*> hAtoms;
521 Molecule::AtomIterator ai;
522 for (Atom* atom = mol->beginAtom(ai); atom !=
nullptr;
523 atom = mol->nextAtom(ai)) {
524 const std::string& aname = atom->getAtomType()->getName();
525 if (aname[0] ==
'O' && !foundO) {
526 Opos = atom->getPos();
527 OgID = atom->getGlobalIndex();
529 }
else if (w10_.find(aname) != w10_.end()) {
530 hAtoms.push_back(atom);
533 if (!foundO)
continue;
534 if (hAtoms.empty())
continue;
541 for (Atom* hAtom : hAtoms) {
542 std::string name = hAtom->getAtomType()->getName();
544 auto wIt = w10_.find(name);
545 auto mpIt = muPrime_.find(name);
546 if (wIt == w10_.end() || mpIt == muPrime_.end())
continue;
548 Vector3d Hpos = hAtom->getPos();
549 Vector3d rOH_mic = Hpos - Opos;
550 currentSnapshot_->wrapVector(rOH_mic);
551 RealType rOH_len = rOH_mic.
length();
552 Vector3d ohUnit = rOH_mic;
556 Vector3d sE = hAtom->getElectricField() + EF_;
558 RealType E =
dot(ohUnit, sE);
561 auto [a0, a1, a2] = wIt->second;
562 RealType freq = a0 + a1*E + a2*E*E;
564 auto [m0, m1, m2] = mpIt->second;
565 RealType muPrime = m0 + m1*E + m2*E*E;
567 auto [x0, x1] = x10_.at(name);
568 RealType x10 = x0 + x1*freq;
571 auto [pv0, pv1] = p10_.at(name);
572 RealType p10 = pv0 + pv1*freq;
575 const RealType eaBohr_to_Debye = 2.5417464;
576 RealType muMag = muPrime * x10 * eaBohr_to_Debye;
580 RealType tdcDist = 0.5 * rOH_len;
581 auto tdcIt = tdcLoc_.find(name);
582 if (tdcIt != tdcLoc_.end())
583 tdcDist = tdcIt->second;
584 Vector3d dipolePos = Opos + tdcDist * ohUnit;
587 auto [apar, aperp] = alphaMap_.at(name);
588 RealType x10_gas = x10_.at(name).first;
591 fd.globalIDs.push_back(hAtom->getGlobalIndex());
592 fd.mu.push_back(muMag * ohUnit);
593 fd.alpha.push_back(bondPolarizability(ohUnit, apar, aperp,
597 ohPos.push_back(dipolePos);
598 molIndex.push_back(molID);
599 freqs.push_back(freq);
600 eFields.push_back(E);
601 x10vals.push_back(x10);
602 p10vals.push_back(p10);
603 mxvals.push_back(muPrime * x10);
605 auto [k0, k1, kp] = wintra_.at(name);
606 intraJ.push_back(k0 + k1*E);
607 intraJ_k0.push_back(k0);
608 intraJ_kp.push_back(kp);
613 MolData& md = molMap[molID];
616 md.modelName = mol->getType();
617 if (md.ohCount == 0) {
621 }
else if (md.ohCount == 1) {
631 fd.nStretch = nStretch;
656 for (
auto& [molID, md] : molMap) {
657 if (md.ohCount != 2)
continue;
658 auto bIt = wb01_.find(md.modelName);
659 if (bIt == wb01_.end())
continue;
660 auto cIt = wb12_.find(md.modelName);
661 if (cIt == wb12_.end())
continue;
664 Vector3d vp =
cross(md.ohUnit1, md.ohUnit2);
665 RealType vpLen = vp.length();
666 if (vpLen < 1.0e-9)
continue;
669 Vector3d ePerp1 =
cross(vp, md.ohUnit1);
670 Vector3d ePerp2 =
cross(md.ohUnit2, vp);
673 RealType Eb =
dot(ePerp1, md.Efield1) / md.rOH1
674 +
dot(ePerp2, md.Efield2) / md.rOH2;
677 auto [b0_10, b1_10] = bIt->second;
678 auto [b0_21, b1_21] = cIt->second;
679 RealType w_2d = (b0_10 + b1_10*Eb) + (b0_21 + b1_21*Eb);
684 fd.globalIDs.push_back(md.molOgID);
685 fd.mu.push_back(V3Zero);
686 fd.alpha.push_back(Mat3x3d(0.0));
694 ohPos.push_back(md.Opos);
695 molIndex.push_back(molID);
696 freqs.push_back(w_2d);
697 eFields.push_back(Eb);
698 x10vals.push_back(0.0);
699 p10vals.push_back(0.0);
700 mxvals.push_back(0.0);
701 intraJ.push_back(0.0);
702 intraJ_k0.push_back(0.0);
703 intraJ_kp.push_back(0.0);
710 useFermi_ = (nBend > 0);
716 for (
int i = 0; i < N; ++i) {
717 fd.H(i, i) = freqs[i];
718 fd.idToIndex[fd.globalIDs[i]] = i;
735 std::vector<RealType> packed;
736 packed.reserve(6 * N);
737 for (
int i = 0; i < N; ++i) {
738 packed.push_back(intraJ[i]);
739 packed.push_back(x10vals[i]);
740 packed.push_back(p10vals[i]);
741 packed.push_back(intraJ_k0[i]);
742 packed.push_back(intraJ_kp[i]);
743 packed.push_back(mxvals[i]);
745 intraJ = std::move(packed);
771 void SFG::buildHamiltonian(FrameData& fd,
772 const std::vector<Vector3d>& ohPos,
773 const std::vector<int>& molIndex,
774 const std::vector<RealType>& intraJ) {
787 const int nStr = fd.nStretch;
789 for (
int i = 0; i < N; ++i) {
790 bool i_is_bend = (i >= nStr);
791 for (
int j = i+1; j < N; ++j) {
792 bool j_is_bend = (j >= nStr);
794 if (molIndex[i] == molIndex[j]) {
795 if (!i_is_bend && !j_is_bend) {
798 RealType Ki = intraJ[6*i + 0];
799 RealType xi = intraJ[6*i + 1];
800 RealType pi = intraJ[6*i + 2];
801 RealType k0_val = intraJ[6*i + 3];
802 RealType kp_val = intraJ[6*i + 4];
803 RealType Kj = intraJ[6*j + 0];
804 RealType xj = intraJ[6*j + 1];
805 RealType pj = intraJ[6*j + 2];
807 RealType potentialTerm = (Ki + Kj - k0_val) * xi * xj;
808 RealType kineticTerm = kp_val * pi * pj;
809 J = potentialTerm + kineticTerm;
810 }
else if (i_is_bend != j_is_bend) {
819 Vector3d r_ij = ohPos[j] - ohPos[i];
820 currentSnapshot_->wrapVector(r_ij);
822 Vector3d e_i = fd.mu[i];
823 RealType m_i = e_i.
length();
824 if (m_i > 1.0e-12) e_i /= m_i;
826 Vector3d e_j = fd.mu[j];
827 RealType m_j = e_j.
length();
828 if (m_j > 1.0e-12) e_j /= m_j;
830 RealType mx_i = intraJ[6*i + 5];
831 RealType mx_j = intraJ[6*j + 5];
833 J = tdcCoupling(r_ij, e_i, e_j, mx_i, mx_j);
862 void SFG::propagateF(std::vector<std::complex<double>>& F,
864 double dt_ps,
int N) {
866 using cdbl = std::complex<double>;
874 thread_local std::vector<double> Hbuf;
875 thread_local std::vector<double> Wbuf;
876 thread_local std::vector<double> Vbuf;
877 thread_local std::vector<double> work_d;
878 thread_local std::vector<int> iwork_d;
879 thread_local std::vector<double> Fr, Fi, Tr, Ti;
880 thread_local int lastN = 0;
882 const int N2 = N * N;
883 if (
static_cast<int>(Hbuf.size()) < N2) {
884 Hbuf.resize(N2); Wbuf.resize(N); Vbuf.resize(N2);
885 Fr.resize(N2); Fi.resize(N2);
886 Tr.resize(N2); Ti.resize(N2);
893#if defined(HAVE_LAPACK)
899 for (
int i = 0; i < N2; ++i)
900 Hbuf[i] =
static_cast<double>(src[i]);
905 int lwork_q = -1, liwork_q = -1, info_q = 0;
908 std::vector<double> Htmp(N2, 0.0);
909 std::vector<double> Wtmp(N, 0.0);
910 dsyevd_(
"V",
"U", &N, Htmp.data(), &N, Wtmp.data(),
911 &wq, &lwork_q, &wiq, &liwork_q, &info_q);
912 int lw =
static_cast<int>(wq);
914 if (
static_cast<int>(work_d.size()) < lw) work_d.resize(lw);
915 if (
static_cast<int>(iwork_d.size()) < liw) iwork_d.resize(liw);
920 int lw =
static_cast<int>(work_d.size());
921 int liw =
static_cast<int>(iwork_d.size());
923 dsyevd_(
"V",
"U", &N, Hbuf.data(), &N, Wbuf.data(),
924 work_d.data(), &lw, iwork_d.data(), &liw, &info);
929 for (
int i = 0; i < N; ++i)
930 for (
int k = 0; k < N; ++k)
931 Vbuf[i*N + k] = Hbuf[i + k*N];
934 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
935 "SFG::propagateF: dsyevd returned info=%d; using JAMA.\n", info);
936 painCave.isFatal = 0;
937 painCave.severity = OPENMD_WARNING;
947 eig.getRealEigenvalues(evals_j);
949 for (
int k = 0; k < N; ++k) {
950 Wbuf[k] =
static_cast<double>(evals_j[k]);
951 for (
int i = 0; i < N; ++i)
952 Vbuf[i*N + k] =
static_cast<double>(evecs_j(i, k));
956#if defined(HAVE_LAPACK)
963 std::vector<cdbl> phase(N);
964 for (
int k = 0; k < N; ++k) {
965 double arg = Wbuf[k] * dt_ps / HBAR_CM_PS;
966 phase[k] = std::exp(cdbl(0.0, arg));
987 for (
int idx = 0; idx < N2; ++idx) {
988 Fr[idx] = F[idx].real();
989 Fi[idx] = F[idx].imag();
992#if defined(HAVE_CBLAS)
993# if defined(__APPLE__)
994# pragma clang diagnostic push
995# pragma clang diagnostic ignored "-Wdeprecated-declarations"
998 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans,
999 N, N, N, 1.0, Vbuf.data(), N, Fr.data(), N, 0.0, Tr.data(), N);
1000 cblas_dgemm(CblasRowMajor, CblasTrans, CblasNoTrans,
1001 N, N, N, 1.0, Vbuf.data(), N, Fi.data(), N, 0.0, Ti.data(), N);
1002# if defined(__APPLE__)
1003# pragma clang diagnostic pop
1007 std::fill(Tr.begin(), Tr.begin()+N2, 0.0);
1008 std::fill(Ti.begin(), Ti.begin()+N2, 0.0);
1009 for (
int k = 0; k < N; ++k)
1010 for (
int i = 0; i < N; ++i) {
1011 double vik = Vbuf[i*N + k];
1012 for (
int j = 0; j < N; ++j) {
1013 Tr[k*N+j] += vik * Fr[i*N+j];
1014 Ti[k*N+j] += vik * Fi[i*N+j];
1020 for (
int k = 0; k < N; ++k) {
1021 double pr = phase[k].real(), pi = phase[k].imag();
1022 for (
int j = 0; j < N; ++j) {
1023 double tr = Tr[k*N+j], ti = Ti[k*N+j];
1024 Tr[k*N+j] = pr*tr - pi*ti;
1025 Ti[k*N+j] = pr*ti + pi*tr;
1029#if defined(HAVE_CBLAS)
1030# if defined(__APPLE__)
1031# pragma clang diagnostic push
1032# pragma clang diagnostic ignored "-Wdeprecated-declarations"
1035 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
1036 N, N, N, 1.0, Vbuf.data(), N, Tr.data(), N, 0.0, Fr.data(), N);
1037 cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
1038 N, N, N, 1.0, Vbuf.data(), N, Ti.data(), N, 0.0, Fi.data(), N);
1039# if defined(__APPLE__)
1040# pragma clang diagnostic pop
1044 std::fill(Fr.begin(), Fr.begin()+N2, 0.0);
1045 std::fill(Fi.begin(), Fi.begin()+N2, 0.0);
1046 for (
int i = 0; i < N; ++i)
1047 for (
int k = 0; k < N; ++k) {
1048 double vik = Vbuf[i*N + k];
1049 for (
int j = 0; j < N; ++j) {
1050 Fr[i*N+j] += vik * Tr[k*N+j];
1051 Fi[i*N+j] += vik * Ti[k*N+j];
1056 for (
int idx = 0; idx < N2; ++idx)
1057 F[idx] = cdbl(Fr[idx], Fi[idx]);
1081 void SFG::accumulateTCF(
int tt,
1082 const std::vector<std::complex<double>>& F,
1083 const FrameData& fd0,
1084 const FrameData& fdt,
1085 int N,
double exptc,
1086 std::vector<std::complex<double>>& tgt_ssp,
1087 std::vector<std::complex<double>>& tgt_ppp,
1088 std::vector<std::complex<double>>& tgt_sps) {
1090 using cdbl = std::complex<double>;
1091 const int n = privilegedAxis_;
1096 auto matvec = [&](
int r) -> std::vector<cdbl> {
1097 std::vector<cdbl> res(N, cdbl(0.0, 0.0));
1098 for (
int i = 0; i < N; ++i)
1099 for (
int j = 0; j < N; ++j)
1100 res[i] += F[i*N+j] *
static_cast<double>(fd0.mu[j][r]);
1104 std::vector<cdbl> Fmu_n = matvec(n);
1105 std::vector<cdbl> Fmu_s1 = matvec(s1);
1106 std::vector<cdbl> Fmu_s2 = matvec(s2);
1109 auto dot_alpha = [&](
int p,
int q,
const std::vector<cdbl>& Fmu) -> cdbl {
1111 for (
int i = 0; i < N; ++i)
1112 s += std::conj(cdbl(fdt.alpha[i](p, q))) * Fmu[i];
1116 cdbl decay(exptc, 0.0);
1119 tgt_ssp[tt] += 0.5 * (dot_alpha(s1, s1, Fmu_n)
1120 + dot_alpha(s2, s2, Fmu_n)) * decay;
1123 tgt_sps[tt] += 0.5 * (dot_alpha(s1, n, Fmu_s1)
1124 + dot_alpha(s2, n, Fmu_s2)) * decay;
1128 tgt_ppp[tt] += (-0.5 * dot_alpha(s1, s1, Fmu_n)
1129 - 0.5 * dot_alpha(s2, s2, Fmu_n)
1130 + dot_alpha(n, n, Fmu_n)) * decay;
1146 bool SFG::remapFrame(
const FrameData& src,
1147 const std::vector<int>& refIDs,
1148 const std::vector<RealType>& refFreqs,
1152 int N =
static_cast<int>(refIDs.size());
1156 out.nStretch = refNStretch;
1157 out.nBend = refNBend;
1158 out.globalIDs = refIDs;
1160 out.mu.assign(N, Vector3d(0.0, 0.0, 0.0));
1161 out.alpha.assign(N, Mat3x3d(0.0));
1165 for (
int i = 0; i < N; ++i) {
1166 auto it = src.idToIndex.find(refIDs[i]);
1167 if (it == src.idToIndex.end()) {
1168 out.H(i, i) = refFreqs[i];
1172 out.mu[i] = src.mu[j];
1173 out.alpha[i] = src.alpha[j];
1174 out.H(i, i) = src.H(j, j);
1175 for (
int k = 0; k < N; ++k) {
1176 if (k == i)
continue;
1177 auto kt = src.idToIndex.find(refIDs[k]);
1178 if (kt != src.idToIndex.end()) {
1179 out.H(i, k) = src.H(j, kt->second);
1180 out.H(k, i) = src.H(kt->second, j);
1185 return nMissing <= N / 2;
1210 RealType SFG::tdcCoupling(
const Vector3d& r_ij,
1211 const Vector3d& e_i,
1212 const Vector3d& e_j,
1213 RealType mx_i, RealType mx_j) {
1214 RealType r_A = r_ij.length();
1215 if (r_A < 1.0e-6)
return 0.0;
1216 Vector3d rhat = r_ij / r_A;
1219 const RealType A_to_bohr = 1.0 / 0.52917721067;
1220 RealType r_bohr = r_A * A_to_bohr;
1221 RealType r3 = r_bohr * r_bohr * r_bohr;
1224 const RealType AU_TO_WN = 219474.6313705;
1226 RealType geom =
dot(e_i, e_j) - 3.0 *
dot(e_i, rhat) *
dot(e_j, rhat);
1228 return AU_TO_WN * geom * mx_i * mx_j / r3;
1243 Mat3x3d SFG::bondPolarizability(
const Vector3d& ohUnit,
1244 RealType alpha_par, RealType alpha_perp,
1245 RealType x10, RealType x10_gas) {
1246 RealType scale = (x10_gas > 0.0) ? x10 / x10_gas : 1.0;
1247 RealType da = alpha_par - alpha_perp;
1249 for (
int p = 0; p < 3; ++p) {
1250 a(p, p) = (alpha_perp + da * ohUnit[p] * ohUnit[p]) * scale;
1251 for (
int q = p+1; q < 3; ++q) {
1252 a(p, q) = da * ohUnit[p] * ohUnit[q] * scale;
1271 void SFG::writeCorrelate() {
1278 std::string tcfName = outputFilename_ +
".tcf";
1279 std::ofstream tcf(tcfName.c_str());
1280 if (!tcf.is_open()) {
1281 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1282 "SFG::writeCorrelate: cannot open %s\n", tcfName.c_str());
1283 painCave.isFatal = 1; simError();
1285 tcf <<
"# SFG time-domain TCFs\n";
1286 tcf <<
"# OpenMD " << r.getFullRevision() <<
"\n";
1287 tcf <<
"# " << r.getBuildDate() <<
"\n";
1288 tcf <<
"# selection: \"" << selectionScript1_ <<
"\"\n";
1289 tcf <<
"# navg = " << navg_ <<
" ncor = " << nTimeBins_ <<
"\n";
1290 tcf <<
"#t(ps)\tRe_ssp\tIm_ssp\tRe_ppp\tIm_ppp\tRe_sps\tIm_sps\n";
1292 for (
int tt = 0; tt < static_cast<int>(nTimeBins_); ++tt) {
1293 tcf << tt * dt_ps_ <<
"\t"
1294 << tcf_ssp_[tt].real() <<
"\t" << tcf_ssp_[tt].imag() <<
"\t"
1295 << tcf_ppp_[tt].real() <<
"\t" << tcf_ppp_[tt].imag() <<
"\t"
1296 << tcf_sps_[tt].real() <<
"\t" << tcf_sps_[tt].imag() <<
"\n";
1300#if defined(HAVE_FFTW3_H)
1302 const double ps_to_invcm = 33.3564;
1305 const std::vector<std::complex<double>>* pTCF = &tcf_ssp_;
1306 if (polarization_ ==
"ppp") pTCF = &tcf_ppp_;
1307 else if (polarization_ ==
"sps") pTCF = &tcf_sps_;
1312 int N_corr =
static_cast<int>(nTimeBins_);
1314 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1315 "SFG::writeCorrelate: too few TCF points (%d) for FFT.\n", N_corr);
1316 painCave.isFatal = 1; simError();
1322 int N_zf = (t_zerofill_ps_ > 0.0)
1323 ?
static_cast<int>(std::ceil((
static_cast<double>(N_corr)
1324 + t_zerofill_ps_ / dt_ps_)))
1326 N_zf = std::max(N_zf, 2 * N_corr);
1329 while (N_fft < N_zf) N_fft <<= 1;
1331 fftw_complex* in = (fftw_complex*)fftw_malloc(
sizeof(fftw_complex)*N_fft);
1332 fftw_complex* out = (fftw_complex*)fftw_malloc(
sizeof(fftw_complex)*N_fft);
1334 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1335 "SFG: FFTW malloc failed.\n");
1336 painCave.isFatal = 1; simError();
1340 std::vector<std::complex<double>> apodTCF(N_corr);
1341 for (
int i = 0; i < N_corr; ++i) {
1342 double t =
static_cast<double>(i) * dt_ps_;
1343 double w = (t_apod_ps_ > 0.0) ? std::exp(-t / t_apod_ps_) : 1.0;
1344 apodTCF[i] = (*pTCF)[i] * w;
1348 std::vector<std::complex<double>> spectrum(N_fft);
1350 fftw_plan plan = fftw_plan_dft_1d(N_fft, in, out,
1351 FFTW_FORWARD, FFTW_ESTIMATE);
1379 for (
int i = 0; i < N_fft; ++i) { in[i][0] = 0.0; in[i][1] = 0.0; }
1380 for (
int i = 0; i < N_corr; ++i) {
1381 in[i][0] = apodTCF[i].real();
1382 in[i][1] = apodTCF[i].imag();
1385 in[N_fft - i][0] = apodTCF[i].real();
1386 in[N_fft - i][1] = -apodTCF[i].imag();
1391 for (
int i = 0; i < N_fft; ++i)
1392 spectrum[i].imag(out[i][0]);
1396 for (
int i = 0; i < N_fft; ++i) { in[i][0] = 0.0; in[i][1] = 0.0; }
1397 for (
int i = 0; i < N_corr; ++i) {
1398 double re_ic = apodTCF[i].imag();
1399 double im_ic = -apodTCF[i].real();
1404 in[N_fft - i][0] = re_ic;
1405 in[N_fft - i][1] = -im_ic;
1410 for (
int i = 0; i < N_fft; ++i)
1411 spectrum[i].real(-out[i][0]);
1413 fftw_destroy_plan(plan);
1420 const double norm = 1.0 / (2.0 *
static_cast<double>(N_corr));
1421 for (
int i = 0; i < N_fft; ++i)
1422 spectrum[i] *= norm;
1425 const double dOmega = ps_to_invcm / (
static_cast<double>(N_fft) * dt_ps_);
1426 const double dOmega_res = ps_to_invcm / (
static_cast<double>(N_corr) * dt_ps_);
1430 if (info_->getSimParams()->haveTargetTemp())
1431 T = info_->getSimParams()->getTargetTemp();
1432 const double kT_invcm = 0.6950356 *
static_cast<double>(T);
1434 std::ofstream ofs(outputFilename_.c_str());
1435 if (!ofs.is_open()) {
1436 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1437 "SFG: cannot open %s\n", outputFilename_.c_str());
1438 painCave.isFatal = 1; simError();
1441 ofs <<
"# SFG spectrum (" << polarization_ <<
")\n";
1442 ofs <<
"# OpenMD " << r.getFullRevision() <<
"\n";
1443 ofs <<
"# " << r.getBuildDate() <<
"\n";
1444 ofs <<
"# selection: \"" << selectionScript1_ <<
"\"\n";
1445 ofs <<
"# T = " << T <<
" K, kT = " << kT_invcm <<
" cm-1\n";
1446 ofs <<
"# wAvg = " << wAvg_ <<
" cm-1\n";
1447 ofs <<
"# navg = " << navg_ <<
" nTimeBins = " << nTimeBins_ <<
"\n";
1448 ofs <<
"# intrinsic resolution = " << dOmega_res <<
" cm-1";
1449 if (t_apod_ps_ > 0.0)
1450 ofs <<
" t_apod = " << t_apod_ps_ <<
" ps"
1451 <<
" (added broadening ~"
1452 << ps_to_invcm / (M_PI * t_apod_ps_) <<
" cm-1)";
1454 ofs <<
"# dOmega = " << dOmega <<
" cm-1";
1455 if (t_zerofill_ps_ > 0.0)
1456 ofs <<
" (zero-filled: N_corr=" << N_corr
1457 <<
" -> N_fft=" << N_fft <<
")";
1459 ofs <<
"#omega(cm-1)\tRe(chi2)\tIm(chi2)\t|chi2|^2\n";
1463 const double halfBW_freq = (N_corr / 2) * dOmega_res;
1464 const int halfBW =
static_cast<int>(std::round(halfBW_freq / dOmega));
1466 auto writeRow = [&](
double omega,
int k) {
1467 if (omega <= 0.0)
return;
1468 double re = spectrum[k].real();
1469 double im = spectrum[k].imag();
1470 double x = omega / kT_invcm;
1471 double qCorr = (x > 1.0e-6) ?
1472 ((x < 50.0) ? x / (1.0 - std::exp(-x)) : x) : 1.0;
1475 ofs << omega <<
"\t" << re <<
"\t" << im <<
"\t"
1476 << re*re + im*im <<
"\n";
1480 for (
int k = N_fft - halfBW; k < N_fft; ++k)
1481 writeRow(wAvg_ + (k - N_fft) * dOmega, k);
1483 for (
int k = 0; k <= halfBW; ++k)
1484 writeRow(wAvg_ + k * dOmega, k);
1491 snprintf(painCave.errMsg, MAX_SIM_ERROR_MSG_LENGTH,
1492 "SFG: FFTW3 required for SFG spectra.\n");
1493 painCave.isFatal = 1; simError();
Computes eigenvalues and eigenvectors of a real (non-complex) matrix.
Rectangular matrix class with contiguous flat storage.
Real * getArrayPointer()
Returns a pointer to the contiguous internal storage (row-major).
Dynamically-sized vector class.
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
void normalize()
Normalizes this vector in place.
Real length() const
Returns the length of this vector.
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.
std::string getPrefix(const std::string &str)