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