GCC Code Coverage Report


Directory: ./
File: src/simulation.cpp
Date: 2024-04-18 12:22:13
Exec Total Coverage
Lines: 72 221 32.6%
Functions: 5 12 41.7%
Branches: 69 162 42.6%

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