Loading...
Searching...
No Matches
AMS_IO.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12
15
16#include <cstddef>
17#include <cstring>
18#include <format>
19#include <fstream>
20#include <iostream>
21#include <iterator>
22#include <stdexcept>
23#include <string>
24#ifndef _WIN32
25#include <sys/stat.h>
26#include <unistd.h>
27#endif
28
29namespace {
30
31// The driver script and its output, both relative to the working directory.
32constexpr const char *kRunScript = "run_AMS_IO.sh";
33constexpr const char *kOutputFile = "ams_output";
34// Truncating rather than appending keeps the previous call's block out of the
35// parse, so a failed run cannot hand back stale forces.
36constexpr const char *kRunCommand = "./run_AMS_IO.sh > ams_output";
37
38// Lines that precede the two blocks of interest in the AMS output.
39constexpr const char *kEnergyMarker = " CALCULATION RESULTS";
40constexpr const char *kGradientMarker =
41 " Index Atom d/dx d/dy d/dz";
42
43constexpr double kHartreeToEv = 27.2114;
44constexpr double kHartreeBohrToEvAngstrom = 51.4220862;
45
46} // namespace
47
56
57void AMS_IO::cleanMemory(void) { return; }
58
60
61namespace {
62
63const char *elementArray[] = {
64 "Unknown", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na",
65 "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V",
66 "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br",
67 "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag",
68 "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr",
69 "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu",
70 "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi",
71 "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", NULL};
72
73// guess the atom type from the atomic mass,
74std::string mass2atom(double atomicmass) {
75 return elementArray[int(atomicmass + .5)];
76}
77
78int symbol2atomicNumber(char const *symbol) {
79 int i = 0;
80
81 while (elementArray[i] != NULL) {
82 if (strcmp(symbol, elementArray[i]) == 0) {
83 return i;
84 }
85 i++;
86 }
87 // invalid symbol
88 return -1;
89}
90
91char const *atomicNumber2symbol(int n) {
92 // The trailing NULL terminates the table, so it bounds the valid range.
93 if (n < 0 || static_cast<std::size_t>(n) + 1 >= std::size(elementArray)) {
94 throw std::runtime_error(
95 std::format("AMS_IO knows no element symbol for atomic number {}", n));
96 }
97 return elementArray[n];
98}
99} // namespace
100
101void AMS_IO::force(long N, const double *R, const int *atomicNrs, double *F,
102 double *U, double *variance, const double *box) {
103 variance = nullptr;
104 passToSystem(N, R, atomicNrs, box);
105 // Run a single point AMS_IO calculation and write the results into
106 // ams_output
107 eonc::pot::runOrThrow(kRunCommand);
108 recieveFromSystem(N, F, U);
109 return;
110}
111
112void AMS_IO::passToSystem(long N, const double *R, const int *atomicNrs,
113 const double *box)
114// Creating the standard input file that will be read by the AMS_IO driver
115{
116 std::ofstream out(kRunScript, std::ios::trunc);
117 if (!out) {
118 throw std::runtime_error(
119 std::format("Could not open {} for writing", kRunScript));
120 }
121
122 out << "#!/bin/sh\n";
123 out << "ams --delete-old-results <<eor\n";
124 out << "Task SinglePoint\n";
125 out << "System\n";
126 out << " Atoms\n";
127 for (long i = 0; i < N; i++) {
128 out << std::format(" {}\t{:.19f}\t{:.19f}\t{:.19f}\n",
129 atomicNumber2symbol(atomicNrs[i]), R[i * 3 + 0],
130 R[i * 3 + 1], R[i * 3 + 2]);
131 }
132 out << " End\n";
133 if (!model.empty() || !forcefield.empty()) {
134 out << " Lattice\n";
135 for (int i = 0; i < 3; i++) {
136 out << std::format(" {:.19f}\t{:.19f}\t{:.19f}\n", box[i * 3 + 0],
137 box[i * 3 + 1], box[i * 3 + 2]);
138 }
139 out << " End\n";
140 }
141 out << "End\n";
142 out << std::format("Engine {}\n", engine);
143 if (!forcefield.empty()) {
144 out << std::format(" Forcefield {}\n", forcefield);
145 }
146 if (!model.empty()) {
147 out << std::format(" Model {}\n", model);
148 }
149 if (!xc.empty()) {
150 out << std::format("xc {}\n", xc);
151 // basis set not specified (default = DZ)
152 out << std::format(" hybrid {}\n", xc);
153 out << "end\n";
154 }
155 out << "EndEngine\n";
156 out << "Properties\n";
157 out << " Gradients\n";
158 out << "End\n";
159 out << "eor";
160
161 out.close();
162 if (!out) {
163 throw std::runtime_error(
164 std::format("Could not write the AMS input to {}", kRunScript));
165 }
166
167#ifndef _WIN32
168 if (chmod(kRunScript, S_IRWXU) != 0) {
169 throw std::runtime_error(
170 std::format("Could not make {} executable", kRunScript));
171 }
172#endif
173 return;
174}
175
176void AMS_IO::recieveFromSystem(long N, double *F, double *U) {
177 std::ifstream in(kOutputFile);
178 if (!in) {
179 throw std::runtime_error(
180 std::format("Could not open {}; AMS left no output", kOutputFile));
181 }
182
183 bool haveEnergy = false;
184 bool haveGradients = false;
185 std::string line;
186
187 while (std::getline(in, line)) {
188
189 if (line == kEnergyMarker) { // Finding the Energy in the output file
190 std::string junk;
191 if (!(in >> junk >> junk >> junk >> *U)) {
192 throw std::runtime_error(
193 std::format("Could not read the energy following \"{}\" in {}",
194 kEnergyMarker, kOutputFile));
195 }
196 *U = *U * kHartreeToEv; // Energy in hartree to eV
197 haveEnergy = true;
198 }
199
200 if (line == kGradientMarker) { // Finding the forces
201 double index;
202 std::string symbol;
203 for (long i = 0; i < N; i++) {
204 if (!(in >> index >> symbol >> F[i * 3 + 0] >> F[i * 3 + 1] >>
205 F[i * 3 + 2])) {
206 throw std::runtime_error(
207 std::format("{} holds gradients for {} atoms, expected {}",
208 kOutputFile, i, N));
209 }
210 // AMS_IO gives gradients, not forces, hence the change.
211 F[i * 3 + 0] = -F[i * 3 + 0];
212 F[i * 3 + 1] = -F[i * 3 + 1];
213 F[i * 3 + 2] = -F[i * 3 + 2];
214 }
215 haveGradients = true;
216 }
217 }
218
219 if (!haveEnergy || !haveGradients) {
220 throw std::runtime_error(std::format(
221 "{} holds no {}", kOutputFile,
222 !haveEnergy
223 ? (!haveGradients ? "energy and no gradient block" : "energy")
224 : "gradient block"));
225 }
226
227 for (long i = 0; i < 3 * N; i++) {
228 F[i] = F[i] * kHartreeBohrToEvAngstrom; // Forces from hartree/bohr to
229 // eV/Angstrom
230 }
231 return;
232}
~AMS_IO()
Definition AMS_IO.cpp:59
std::string forcefield
Definition AMS_IO.h:37
std::string model
Definition AMS_IO.h:36
std::string engine
Definition AMS_IO.h:35
AMS_IO(const Parameters &p)
Definition AMS_IO.cpp:48
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box)
Definition AMS_IO.cpp:101
void recieveFromSystem(long N, double *F, double *U)
Definition AMS_IO.cpp:176
void cleanMemory(void)
Definition AMS_IO.cpp:57
std::string xc
Definition AMS_IO.h:38
void passToSystem(long N, const double *R, const int *atomicNrs, const double *box)
Definition AMS_IO.cpp:112
struct eonc::Parameters::ams_options_t ams_options
Potential(PotType a_ptype)
Definition Potential.h:35
void runOrThrow(const std::string &command)
Run a shell command, throwing when it does not succeed.