Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <cstdlib> | ||
2 | #include <exception> | ||
3 | #include <iomanip> | ||
4 | |||
5 | #include "graphics.hpp" | ||
6 | #include "integrator.hpp" | ||
7 | #include "observables.hpp" | ||
8 | #include "output.hpp" | ||
9 | #include "simulation.hpp" | ||
10 | #include "system.hpp" | ||
11 | #include "timeseries.hpp" | ||
12 | #include "trajectories.hpp" | ||
13 | #include "types.hpp" | ||
14 | |||
15 | ✗ | static void rewindCout() { | |
16 | ✗ | std::cout << "\33[2K\33[1A"; | |
17 | ✗ | std::cout.flush(); | |
18 | } | ||
19 | |||
20 | ✗ | void Simulation::signalHandler(int signum) { | |
21 | ✗ | rewindCout(); | |
22 | ✗ | std::cout.setstate(std::ios_base::failbit); | |
23 | ✗ | std::clog << "\nSignal " << signum << " received."; | |
24 | ✗ | switch (signum) { | |
25 | ✗ | case SIGTERM: | |
26 | ✗ | case SIGQUIT: | |
27 | ✗ | std::clog << " Aborting run!"; | |
28 | ✗ | signalFlags |= SIGNAL_ABORT; | |
29 | ✗ | break; | |
30 | ✗ | case SIGINT: | |
31 | ✗ | std::clog << " Skipping current stage."; | |
32 | ✗ | signalFlags |= SIGNAL_SKIP; | |
33 | ✗ | break; | |
34 | ✗ | case SIGUSR1: | |
35 | ✗ | std::clog << " Writing data."; | |
36 | ✗ | signalFlags |= SIGNAL_WRITE; | |
37 | ✗ | break; | |
38 | ✗ | case SIGUSR2: | |
39 | ✗ | std::clog << " Toggling graphics output."; | |
40 | ✗ | signalFlags |= SIGNAL_GRAPHICS; | |
41 | ✗ | break; | |
42 | } | ||
43 | ✗ | std::clog.flush(); | |
44 | } | ||
45 | |||
46 | 2 | Simulation::Simulation(const std::filesystem::path &outputPath, const std::string &outputFormat, | |
47 | 2 | int autosaveMinutes, bool graphics) | |
48 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | : stageCounter(0), outputPath(outputPath), outputFormat(outputFormat), |
49 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
4 | autosaveSeconds(autosaveMinutes * 60), lastSave(time(0)) { |
50 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (graphics) { |
51 | ✗ | signalFlags |= SIGNAL_GRAPHICS; | |
52 | } | ||
53 | 2 | } | |
54 | |||
55 | ✗ | int Simulation::operator()(const std::filesystem::path &configFile, bool dryrun) { | |
56 | ✗ | this->dryrun = dryrun; | |
57 | ✗ | int exitCode = EXIT_SUCCESS; | |
58 | ✗ | try { | |
59 | ✗ | init(configFile); | |
60 | ✗ | run(); | |
61 | ✗ | } catch (const std::exception &e) { | |
62 | ✗ | std::cout.flush(); | |
63 | ✗ | std::clog.flush(); | |
64 | ✗ | std::clog << std::endl << "\nERROR:\n" << e.what() << std::endl; | |
65 | ✗ | exitCode = EXIT_FAILURE; | |
66 | } | ||
67 | ✗ | return exitCode; | |
68 | } | ||
69 | |||
70 | ✗ | void Simulation::init(const std::filesystem::path &configFile) { init(YAML::LoadFile(configFile)); } | |
71 | |||
72 | 1 | void Simulation::init(const YAML::Node &config) { | |
73 | 1 | this->config = config; | |
74 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (outputFormat == "H5MD") { |
75 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | output = new H5MDOutput(outputPath, dryrun); |
76 | ✗ | } else if (outputFormat == "ASCII") { | |
77 | ✗ | output = new ASCIIOutput(outputPath, dryrun); | |
78 | } else { | ||
79 | ✗ | throw std::invalid_argument("Invalid output format " + outputFormat); | |
80 | } | ||
81 | 1 | output->emitConfig(config); | |
82 | |||
83 |
5/8✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
|
5 | for (const auto &c : config["Stages"]) { |
84 |
3/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
|
2 | if (c["loop"]) { |
85 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | auto &cLoop = c["loop"]; |
86 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | int times = YAML::parse<int>(cLoop, "times"); |
87 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
|
11 | for (int i = 0; i < times; ++i) { |
88 |
6/10✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 10 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 10 times.
✓ Branch 14 taken 10 times.
|
30 | for (auto &cLoop : cLoop["stages"]) { |
89 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | stageNodes.push_back(cLoop); |
90 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
30 | } |
91 | } | ||
92 | 1 | } else { | |
93 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | stageNodes.push_back(c); |
94 | } | ||
95 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
4 | } |
96 | 1 | std::clog << "\nTotal number of stages: " << stageNodes.size(); | |
97 | |||
98 | #ifndef NO_OMP | ||
99 | 1 | std::clog << "\nMax OpenMP threads: " << omp_get_max_threads(); | |
100 | #endif | ||
101 | |||
102 | 1 | signal(SIGTERM, signalHandler); | |
103 | 1 | signal(SIGQUIT, signalHandler); | |
104 | 1 | signal(SIGINT, signalHandler); | |
105 | 1 | signal(SIGUSR2, signalHandler); | |
106 | 1 | signal(SIGUSR1, signalHandler); | |
107 | 1 | } | |
108 | |||
109 | 3 | void Simulation::clearStage() { | |
110 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | delete system; |
111 | 3 | system = nullptr; | |
112 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | delete integrator; |
113 | 3 | integrator = nullptr; | |
114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | delete trajectories; |
115 | 3 | trajectories = nullptr; | |
116 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | delete timeseries; |
117 | 3 | timeseries = nullptr; | |
118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | for (Observables *o : observables) { |
119 | ✗ | delete o; | |
120 | } | ||
121 | 3 | observables.clear(); | |
122 | 3 | } | |
123 | |||
124 | 1 | void Simulation::initStage() { | |
125 | 1 | clearStage(); | |
126 | 1 | std::clog << "\n- Stage " << stageCounter << " -"; | |
127 | 1 | const auto &stageNode = stageNodes[stageCounter]; | |
128 | // first casting to double for exponential notation | ||
129 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | int steps = (int)(YAML::parse<double>(stageNode, "steps", -1)); |
130 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | double time = YAML::parse<double>(stageNode, "time", -1); |
131 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
3 | std::clog << "\nsteps: " << (steps > 0 ? std::to_string(steps) : "not specified"); |
132 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
2 | std::clog << "\ntime: " << (time > 0 ? std::to_string(time) : "not specified"); |
133 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | auto &configSystem = stageNode["System"] ? stageNode["System"] : config["System"]; |
134 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (stageCounter == 0) { |
135 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
1 | system = new System(particles, configSystem, 0, config["InitParticles"]); |
136 | } else { | ||
137 | ✗ | system = new System(particles, configSystem, stageStats[stageCounter - 1].tAfter, | |
138 | ✗ | stageNode["AddParticles"]); | |
139 | } | ||
140 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | auto &configIntegrator = stageNode["Integrator"] ? stageNode["Integrator"] : config["Integrator"]; |
141 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | integrator = IntegratorFactory::create(system, configIntegrator); |
142 | 1 | auto &configTrajectories = | |
143 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | stageNode["Trajectories"] ? stageNode["Trajectories"] : config["Trajectories"]; |
144 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | auto &configTimeseries = stageNode["Timeseries"] ? stageNode["Timeseries"] : config["Timeseries"]; |
145 | 1 | auto &configObservables = | |
146 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
|
1 | stageNode["Observables"] ? stageNode["Observables"] : config["Observables"]; |
147 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (configTrajectories) { |
148 | ✗ | if (outputFormat == "ASCII") { | |
149 | ✗ | throw std::invalid_argument("Use output format H5MD for particle trajectories."); | |
150 | } | ||
151 | ✗ | trajectories = new Trajectories(configTrajectories); | |
152 | ✗ | std::clog << "\nTrajectories:"; | |
153 | ✗ | for (auto &q : trajectories->quantities) { | |
154 | ✗ | std::clog << " " << q; | |
155 | } | ||
156 | } | ||
157 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (configTimeseries) { |
158 | ✗ | timeseries = new Timeseries(system, integrator, configTimeseries); | |
159 | ✗ | std::clog << "\nTimeseries:"; | |
160 | ✗ | for (std::string &k : timeseries->keys) { | |
161 | ✗ | std::clog << " " << k; | |
162 | } | ||
163 | } | ||
164 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | observables = {}; |
165 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (configObservables) { |
166 | ✗ | for (auto &oc : configObservables) { | |
167 | ✗ | observables.push_back( | |
168 | ✗ | ObservablesFactory::create(system, oc.first.as<std::string>(), oc.second)); | |
169 | } | ||
170 | ✗ | std::clog << "\nObservables:"; | |
171 | ✗ | for (auto &o : observables) { | |
172 | ✗ | std::clog << "\n\t" << o->type() << ":"; | |
173 | ✗ | for (std::string &k : o->keys) { | |
174 | ✗ | std::clog << " " << k; | |
175 | } | ||
176 | ✗ | auto requiredAnalyzers = o->analyzers(); | |
177 | ✗ | for (const std::string &a : requiredAnalyzers) { | |
178 | ✗ | system->ensureAnalyzer(a); | |
179 | } | ||
180 | } | ||
181 | } | ||
182 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | stageStats.push_back({ |
183 | .stepsGoal = steps, | ||
184 | .timeGoal = time, | ||
185 | }); | ||
186 | 1 | } | |
187 | |||
188 | ✗ | Simulation::StageReturn Simulation::doStage() { | |
189 | ✗ | StageReturn stageReturn = STAGE_RETURN_NORMAL; | |
190 | ✗ | StageStats &s = stageStats[stageCounter]; | |
191 | ✗ | std::clog << "\nWriting initial particle configuration... "; | |
192 | ✗ | output->writeParticles(particles, std::to_string(stageCounter) + "_particlesInit.dat"); | |
193 | ✗ | std::clog << "done"; | |
194 | ✗ | std::clog << "\nBegin stage " << stageCounter; | |
195 | ✗ | std::clog.flush(); | |
196 | ✗ | s.walltimeBefore = time(0); | |
197 | ✗ | s.tBefore = system->t; | |
198 | ✗ | system->updateState(); | |
199 | ✗ | std::cout << "\n"; | |
200 | ✗ | while (!s.done) { | |
201 | ✗ | std::cout << "\rStep " << s.stepsMade; | |
202 | ✗ | if (s.stepsGoal > 0) { | |
203 | ✗ | std::cout << " (goal: " << s.stepsGoal << ")"; | |
204 | } | ||
205 | ✗ | std::ios coutStateBefore(nullptr); | |
206 | ✗ | coutStateBefore.copyfmt(std::cout); | |
207 | ✗ | std::cout << ", t: " << std::setprecision(5) << std::fixed << s.timeMade; | |
208 | ✗ | std::cout.copyfmt(coutStateBefore); | |
209 | ✗ | if (s.timeGoal > 0) { | |
210 | ✗ | std::cout << " (goal: " << s.timeGoal << ")"; | |
211 | } | ||
212 | ✗ | if (graphics) { | |
213 | ✗ | graphics->draw(); | |
214 | } | ||
215 | ✗ | if (trajectories && system->t >= trajectories->tLast + trajectories->every - 10e-10) { | |
216 | ✗ | output->writeTrajectoryFrame(trajectories, system, stageCounter); | |
217 | ✗ | trajectories->count++; | |
218 | ✗ | trajectories->tLast = system->t; | |
219 | } | ||
220 | ✗ | integrator->step(); | |
221 | ✗ | system->updateAnalyzers(); | |
222 | ✗ | if (timeseries) { | |
223 | ✗ | timeseries->record(); | |
224 | } | ||
225 | ✗ | for (auto &o : observables) { | |
226 | ✗ | o->sample(integrator->weight()); | |
227 | } | ||
228 | ✗ | s.stepsMade++; | |
229 | ✗ | s.tAfter = system->t; | |
230 | ✗ | s.timeMade = s.tAfter - s.tBefore; | |
231 | ✗ | s.walltimeAfter = time(0); | |
232 | ✗ | s.done = (s.stepsGoal > 0 && s.stepsMade >= s.stepsGoal) || | |
233 | ✗ | (s.timeGoal > 0 && s.timeMade >= s.timeGoal); | |
234 | ✗ | if (graphics) { | |
235 | ✗ | graphics->printStatusAndWait(); | |
236 | } | ||
237 | ✗ | if (autosaveSeconds && s.walltimeAfter - lastSave > autosaveSeconds) { | |
238 | ✗ | write(); | |
239 | } | ||
240 | ✗ | if (signalFlags) { | |
241 | ✗ | if (signalFlags & SIGNAL_ABORT) { | |
242 | ✗ | signalFlags ^= SIGNAL_ABORT; | |
243 | ✗ | stageReturn = STAGE_RETURN_ABORT; | |
244 | } | ||
245 | ✗ | if (signalFlags & SIGNAL_SKIP) { | |
246 | ✗ | signalFlags ^= SIGNAL_SKIP; | |
247 | ✗ | stageReturn = STAGE_RETURN_SKIP; | |
248 | } | ||
249 | ✗ | if (signalFlags & SIGNAL_GRAPHICS) { | |
250 | ✗ | signalFlags ^= SIGNAL_GRAPHICS; | |
251 | ✗ | delete graphics; | |
252 | ✗ | graphics = new Graphics(this, config["Graphics"]); | |
253 | } | ||
254 | ✗ | if (signalFlags & SIGNAL_WRITE) { | |
255 | ✗ | signalFlags ^= SIGNAL_WRITE; | |
256 | ✗ | write(); | |
257 | } | ||
258 | ✗ | if (stageReturn == STAGE_RETURN_ABORT || stageReturn == STAGE_RETURN_SKIP) { | |
259 | break; | ||
260 | } | ||
261 | } | ||
262 | ✗ | std::cout << "\33[K"; // clear rest of line | |
263 | ✗ | if (std::cout.fail()) { | |
264 | ✗ | std::cout.clear(); | |
265 | ✗ | std::cout << "\n"; | |
266 | } | ||
267 | } | ||
268 | ✗ | rewindCout(); | |
269 | ✗ | int totalStepsMade = | |
270 | ✗ | std::accumulate(stageStats.begin(), stageStats.end(), 0, | |
271 | ✗ | [&](int sum, const StageStats &s) -> int { return sum + s.stepsMade; }); | |
272 | ✗ | std::clog << "\nFinished stage " << stageCounter << " after " << s.stepsMade << " steps and " | |
273 | ✗ | << s.timeMade << " time units (steps total: " << totalStepsMade | |
274 | ✗ | << ", time total: " << system->t << ")"; | |
275 | ✗ | std::clog.flush(); | |
276 | ✗ | return stageReturn; | |
277 | } | ||
278 | |||
279 | ✗ | void Simulation::run() { | |
280 | ✗ | for (stageCounter = 0; stageCounter < stageNodes.size(); stageCounter++) { | |
281 | ✗ | initStage(); | |
282 | ✗ | if (dryrun) { | |
283 | ✗ | continue; | |
284 | } | ||
285 | ✗ | StageReturn stageReturn = doStage(); | |
286 | ✗ | write(); | |
287 | ✗ | if (stageReturn == STAGE_RETURN_ABORT) { | |
288 | break; | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | ✗ | void Simulation::write() { | |
294 | ✗ | std::clog << "\nStep " << stageStats[stageCounter].stepsMade << ": Writing to files... "; | |
295 | ✗ | output->write(this); | |
296 | ✗ | lastSave = stageStats[stageCounter].walltimeAfter; | |
297 | ✗ | std::clog << "done"; | |
298 | ✗ | std::clog.flush(); | |
299 | } | ||
300 | |||
301 | 2 | Simulation::~Simulation() { | |
302 | 2 | clearStage(); | |
303 | 2 | std::clog << "\nBye!" << std::endl; | |
304 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | delete output; |
305 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | delete graphics; |
306 | 4 | } | |
307 |