simulation-editor.cpp 12.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
// Copyright (c) 2020 INRA Distributed under the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifdef _WIN32
#define NOMINMAX
#define WINDOWS_LEAN_AND_MEAN
#endif

Gauthier Quesnel's avatar
Gauthier Quesnel committed
10
11
#include <cstdlib>

12
13
14
15
16
#include "gui.hpp"
#include "imnodes.hpp"
#include "implot.h"
#include "node-editor.hpp"

17
#include <chrono>
18
#include <fstream>
19
#include <filesystem>
20
21
22
#include <string>

#include <fmt/format.h>
23
24
#include <fmt/ostream.h>

25
26
27
28
#include <irritator/core.hpp>
#include <irritator/io.hpp>

namespace irt {
29
30

void
31
plot_output::operator()(const irt::observer& obs,
32
                        const irt::dynamics_type /*type*/,
33
34
                        const irt::time t,
                        const irt::observer::status s)
35
{
36
37
38
39
40
41
42
43
44
45
46
    switch (s) {
    case irt::observer::status::initialize:
        xs.clear();
        ys.clear();
        xs.reserve(4096u);
        ys.reserve(4096u);
        tl = t;
        break;
    case irt::observer::status::run: {
        const float value = static_cast<float>(obs.msg[0]);

47
        for (auto to_fill = tl; to_fill < t; to_fill += time_step) {
48
49
50
            ys.emplace_back(value);
            xs.emplace_back(static_cast<float>(t));
        }
51

52
53
54
55
56
57
58
        tl = t;
    } break;
    case irt::observer::status::finalize:
        ys.emplace_back(static_cast<float>(obs.msg[0]));
        xs.emplace_back(static_cast<float>(t));
        break;
    }
59
}
60

61
void
62
file_output::operator()(const irt::observer& obs,
63
                        const irt::dynamics_type /*type*/,
64
65
                        const irt::time t,
                        const irt::observer::status s)
66
{
67
    std::filesystem::path file;
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    switch (s) {
    case irt::observer::status::initialize:
        tl = t;

        if (ed && !ed->observation_directory.empty())
            file = ed->observation_directory;

        file.append(obs.name.begin());
        file.replace_extension(".dat");

        ofs.open(file);
        if (ofs.is_open())
            fmt::print(ofs, "t,{}\n", name.c_str());
        break;
    case irt::observer::status::run:
        if (ofs.is_open())
            fmt::print(ofs, "{},{}\n", t, obs.msg[0]);
        tl = t;
        break;
    case irt::observer::status::finalize:
        if (ofs.is_open()) {
            fmt::print(ofs, "{},{}\n", t, obs.msg[0]);
            ofs.close();
        }
        break;
94
95
96
    }
}

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
static void
run_synchronized_simulation(window_logger& log_w,
                            simulation& sim,
                            double begin,
                            double end,
                            double synchronize_timestep,
                            double& current,
                            editor_status& st,
                            status& sim_st,
                            const bool& stop) noexcept
{
    current = begin;
    st = editor_status::initializing;
    if (sim_st = sim.initialize(current); irt::is_bad(sim_st)) {
        log_w.log(3,
                  "Simulation initialization failure (%s)\n",
                  irt::status_string(sim_st));
        st = editor_status::editing;
        return;
    }

    st = editor_status::running_thread;
    do {
        const double old = current;

        if (sim_st = sim.run(current); irt::is_bad(sim_st)) {
            log_w.log(
              3, "Simulation failure (%s)\n", irt::status_string(sim_st));
            st = editor_status::editing;
            return;
        }

        const auto duration = (current - old) / synchronize_timestep;
        if (duration > 0.)
            std::this_thread::sleep_for(
              std::chrono::duration<double>(duration));
    } while (current < end && !stop);

135
136
    sim.finalize(end);

137
138
139
    st = editor_status::running_thread_need_join;
}

140
141
142
143
144
145
static void
run_simulation(window_logger& log_w,
               simulation& sim,
               double begin,
               double end,
               double& current,
146
               editor_status& st,
147
               status& sim_st,
148
149
150
               const bool& stop) noexcept
{
    current = begin;
151
152
    st = editor_status::initializing;
    if (sim_st = sim.initialize(current); irt::is_bad(sim_st)) {
153
154
        log_w.log(3,
                  "Simulation initialization failure (%s)\n",
155
156
                  irt::status_string(sim_st));
        st = editor_status::editing;
157
158
159
        return;
    }

160
    st = editor_status::running_thread;
161
    do {
162
163
164
165
        if (sim_st = sim.run(current); irt::is_bad(sim_st)) {
            log_w.log(
              3, "Simulation failure (%s)\n", irt::status_string(sim_st));
            st = editor_status::editing;
166
167
168
169
            return;
        }
    } while (current < end && !stop);

170
171
    sim.finalize(end);

172
    st = editor_status::running_thread_need_join;
173
174
175
176
177
}

static void
show_simulation_run_once(window_logger& log_w, editor& ed)
{
178
    if (ed.st == editor_status::running_thread_need_join) {
179
180
        if (ed.simulation_thread.joinable()) {
            ed.simulation_thread.join();
181
            ed.st = editor_status::editing;
182
        }
183
184
    } else if (ed.st == editor_status::editing ||
               ed.st == editor_status::running_debug) {
185
186
        ImGui::Checkbox("Time synchronization", &ed.use_real_time);

187
        if (ed.use_real_time) {
188
189
            ImGui::InputDouble("t/s", &ed.synchronize_timestep);

190
            if (ed.synchronize_timestep > 0. && ImGui::Button("run")) {
191
                // initialize_observation(log_w, ed);
192
                ed.stop = false;
193

194
195
196
197
198
199
200
201
202
203
204
                ed.simulation_thread =
                  std::thread(&run_synchronized_simulation,
                              std::ref(log_w),
                              std::ref(ed.sim),
                              ed.simulation_begin,
                              ed.simulation_end,
                              ed.synchronize_timestep,
                              std::ref(ed.simulation_current),
                              std::ref(ed.st),
                              std::ref(ed.sim_st),
                              std::cref(ed.stop));
205
206
207
            }
        } else {
            if (ImGui::Button("run")) {
208
                // initialize_observation(log_w, ed);
209
210
                ed.stop = false;

211
212
213
214
215
216
217
218
219
220
                ed.simulation_thread =
                  std::thread(&run_simulation,
                              std::ref(log_w),
                              std::ref(ed.sim),
                              ed.simulation_begin,
                              ed.simulation_end,
                              std::ref(ed.simulation_current),
                              std::ref(ed.st),
                              std::ref(ed.sim_st),
                              std::cref(ed.stop));
221
            }
222
223
224
        }
    }

225
    if (ed.st == editor_status::running_thread) {
226
227
228
229
230
231
232
233
234
235
236
        ImGui::SameLine();
        if (ImGui::Button("Force stop"))
            ed.stop = true;
    }
}

static void
simulation_init(window_logger& log_w, editor& ed)
{
    ed.sim.clean();

237
    // initialize_observation(log_w, ed);
238
239

    ed.simulation_current = ed.simulation_begin;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
240
    ed.simulation_during_date = ed.simulation_begin;
241
    ed.st = editor_status::initializing;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
242

243
    ed.models_make_transition.resize(ed.sim.models.capacity(), false);
244

245
246
    if (ed.sim_st = ed.sim.initialize(ed.simulation_current);
        irt::is_bad(ed.sim_st)) {
247
248
        log_w.log(3,
                  "Simulation initialisation failure (%s)\n",
249
250
                  irt::status_string(ed.sim_st));
        ed.st = editor_status::editing;
251
252
253
254
    } else {
        ed.simulation_next_time = ed.sim.sched.empty()
                                    ? time_domain<time>::infinity
                                    : ed.sim.sched.tn();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
255
        ed.simulation_bag_id = 0;
256
        ed.st = editor_status::running_debug;
257
258
259
260
261
262
    }
}

static void
show_simulation_run_debug(window_logger& log_w, editor& ed)
{
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
    ImGui::Text("Current time %g", ed.simulation_current);
    ImGui::Text("Current bag %ld", ed.simulation_bag_id);
    ImGui::Text("Next time %g", ed.simulation_next_time);
    ImGui::Text("Model %lu", (unsigned long)ed.sim.sched.size());

    if (ImGui::Button("init."))
        simulation_init(log_w, ed);

    ImGui::SameLine();

    if (ImGui::Button("Next bag")) {
        if (ed.sim_st = ed.sim.run(ed.simulation_current); is_bad(ed.sim_st)) {
            ed.st = editor_status::editing;
            log_w.log(3,
                      "Simulation next bag failure (%s)\n",
                      irt::status_string(ed.sim_st));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
279
        }
280
281
        ++ed.simulation_bag_id;
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
282

283
284
285
286
287
288
289
290
291
292
293
    ImGui::SameLine();

    if (ImGui::Button("Bag >> 10")) {
        for (int i = 0; i < 10; ++i) {
            if (ed.sim_st = ed.sim.run(ed.simulation_current);
                is_bad(ed.sim_st)) {
                ed.st = editor_status::editing;
                log_w.log(3,
                          "Simulation next bag failure (%s)\n",
                          irt::status_string(ed.sim_st));
                break;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
294
            }
295
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
296
        }
297
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
298

299
300
301
302
303
304
305
306
307
308
309
    ImGui::SameLine();

    if (ImGui::Button("Bag >> 100")) {
        for (int i = 0; i < 100; ++i) {
            if (ed.sim_st = ed.sim.run(ed.simulation_current);
                is_bad(ed.sim_st)) {
                ed.st = editor_status::editing;
                log_w.log(3,
                          "Simulation next bag failure (%s)\n",
                          irt::status_string(ed.sim_st));
                break;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
310
            }
311
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
312
        }
313
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
314

315
    ImGui::InputDouble("##date", &ed.simulation_during_date);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
316

317
    ImGui::SameLine();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
318

319
320
321
322
323
324
325
326
327
328
    if (ImGui::Button("run##date")) {
        const auto end = ed.simulation_current + ed.simulation_during_date;
        while (ed.simulation_current < end) {
            if (ed.sim_st = ed.sim.run(ed.simulation_current);
                is_bad(ed.sim_st)) {
                ed.st = editor_status::editing;
                log_w.log(3,
                          "Simulation next bag failure (%s)\n",
                          irt::status_string(ed.sim_st));
                break;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
329
            }
330
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
331
        }
332
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
333

334
    ImGui::InputInt("##bag", &ed.simulation_during_bag);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
335

336
    ImGui::SameLine();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
337

338
339
340
341
342
343
344
345
346
    if (ImGui::Button("run##bag")) {
        for (int i = 0, e = ed.simulation_during_bag; i != e; ++i) {
            if (ed.sim_st = ed.sim.run(ed.simulation_current);
                is_bad(ed.sim_st)) {
                ed.st = editor_status::editing;
                log_w.log(3,
                          "Simulation next bag failure (%s)\n",
                          irt::status_string(ed.sim_st));
                break;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
347
            }
348
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
349
        }
350
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
351

352
353
354
355
    if (ed.st == editor_status::running_debug) {
        ed.simulation_next_time = ed.sim.sched.empty()
                                    ? time_domain<time>::infinity
                                    : ed.sim.sched.tn();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
356

357
        const auto& l = ed.sim.sched.list_model_id();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
358

359
360
361
362
        std::fill_n(ed.models_make_transition.begin(),
                    ed.models_make_transition.size(),
                    false);

363
364
365
366
        for (auto it = l.begin(), e = l.end(); it != e; ++it)
            ed.models_make_transition[get_index(*it)] = true;
    } else {
        ed.simulation_next_time = time_domain<time>::infinity;
367
368
369
370
    }
}

void
371
show_simulation_box(editor& ed, bool* show_simulation)
372
373
374
375
376
377
378
379
{
    ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);
    ImGui::SetNextWindowSize(ImVec2(250, 350), ImGuiCond_Once);
    if (!ImGui::Begin("Simulation", show_simulation)) {
        ImGui::End();
        return;
    }

380
381
382
    ImGui::InputDouble("Begin", &ed.simulation_begin);
    ImGui::InputDouble("End", &ed.simulation_end);
    ImGui::Checkbox("Show values", &ed.simulation_show_value);
383

384
385
    if (ImGui::Button("Output files"))
        ed.show_select_directory_dialog = true;
386

387
388
389
390
391
392
393
394
    ImGui::Text("output directory: ");
#if _WIN32
    ImGui::Text("%s", ed.observation_directory.u8string().c_str());
#else
    ImGui::Text("%s",
                reinterpret_cast<const char*>(
                  ed.observation_directory.u8string().c_str()));
#endif
395

396
397
    if (ImGui::CollapsingHeader("Simulation run one"))
        show_simulation_run_once(log_w, ed);
398

399
400
401
402
403
404
405
406
407
408
    if (ImGui::CollapsingHeader("Debug simulation"))
        show_simulation_run_debug(log_w, ed);

    if (ed.st != editor_status::editing) {
        ImGui::Text("Current: %g", ed.simulation_current);

        const double duration = ed.simulation_end - ed.simulation_begin;
        const double elapsed = ed.simulation_current - ed.simulation_begin;
        const double fraction = elapsed / duration;
        ImGui::ProgressBar(static_cast<float>(fraction));
409
410
411
412
413
414
    }

    ImGui::End();
}

} // namesapce irt