simulation-editor.cpp 14.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
19
20
21
#include <fstream>
#include <string>

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

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

namespace irt {
28
29
30
31

void
observation_plot_output_initialize(const irt::observer& obs,
                                   const irt::time t) noexcept
32
{
33
34
35
36
    // const auto diff = ed.simulation_end - ed.simulation_begin;
    // const auto freq = diff / obs->time_step;
    // const auto length = std::min((size_t)freq, (size_t)4096);

37
38
39
    if (!obs.user_data)
        return;

40
41
42
43
44
45
46
47
48
    auto* output = reinterpret_cast<plot_output*>(obs.user_data);
    output->xs.clear();
    output->ys.clear();
    output->xs.reserve(4096u);
    output->ys.reserve(4096u);
    output->tl = t;
    output->min = -1.f;
    output->max = +1.f;
}
49

50
51
52
53
54
55
56
57
58
59
void
observation_file_output_initialize(const irt::observer& obs,
                                   const irt::time t) noexcept
{
    if (!obs.user_data)
        return;

    auto* output = reinterpret_cast<file_output*>(obs.user_data);
    output->tl = t;

60
61
62
63
    std::filesystem::path file(obs.name.begin());
    file.replace_extension(".dat");

    output->ofs.open(file);
64
65
    if (output->ofs.is_open())
        fmt::print(output->ofs, "t,{}\n", output->name.c_str());
66
67
}

68
69
70
71
void
observation_plot_output_observe(const irt::observer& obs,
                                const irt::time t,
                                const irt::message& msg) noexcept
72
73
74
75
{
    if (!obs.user_data)
        return;

76
    auto* output = reinterpret_cast<plot_output*>(obs.user_data);
77
78
    const auto value = static_cast<float>(msg[0]);

79
80
    output->min = std::min(output->min, value);
    output->max = std::max(output->max, value);
81

82
83
84
    for (auto to_fill = output->tl; to_fill < t; to_fill += obs.time_step) {
        output->ys.emplace_back(value);
        output->xs.emplace_back(static_cast<float>(t));
85
86
87
88
89
    }

    output->tl = t;
}

90
91
92
93
void
observation_file_output_observe(const irt::observer& obs,
                                const irt::time t,
                                const irt::message& msg) noexcept
94
95
96
97
{
    if (!obs.user_data)
        return;

98
99
    auto* output = reinterpret_cast<file_output*>(obs.user_data);
    const auto value = static_cast<float>(msg[0]);
100

101
102
103
    fmt::print(output->ofs, "{},{}\n", t, value);

    output->tl = t;
104
105
}

106
107
108
109
void
observation_plot_output_free(const irt::observer& /*obs*/,
                             const irt::time /*t*/) noexcept
{}
110

111
112
113
114
115
116
void
observation_file_output_free(const irt::observer& obs,
                             const irt::time /*t*/) noexcept
{
    if (!obs.user_data)
        return;
117

118
119
    auto* output = reinterpret_cast<file_output*>(obs.user_data);
    output->ofs.close();
120
121
}

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// static void
// initialize_observation(window_logger& log_w, irt::editor& ed) noexcept
//{
//    observer* obs = nullptr;
//    while (ed.sim.observers.next(obs)) {
//        auto& output = ed.observation_outputs.emplace_back(obs->name.sv());
//
//
//        if (output.plot) {
//            output.xs.clear();
//            output.ys.clear();
//            output.xs.reserve(length);
//            output.ys.reserve(length);
//        }
//
//        if (!obs->name.empty()) {
//            const std::filesystem::path obs_file_path =
//              ed.observation_directory / obs->name.c_str();
//
//            if (output.file) {
//                if (output.ofs.open(obs_file_path); !output.ofs.is_open())
//                    log_w.log(4,
//                              "Fail to open "
//                              "observation file: %s in "
//                              "%s\n",
//                              obs->name.c_str(),
//#if _WIN32
//                              ed.observation_directory.u8string().c_str());
//#else
//                              reinterpret_cast<const char*>(
//                                ed.observation_directory.u8string().c_str()));
//#endif
//            }
//        }
//
//        obs->initialize = &observation_output_initialize;
//        obs->observe = &observation_output_observe;
//        obs->free = &observation_output_free;
//        obs->user_data = static_cast<void*>(&output);
//    }
//}

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
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);

    st = editor_status::running_thread_need_join;
}

205
206
207
208
209
210
static void
run_simulation(window_logger& log_w,
               simulation& sim,
               double begin,
               double end,
               double& current,
211
               editor_status& st,
212
               status& sim_st,
213
214
215
               const bool& stop) noexcept
{
    current = begin;
216
217
    st = editor_status::initializing;
    if (sim_st = sim.initialize(current); irt::is_bad(sim_st)) {
218
219
        log_w.log(3,
                  "Simulation initialization failure (%s)\n",
220
221
                  irt::status_string(sim_st));
        st = editor_status::editing;
222
223
224
        return;
    }

225
    st = editor_status::running_thread;
226
    do {
227
228
229
230
        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;
231
232
233
234
            return;
        }
    } while (current < end && !stop);

