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
32
33
plot_output::operator()(const irt::observer& obs,
                        const irt::time t,
                        const irt::observer::status s)
34
{
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    switch (s) {
    case irt::observer::status::initialize:
        xs.clear();
        ys.clear();
        xs.reserve(4096u);
        ys.reserve(4096u);
        tl = t;
        min = -1.f;
        max = +1.f;
        break;
    case irt::observer::status::run: {
        const float value = static_cast<float>(obs.msg[0]);
        min = std::min(min, value);
        max = std::max(max, value);

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

55
56
57
58
59
60
61
        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;
    }
62
}
63

64
void
65
66
67
file_output::operator()(const irt::observer& obs,
                        const irt::time t,
                        const irt::observer::status s)
68
{
69
    std::filesystem::path file;
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    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;
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
135
136
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);

137
138
    sim.finalize(end);

139
140
141
    st = editor_status::running_thread_need_join;
}

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

162
    st = editor_status::running_thread;
163
    do {
164
165
166
167
        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;
168
169
170
171
            return;
        }
    } while (current < end && !stop);

172
173
    sim.finalize(end);

174
    st = editor_status::running_thread_need_join;
175
176
177
178
179
}

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

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

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

196
197
198
199
200
201
202
203
204
205
206
                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));
207
208
209
            }
        } else {
            if (ImGui::Button("run")) {
210
                // initialize_observation(log_w, ed);
211
212
                ed.stop = false;

213
214
215
216
217
218
219
220
221
222
                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));
223
            }
224
225
226
        }
    }

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

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

239
    // initialize_observation(log_w, ed);
240
241

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

245
    ed.models_make_transition.resize(ed.sim.models.capacity(), false);
246

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

static void
show_simulation_run_debug(window_logger& log_w, editor& ed)
{
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
    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
281
        }
282
283
        ++ed.simulation_bag_id;
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
284

285
286
287
288
289
290
291
292
293
294
295
    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
296
            }
297
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
298
        }
299
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
300

301
302
303
304
305
306
307
308
309
310
311
    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
312
            }
313
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
314
        }
315
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
316

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

319
    ImGui::SameLine();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
320

321
322
323
324
325
326
327
328
329
330
    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
331
            }
332
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
333
        }
334
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
335

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

338
    ImGui::SameLine();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
339

340
341
342
343
344
345
346
347
348
    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
349
            }
350
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
351
        }
352
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
353

354
355
356
357
    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
358

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

361
362
363
364
        std::fill_n(ed.models_make_transition.begin(),
                    ed.models_make_transition.size(),
                    false);

365
366
367
368
        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;
369
370
371
372
    }
}

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

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

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

389
390
391
392
393
394
395
396
    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
397

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

401
402
403
404
405
406
407
408
409
410
    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));
411
412
413
414
415
416
    }

    ImGui::End();
}

} // namesapce irt