| 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 |