Skip to content

Commit c49ebb6

Browse files
davidhjp01restenb
andauthored
Run fmi2SetXXX function to initialize FMU start values (#778)
* Swapped the order of setup and do_iteration in initialize function * Added initialize start values function * Cleaned up code * Added a filtered list for modify_and_get. * Error fix * Filter function update * Filter Update * Typo fix * review follow-up * added FMU to README * Fixed dangling reference. * Revert to "Typo fix" This reverts commit 790ba4c. * Removed unused includes * Ref for scalar_value (string) * return string from the textual representations, since nullptr "short-circuits" the error message. --------- Co-authored-by: Roger Eivind Stenbro <[email protected]>
1 parent 5c6e196 commit c49ebb6

File tree

9 files changed

+130
-7
lines changed

9 files changed

+130
-7
lines changed

include/cosim/algorithm/simulator.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ class simulator : public manipulable
6060
virtual void set_string(value_reference reference, std::string_view value) = 0;
6161

6262
/**
63-
* Performs pre-simulation setup and enters initialisation mode.
63+
* Performs pre-simulation setup, instantiates the FMUs and enters
64+
* initialisation mode.
6465
*
6566
* This function must be called exactly once, before initialisation and
6667
* simulation can begin (i.e. before the first time either of

include/cosim/model_description.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ constexpr const char* to_text(variable_type v)
6969
case variable_type::boolean: return "boolean";
7070
case variable_type::string: return "string";
7171
case variable_type::enumeration: return "enumeration";
72-
default: return nullptr;
72+
default: return "NULL";
7373
}
7474
}
7575

@@ -83,7 +83,7 @@ constexpr const char* to_text(variable_causality v)
8383
case variable_causality::input: return "input";
8484
case variable_causality::output: return "output";
8585
case variable_causality::local: return "local";
86-
default: return nullptr;
86+
default: return "NULL";
8787
}
8888
}
8989

@@ -97,7 +97,7 @@ constexpr const char* to_text(variable_variability v)
9797
case variable_variability::tunable: return "tunable";
9898
case variable_variability::discrete: return "discrete";
9999
case variable_variability::continuous: return "continuous";
100-
default: return nullptr;
100+
default: return "NULL";
101101
}
102102
}
103103

src/cosim/algorithm/fixed_step_algorithm.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ class fixed_step_algorithm::impl
171171
calculate_and_transfer();
172172
}
173173

174+
174175
for (auto& s : simulators_) {
175176
pool_.submit([&] {
176177
s.second.sim->start_simulation();

src/cosim/slave_simulator.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
#include "cosim/slave_simulator.hpp"
77

88
#include "cosim/error.hpp"
9+
#include <cosim/utility/utility.hpp>
910

1011
#include <algorithm>
1112
#include <cassert>
12-
#include <sstream>
1313
#include <stdexcept>
1414
#include <unordered_map>
1515

@@ -216,7 +216,9 @@ class set_variable_cache
216216
}
217217
}
218218

219-
std::pair<gsl::span<value_reference>, gsl::span<const T>> modify_and_get(duration deltaT)
219+
std::pair<gsl::span<value_reference>, gsl::span<const T>> modify_and_get(
220+
duration deltaT,
221+
std::optional<std::function<bool(const value_reference, const T&)>> filter = std::nullopt)
220222
{
221223
if (!hasRunModifiers_) {
222224
for (const auto& entry : modifiers_) {
@@ -233,6 +235,24 @@ class set_variable_cache
233235
assert(references_.size() == values_.size());
234236
hasRunModifiers_ = true;
235237
}
238+
239+
if (filter) {
240+
references_filtered_.clear();
241+
values_filtered_.clear();
242+
243+
for (size_t i = 0; i < references_.size(); i++) {
244+
auto& ref = references_.at(i);
245+
auto& value = values_.at(i);
246+
247+
if ((*filter)(ref, value)) {
248+
references_filtered_.push_back(ref);
249+
values_filtered_.push_back(value);
250+
}
251+
}
252+
253+
return std::pair(gsl::make_span(references_filtered_), gsl::make_span(values_filtered_));
254+
}
255+
236256
return std::pair(gsl::make_span(references_), gsl::make_span(values_));
237257
}
238258

@@ -243,6 +263,8 @@ class set_variable_cache
243263
}
244264
references_.clear();
245265
values_.clear();
266+
references_filtered_.clear();
267+
values_filtered_.clear();
246268
hasRunModifiers_ = false;
247269
}
248270

@@ -268,6 +290,10 @@ class set_variable_cache
268290
// The references and values of the variables that will be set next.
269291
std::vector<value_reference> references_;
270292
boost::container::vector<T> values_;
293+
294+
// Filtered references and values of the values to be set next (if a filter is applied).
295+
std::vector<value_reference> references_filtered_;
296+
boost::container::vector<T> values_filtered_;
271297
};
272298

273299

@@ -490,6 +516,30 @@ class slave_simulator::impl
490516
std::optional<time_point> stopTime,
491517
std::optional<double> relativeTolerance)
492518
{
519+
auto deltaT = duration::zero();
520+
auto filter = [this](const variable_type vt) {
521+
return [this, vt](const value_reference vr, const cosim::scalar_value&) {
522+
const auto& vd = this->find_variable_description(vr, vt);
523+
return vd.variability != variable_variability::constant &&
524+
vd.causality != variable_causality::input;
525+
};
526+
};
527+
528+
const auto [realRefs, realValues] = realSetCache_.modify_and_get(deltaT, filter(variable_type::real));
529+
const auto [integerRefs, integerValues] = integerSetCache_.modify_and_get(deltaT, filter(variable_type::integer));
530+
const auto [booleanRefs, booleanValues] = booleanSetCache_.modify_and_get(deltaT, filter(variable_type::boolean));
531+
const auto [stringRefs, stringValues] = stringSetCache_.modify_and_get(deltaT, filter(variable_type::string));
532+
533+
slave_->set_variables(
534+
gsl::make_span(realRefs),
535+
gsl::make_span(realValues),
536+
gsl::make_span(integerRefs),
537+
gsl::make_span(integerValues),
538+
gsl::make_span(booleanRefs),
539+
gsl::make_span(booleanValues),
540+
gsl::make_span(stringRefs),
541+
gsl::make_span(stringValues));
542+
493543
slave_->setup(startTime, stopTime, relativeTolerance);
494544
get_variables(duration::zero());
495545
}
@@ -802,5 +852,4 @@ step_result slave_simulator::do_step(
802852
return pimpl_->do_step(currentT, deltaT);
803853
}
804854

805-
806855
} // namespace cosim

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set(tests
1313
"scenario_manager_test"
1414
"synchronized_xy_series_test"
1515
"config_end_time_test"
16+
"state_init_test"
1617
)
1718

1819
set(unittests

tests/data/fmi2/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
| `CraneController.fmu` | OSP | [MPL 2.0](../../../LICENSE) |
66
| `KnuckleBoomCrane.fmu` | OSP | [MPL 2.0](../../../LICENSE) |
77
| `vector.fmu` | [OSP cpp-fmus] | [MIT](./osp_cpp-fmus_LICENSE) |
8+
| `StateInitExample.fmu` | OSP | [MPL 2.0](../../../LICENSE) |
89

910

1011
[OSP cpp-fmus]: https://github.com/open-simulation-platform/cpp-fmus

tests/data/fmi2/StateInitExample.fmu

82.1 KB
Binary file not shown.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<OspSystemStructure xmlns="http://opensimulationplatform.com/MSMI/OSPSystemStructure" version="0.1">
3+
<StartTime>0.0</StartTime>
4+
<BaseStepSize>0.01</BaseStepSize>
5+
<Algorithm>fixedStep</Algorithm>
6+
<Simulators>
7+
<Simulator name="example" source="../fmi2/StateInitExample.fmu">
8+
<InitialValues>
9+
<InitialValue variable="Parameters.Integrator1_x0">
10+
<Real value="10.0" />
11+
</InitialValue>
12+
</InitialValues>
13+
</Simulator>
14+
</Simulators>
15+
<Connections>
16+
</Connections>
17+
</OspSystemStructure>

tests/state_init_test.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <cosim/algorithm/fixed_step_algorithm.hpp>
2+
#include <cosim/osp_config_parser.hpp>
3+
#include <cosim/observer/file_observer.hpp>
4+
#include <cosim/exception.hpp>
5+
#include <cosim/execution.hpp>
6+
#include <cosim/function/linear_transformation.hpp>
7+
#include <cosim/observer/last_value_observer.hpp>
8+
#include <cosim/system_structure.hpp>
9+
#include <algorithm>
10+
#include <iostream>
11+
12+
#define REQUIRE(test) \
13+
if (!(test)) throw std::runtime_error("Requirement not satisfied: " #test)
14+
15+
int main()
16+
{
17+
try {
18+
const auto testDataDir = std::getenv("TEST_DATA_DIR");
19+
REQUIRE(!!testDataDir);
20+
21+
cosim::filesystem::path configPath = testDataDir;
22+
23+
auto resolver = cosim::default_model_uri_resolver();
24+
const auto config = cosim::load_osp_config(configPath / "msmi" / "OspSystemStructure_StateInitExample.xml", *resolver);
25+
26+
auto execution = cosim::execution(
27+
config.start_time,
28+
std::make_shared<cosim::fixed_step_algorithm>(config.step_size));
29+
30+
const auto entityMaps = cosim::inject_system_structure(
31+
execution, config.system_structure, config.initial_values);
32+
auto lvObserver = std::make_shared<cosim::last_value_observer>();
33+
34+
execution.add_observer(lvObserver);
35+
execution.simulate_until(cosim::to_time_point(0.1));
36+
37+
auto sim = entityMaps.simulators.at("example");
38+
const auto paramRef = config.system_structure.get_variable_description({"example", "Parameters.Integrator1_x0"}).reference;
39+
const auto outRef = config.system_structure.get_variable_description({"example", "Integrator_out1"}).reference;
40+
41+
double initialValue = 0.0;
42+
double outputValue = 0.0;
43+
lvObserver->get_real(sim, gsl::make_span(&paramRef, 1), gsl::make_span(&initialValue, 1));
44+
lvObserver->get_real(sim, gsl::make_span(&outRef, 1), gsl::make_span(&outputValue, 1));
45+
46+
REQUIRE(std::fabs(initialValue - 10.0) < 1.0e-9);
47+
REQUIRE(std::fabs(outputValue - 10.1) < 1.0e-9);
48+
49+
} catch (const std::exception& e) {
50+
std::cerr << "Error: " << e.what() << std::endl;
51+
return 1;
52+
}
53+
}

0 commit comments

Comments
 (0)