Commit d152c2bb authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

core: add qss2 integrator

parent 2c2caa73
Pipeline #14778 passed with stage
in 53 seconds
...@@ -26,70 +26,6 @@ load_file_dialog(std::filesystem::path& out); ...@@ -26,70 +26,6 @@ load_file_dialog(std::filesystem::path& out);
bool bool
save_file_dialog(std::filesystem::path& out); save_file_dialog(std::filesystem::path& out);
static inline const char* simulation_status_string[] = {
"success",
"running",
"uninitialized",
"internal_error",
};
static inline const char* status_string[] = {
"success",
"unknown_dynamics",
"block_allocator_bad_capacity",
"block_allocator_not_enough_memory",
"head_allocator_bad_capacity",
"head_allocator_not_enough_memory",
"simulation_not_enough_model",
"simulation_not_enough_memory_message_list_allocator",
"simulation_not_enough_memory_input_port_list_allocator",
"simulation_not_enough_memory_output_port_list_allocator",
"data_array_init_capacity_error",
"data_array_not_enough_memory",
"data_array_archive_init_capacity_error",
"data_array_archive_not_enough_memory",
"array_init_capacity_zero",
"array_init_capacity_too_big",
"array_init_not_enough_memory",
"vector_init_capacity_zero",
"vector_init_capacity_too_big",
"vector_init_not_enough_memory",
"dynamics_unknown_id",
"dynamics_unknown_port_id",
"dynamics_not_enough_memory",
"model_connect_output_port_unknown",
"model_connect_input_port_unknown",
"model_connect_already_exist",
"model_connect_bad_dynamics",
"model_adder_empty_init_message",
"model_adder_bad_init_message",
"model_adder_bad_external_message",
"model_mult_empty_init_message",
"model_mult_bad_init_message",
"model_mult_bad_external_message",
"model_integrator_internal_error",
"model_integrator_output_error",
"model_integrator_running_without_x_dot",
"model_integrator_ta_with_bad_x_dot",
"model_integrator_bad_external_message",
"model_quantifier_bad_quantum_parameter",
"model_quantifier_bad_archive_length_parameter",
"model_quantifier_shifting_value_neg",
"model_quantifier_shifting_value_less_1",
"model_quantifier_bad_external_message",
"model_cross_bad_external_message",
"model_time_func_bad_init_message",
"model_accumulator_bad_external_message",
"gui_not_enough_memory",
"io_file_format_error",
"io_file_format_model_error",
"io_file_format_model_number_error",
"io_file_format_model_unknown",
"io_file_format_dynamics_unknown",
"io_file_format_dynamics_limit_reach",
"io_file_format_dynamics_init_error"
};
} // namespace irt } // namespace irt
#endif #endif
...@@ -38,8 +38,7 @@ static auto automatic_layout_y_distance = 350.f; ...@@ -38,8 +38,7 @@ static auto automatic_layout_y_distance = 350.f;
static auto grid_layout_x_distance = 250.f; static auto grid_layout_x_distance = 250.f;
static auto grid_layout_y_distance = 250.f; static auto grid_layout_y_distance = 250.f;
static ImVec4 static ImVec4 operator*(const ImVec4& lhs, const float rhs) noexcept
operator*(const ImVec4& lhs, const float rhs) noexcept
{ {
return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs);
} }
...@@ -206,16 +205,14 @@ run_simulation(simulation& sim, ...@@ -206,16 +205,14 @@ run_simulation(simulation& sim,
if (auto ret = sim.initialize(current); irt::is_bad(ret)) { if (auto ret = sim.initialize(current); irt::is_bad(ret)) {
log_w.log(3, log_w.log(3,
"Simulation initialization failure (%s)\n", "Simulation initialization failure (%s)\n",
irt::status_string[static_cast<int>(ret)]); irt::status_string(ret));
st = simulation_status::internal_error; st = simulation_status::internal_error;
return; return;
} }
do { do {
if (auto ret = sim.run(current); irt::is_bad(ret)) { if (auto ret = sim.run(current); irt::is_bad(ret)) {
log_w.log(3, log_w.log(3, "Simulation failure (%s)\n", irt::status_string(ret));
"Simulation failure (%s)\n",
irt::status_string[static_cast<int>(ret)]);
st = simulation_status::internal_error; st = simulation_status::internal_error;
return; return;
...@@ -665,48 +662,49 @@ struct copier ...@@ -665,48 +662,49 @@ struct copier
auto ret = sim.dispatch( auto ret = sim.dispatch(
mdl->type, mdl->type,
[this, &sim, mdl, &mdl_id_dst]<typename DynamicsM>( [ this, &sim, mdl, &
DynamicsM& dynamics_models) -> status { mdl_id_dst ]<typename DynamicsM>(DynamicsM & dynamics_models)
using Dynamics = typename DynamicsM::value_type; ->status {
using Dynamics = typename DynamicsM::value_type;
irt_return_if_fail(dynamics_models.can_alloc(1), irt_return_if_fail(dynamics_models.can_alloc(1),
status::dynamics_not_enough_memory); status::dynamics_not_enough_memory);
auto* dyn_ptr = dynamics_models.try_to_get(mdl->id); auto* dyn_ptr = dynamics_models.try_to_get(mdl->id);
irt_return_if_fail(dyn_ptr, status::dynamics_unknown_id); irt_return_if_fail(dyn_ptr, status::dynamics_unknown_id);
auto& new_dyn = dynamics_models.alloc(*dyn_ptr); auto& new_dyn = dynamics_models.alloc(*dyn_ptr);
auto new_dyn_id = dynamics_models.get_id(new_dyn); auto new_dyn_id = dynamics_models.get_id(new_dyn);
if constexpr (is_detected_v<has_input_port_t, Dynamics>) if constexpr (is_detected_v<has_input_port_t, Dynamics>)
std::fill_n(new_dyn.x, std::fill_n(new_dyn.x,
std::size(new_dyn.x), std::size(new_dyn.x),
static_cast<input_port_id>(0)); static_cast<input_port_id>(0));
if constexpr (is_detected_v<has_output_port_t, Dynamics>) if constexpr (is_detected_v<has_output_port_t, Dynamics>)
std::fill_n(new_dyn.y, std::fill_n(new_dyn.y,
std::size(new_dyn.y), std::size(new_dyn.y),
static_cast<output_port_id>(0)); static_cast<output_port_id>(0));
irt_return_if_bad( irt_return_if_bad(
sim.alloc(new_dyn, new_dyn_id, mdl->name.c_str())); sim.alloc(new_dyn, new_dyn_id, mdl->name.c_str()));
*mdl_id_dst = new_dyn.id; *mdl_id_dst = new_dyn.id;
if constexpr (is_detected_v<has_input_port_t, Dynamics>) if constexpr (is_detected_v<has_input_port_t, Dynamics>)
for (size_t j = 0, ej = std::size(new_dyn.x); j != ej; for (size_t j = 0, ej = std::size(new_dyn.x); j != ej;
++j) ++j)
this->c_input_ports.emplace_back(dyn_ptr->x[j], this->c_input_ports.emplace_back(dyn_ptr->x[j],
new_dyn.x[j]); new_dyn.x[j]);
if constexpr (is_detected_v<has_output_port_t, Dynamics>) if constexpr (is_detected_v<has_output_port_t, Dynamics>)
for (size_t j = 0, ej = std::size(new_dyn.y); j != ej; for (size_t j = 0, ej = std::size(new_dyn.y); j != ej;
++j) ++j)
this->c_output_ports.emplace_back(dyn_ptr->y[j], this->c_output_ports.emplace_back(dyn_ptr->y[j],
new_dyn.y[j]); new_dyn.y[j]);
return status::success; return status::success;
}); });
irt_return_if_bad(ret); irt_return_if_bad(ret);
} }
...@@ -1398,621 +1396,561 @@ editor::show_model_cluster(cluster& mdl) noexcept ...@@ -1398,621 +1396,561 @@ editor::show_model_cluster(cluster& mdl) noexcept
} }
} }
void static const char* str_empty[] = { "" };
editor::show_model_dynamics(model& mdl) noexcept static const char* str_integrator[] = { "x-dot", "reset" };
static const char* str_adaptative_integrator[] = { "quanta", "x-dot", "reset" };
static const char* str_in_1[] = { "in" };
static const char* str_in_2[] = { "in-1", "in-2" };
static const char* str_in_3[] = { "in-1", "in-2", "in-3" };
static const char* str_in_4[] = { "in-1", "in-2", "in-3", "in-4" };
static const char* str_value_if_else[] = { "value", "if", "else" };
static const char* str_in_2_nb_2[] = { "in-1", "in-2", "nb-1", "nb-2" };
static const char* str_out_1[] = { "out" };
static const char* str_out_2[] = { "out-1", "out-2" };
template<typename Dynamics>
static constexpr const char**
get_input_port_names()
{ {
ImGui::PushItemWidth(100.0f); if constexpr (std::is_same_v<Dynamics, none>)
return str_empty;
else if constexpr (std::is_same_v<Dynamics, qss1_integrator>)
return str_integrator;
else if constexpr (std::is_same_v<Dynamics, qss2_integrator>)
return str_integrator;
else if constexpr (std::is_same_v<Dynamics, qss2_multiplier>)
return str_in_2;
else if constexpr (std::is_same_v<Dynamics, qss2_sum_2>)
return str_in_2;
else if constexpr (std::is_same_v<Dynamics, qss2_sum_3>)
return str_in_3;
else if constexpr (std::is_same_v<Dynamics, qss2_sum_4>)
return str_in_4;
else if constexpr (std::is_same_v<Dynamics, qss2_wsum_2>)
return str_in_2;
else if constexpr (std::is_same_v<Dynamics, qss2_wsum_3>)
return str_in_3;
else if constexpr (std::is_same_v<Dynamics, qss2_wsum_4>)
return str_in_4;
else if constexpr (std::is_same_v<Dynamics, integrator>)
return str_adaptative_integrator;
else if constexpr (std::is_same_v<Dynamics, quantifier>)
return str_in_1;
else if constexpr (std::is_same_v<Dynamics, adder_2>)
return str_in_2;
else if constexpr (std::is_same_v<Dynamics, adder_3>)
return str_in_3;
else if constexpr (std::is_same_v<Dynamics, adder_4>)
return str_in_4;
else if constexpr (std::is_same_v<Dynamics, mult_2>)
return str_in_2;
else if constexpr (std::is_same_v<Dynamics, mult_3>)
return str_in_3;
else if constexpr (std::is_same_v<Dynamics, mult_4>)
return str_in_4;
else if constexpr (std::is_same_v<Dynamics, counter>)
return str_in_1;
else if constexpr (std::is_same_v<Dynamics, generator>)
return str_empty;
else if constexpr (std::is_same_v<Dynamics, constant>)
return str_empty;
else if constexpr (std::is_same_v<Dynamics, cross>)
return str_value_if_else;
else if constexpr (std::is_same_v<Dynamics, accumulator_2>)
return str_in_2_nb_2;
else if constexpr (std::is_same_v<Dynamics, time_func>)
return str_empty;
}
{ template<typename Dynamics>
const char* items[] = { "none", "plot", "file", "both" }; static constexpr const char**
int current_item = 0; /* Default show none */ get_output_port_names()
auto* obs = sim.observers.try_to_get(mdl.obs_id); {
if constexpr (std::is_same_v<Dynamics, none>)
return str_empty;
else if constexpr (std::is_same_v<Dynamics, qss1_integrator>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_integrator>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_multiplier>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_sum_2>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_sum_3>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_sum_4>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_wsum_2>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_wsum_3>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, qss2_wsum_4>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, integrator>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, quantifier>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, adder_2>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, adder_3>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, adder_4>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, mult_2>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, mult_3>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, mult_4>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, counter>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, generator>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, constant>)
return str_out_1;
else if constexpr (std::is_same_v<Dynamics, cross>)
return str_out_2;
else if constexpr (std::is_same_v<Dynamics, accumulator_2>)
return str_empty;
else if constexpr (std::is_same_v<Dynamics, time_func>)
return str_out_1;
}
if (obs) template<typename Dynamics>
current_item = static void
static_cast<int>(observation_types[get_index(mdl.obs_id)]); add_input_attribute(editor& ed, const Dynamics& dyn) noexcept
{
if constexpr (is_detected_v<has_input_port_t, Dynamics>) {
const auto** names = get_input_port_names<Dynamics>();
if (ImGui::Combo( for (size_t i = 0, e = std::size(dyn.x); i != e; ++i) {
"observation", &current_item, items, IM_ARRAYSIZE(items))) { imnodes::BeginInputAttribute(ed.get_in(dyn.x[i]));
if (current_item == 0) { ImGui::TextUnformatted(names[i]);
if (obs) { imnodes::EndAttribute();
observation_types[get_index(mdl.obs_id)] = }
observation_output::type::none; }
sim.observers.free(*obs); }
mdl.obs_id = static_cast<observer_id>(0);
}
} else {
if (!obs) {
auto& o =
sim.observers.alloc(0.01, mdl.name.c_str(), nullptr);
sim.observe(mdl, o);
}
observation_types[get_index(mdl.obs_id)] = template<typename Dynamics>
current_item == 1 static void
? observation_output::type::plot add_output_attribute(editor& ed, const Dynamics& dyn) noexcept
: current_item == 2 ? observation_output::type::file {
: observation_output::type::both; if constexpr (is_detected_v<has_output_port_t, Dynamics>) {
} const auto** names = get_output_port_names<Dynamics>();
if (auto* o = sim.observers.try_to_get(mdl.obs_id); o) { for (size_t i = 0, e = std::size(dyn.y); i != e; ++i) {
float v = static_cast<float>(o->time_step); imnodes::BeginOutputAttribute(ed.get_out(dyn.y[i]));
if (ImGui::InputFloat("freq.", &v, 0.001f, 0.1f, "%.3f", 0)) ImGui::TextUnformatted(names[i]);
o->time_step = static_cast<double>(v); imnodes::EndAttribute();
}
} }
} }
}
ImGui::PopItemWidth(); static void
show_dynamics_values(const none& /*dyn*/)
{}
if (simulation_show_value && static void
match(st, simulation_status::success, simulation_status::running)) { show_dynamics_values(const qss1_integrator& dyn)
switch (mdl.type) { {
case dynamics_type::none: /* none does not have input port. */ ImGui::Text("X %.3f", dyn.X);
break; ImGui::Text("dQ %.3f", dyn.dQ);
case dynamics_type::integrator: { }
auto& dyn = sim.integrator_models.get(mdl.id);
imnodes::BeginInputAttribute(get_in(dyn.x[0]));
ImGui::TextUnformatted("quanta");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[1]));
ImGui::TextUnformatted("x_dot");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[2]));
ImGui::TextUnformatted("reset");
imnodes::EndAttribute();
ImGui::PushItemWidth(120.0f); static void
ImGui::Text("value %.3f", dyn.current_value); show_dynamics_values(const qss2_integrator& dyn)
ImGui::PopItemWidth(); {
ImGui::Text("X %.3f", dyn.X);
ImGui::Text("dQ %.3f", dyn.dQ);
}
imnodes::BeginOutputAttribute(get_out(dyn.y[0])); static void
const float text_width = ImGui::CalcTextSize("x").x; show_dynamics_values(const qss2_sum_2& dyn)
ImGui::Indent(120.f + ImGui::CalcTextSize("quanta").x - text_width); {
ImGui::TextUnformatted("x"); ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
imnodes::EndAttribute(); ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
} break; }
case dynamics_type::quantifier: {
auto& dyn = sim.quantifier_models.get(mdl.id);
imnodes::BeginInputAttribute(get_in(dyn.x[0]));
ImGui::TextUnformatted("x_dot");
imnodes::EndAttribute();
ImGui::PushItemWidth(120.0f); static void
ImGui::Text("up threshold %.3f", dyn.m_upthreshold); show_dynamics_values(const qss2_sum_3& dyn)
ImGui::Text("down threshold %.3f", dyn.m_downthreshold); {
ImGui::PopItemWidth(); ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
ImGui::Text("%.3f %.3f", dyn.values[2], dyn.slopes[2]);
}
imnodes::BeginOutputAttribute(get_out(dyn.y[0])); static void
ImGui::TextUnformatted("quanta"); show_dynamics_values(const qss2_sum_4& dyn)
imnodes::EndAttribute(); {
} break; ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
case dynamics_type::adder_2: { ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
auto& dyn = sim.adder_2_models.get(mdl.id); ImGui::Text("%.3f %.3f", dyn.values[2], dyn.slopes[2]);
imnodes::BeginInputAttribute(get_in(dyn.x[0])); ImGui::Text("%.3f %.3f", dyn.values[3], dyn.slopes[3]);
ImGui::TextUnformatted("x0"); }
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[1]));
ImGui::TextUnformatted("x1");
imnodes::EndAttribute();
ImGui::PushItemWidth(120.0f); static void
ImGui::Text("%.3f * %.3f", dyn.values[0], dyn.input_coeffs[0]); show_dynamics_values(const qss2_multiplier& dyn)
ImGui::Text("%.3f * %.3f", dyn.values[1], dyn.input_coeffs[1]); {
ImGui::PopItemWidth(); ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
}
imnodes::BeginOutputAttribute(get_out(dyn.y[0])); static void
const float text_width = ImGui::CalcTextSize("sum").x; show_dynamics_values(const qss2_wsum_2& dyn)
ImGui::Indent(120.f + ImGui::CalcTextSize("coeff-0").x - {
text_width); ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
ImGui::TextUnformatted("sum"); ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
imnodes::EndAttribute(); }
} break;
case dynamics_type::adder_3: {
auto& dyn = sim.adder_3_models.get(mdl.id);
imnodes::BeginInputAttribute(get_in(dyn.x[0]));
ImGui::TextUnformatted("x0");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[1]));
ImGui::TextUnformatted("x1");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[2]));
ImGui::TextUnformatted("x2");
imnodes::EndAttribute();
ImGui::PushItemWidth(120.0f); static void
ImGui::Text("%.3f * %.3f", dyn.values[0], dyn.input_coeffs[0]); show_dynamics_values(const qss2_wsum_3& dyn)
ImGui::Text("%.3f * %.3f", dyn.values[1], dyn.input_coeffs[1]); {
ImGui::Text("%.3f * %.3f", dyn.values[2], dyn.input_coeffs[2]); ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
ImGui::PopItemWidth(); ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
ImGui::Text("%.3f %.3f", dyn.values[2], dyn.slopes[2]);
}
imnodes::BeginOutputAttribute(get_out(dyn.y[0])); static void
const float text_width = ImGui::CalcTextSize("sum").x; show_dynamics_values(const qss2_wsum_4& dyn)
ImGui::Indent(120.f + ImGui::CalcTextSize("coeff-0").x - {
text_width); ImGui::Text("%.3f %.3f", dyn.values[0], dyn.slopes[0]);
ImGui::TextUnformatted("sum"); ImGui::Text("%.3f %.3f", dyn.values[1], dyn.slopes[1]);
imnodes::EndAttribute(); ImGui::Text("%.3f %.3f", dyn.values[2], dyn.slopes[2]);
} break; ImGui::Text("%.3f %.3f", dyn.values[3], dyn.slopes[3]);
case dynamics_type::adder_4: { }
auto& dyn = sim.adder_4_models.get(mdl.id);
imnodes::BeginInputAttribute(get_in(dyn.x[0]));
ImGui::TextUnformatted("x0");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[1]));
ImGui::TextUnformatted("x1");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[2]));
ImGui::TextUnformatted("x2");
imnodes::EndAttribute();
imnodes::BeginInputAttribute(get_in(dyn.x[3]));
ImGui::TextUnformatted("x3");
imnodes::EndAttribute();
ImGui::PushItemWidth(120.0f); static void
ImGui::Text("%.3f * %.3f", dyn.values[0], dyn.input_coeffs[0]); show_dynamics_values(const integrator& dyn)
ImGui::Text("%.3f * %.3f", dyn.values[1], dyn.input_coeffs[1]); {
ImGui::Text("%.3f * %.3f", dyn.values[2], dyn.input_coeffs[2]); ImGui::Text("value %.3f", dyn.current_value);
ImGui::Text("%.3f * %.3f", dyn.values[3], dyn.input_coeffs[3]); }
ImGui::PopItemWidth();
imnodes::BeginOutputAttribute(get_out(dyn.y[0])); static void
const float text_width = ImGui::CalcTextSize("sum").x; show_dynamics_values(const quantifier& dyn)
ImGui::Indent(120.f + ImGui::CalcTextSize("coeff-0").x - {
text_width); ImGui::Text("up threshold %.3f", dyn.m_upthreshold);
ImGui::TextUnformatted("sum"); ImGui::Text("down threshold %.3f", dyn.m_downthreshold);
imnodes::EndAttribute(); }