GCC Code Coverage Report


Directory: ./
File: src/simulation.cpp
Date: 2025-02-03 10:58:24
Exec Total Coverage
Lines: 72 225 32.0%
Functions: 5 12 41.7%
Branches: 69 163 42.3%

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