235
    st = editor_status::running_thread_need_join;
236
237
238
239
240
}

static void
show_simulation_run_once(window_logger& log_w, editor& ed)
{
241
    if (ed.st == editor_status::running_thread_need_join) {
242
243
        if (ed.simulation_thread.joinable()) {
            ed.simulation_thread.join();
244
            ed.st = editor_status::editing;
245
        }
246
247
    } else if (ed.st == editor_status::editing ||
               ed.st == editor_status::running_debug) {
248
249
        ImGui::Checkbox("Time synchronization", &ed.use_real_time);

250
        if (ed.use_real_time) {
251
252
            ImGui::InputDouble("t/s", &ed.synchronize_timestep);

253
            if (ed.synchronize_timestep > 0. && ImGui::Button("run")) {
254
                // initialize_observation(log_w, ed);
255
                ed.stop = false;
256

257
258
259
260
261
262
263
264
265
266
267
                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));
268
269
270
            }
        } else {
            if (ImGui::Button("run")) {
271
                // initialize_observation(log_w, ed);
272
273
                ed.stop = false;

274
275
276
277
278
279
280
281
282
283
                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));
284
            }
285
286
287
        }
    }

288
    if (ed.st == editor_status::running_thread) {
289
290
291
292
293
294
295
296
297
298
299
        ImGui::SameLine();
        if (ImGui::Button("Force stop"))
            ed.stop = true;
    }
}

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

300
    // initialize_observation(log_w, ed);
301
302

    ed.simulation_current = ed.simulation_begin;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
303
    ed.simulation_during_date = ed.simulation_begin;
304
    ed.st = editor_status::initializing;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
305

306
    ed.models_make_transition.resize(ed.sim.models.capacity(), false);
307

308
309
    if (ed.sim_st = ed.sim.initialize(ed.simulation_current);
        irt::is_bad(ed.sim_st)) {
310
311
        log_w.log(3,
                  "Simulation initialisation failure (%s)\n",
312
313
                  irt::status_string(ed.sim_st));
        ed.st = editor_status::editing;
314
315
316
317
    } else {
        ed.simulation_next_time = ed.sim.sched.empty()
                                    ? time_domain<time>::infinity
                                    : ed.sim.sched.tn();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
318
        ed.simulation_bag_id = 0;
319
        ed.st = editor_status::running_debug;
320
321
322
323
324
325
    }
}

static void
show_simulation_run_debug(window_logger& log_w, editor& ed)
{
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
    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
342
        }
343
344
        ++ed.simulation_bag_id;
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
345

346
347
348
349
350
351
352
353
354
355
356
    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
357
            }
358
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
359
        }
360
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
361

362
363
364
365
366
367
368
369
370
371
372
    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
373
            }
374
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
375
        }
376
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
377

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

380
    ImGui::SameLine();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
381

382
383
384
385
386
387
388
389
390
391
    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
392
            }
393
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
394
        }
395
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
396

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

399
    ImGui::SameLine();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
400

401
402
403
404
405
406
407
408
409
    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
410
            }
411
            ++ed.simulation_bag_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
412
        }
413
    }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
414

415
416
417
418
    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
419

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

422
423
424
425
        std::fill_n(ed.models_make_transition.begin(),
                    ed.models_make_transition.size(),
                    false);

426
427
428
429
        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;
430
431
432
433
    }
}

void
434
show_simulation_box(editor& ed, bool* show_simulation)
435
436
437
438
439
440
441
442
{
    ImGui::SetNextWindowPos(ImVec2(50, 50), ImGuiCond_FirstUseEver);
    ImGui::SetNextWindowSize(ImVec2(250, 350), ImGuiCond_Once);
    if (!ImGui::Begin("Simulation", show_simulation)) {
        ImGui::End();
        return;
    }

443
444
445
    ImGui::InputDouble("Begin", &ed.simulation_begin);
    ImGui::InputDouble("End", &ed.simulation_end);
    ImGui::Checkbox("Show values", &ed.simulation_show_value);
446

447
448
    if (ImGui::Button("Output files"))
        ed.show_select_directory_dialog = true;
449

450
451
452
453
454
455
456
457
    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
458

459
460
    if (ImGui::CollapsingHeader("Simulation run one"))
        show_simulation_run_once(log_w, ed);
461

462
463
464
465
466
467
468
469
470
471
    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));
472
473
474
475
476
477
    }

    ImGui::End();
}

} // namesapce irt