-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run fmi2SetXXX function to initialize FMU start values #778
Changes from 8 commits
1e54be8
00ced51
212f007
4ec6c32
7a27b47
12334a7
c1da6f6
790ba4c
7bc91c8
f79ac15
68c1849
7e4d90b
9cb4d18
cc5f93d
2cf995a
728c883
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,8 @@ | |
#include <sstream> | ||
#include <stdexcept> | ||
#include <unordered_map> | ||
#include <iostream> | ||
#include <cosim/utility/utility.hpp> | ||
|
||
|
||
namespace cosim | ||
|
@@ -216,7 +218,9 @@ class set_variable_cache | |
} | ||
} | ||
|
||
std::pair<gsl::span<value_reference>, gsl::span<const T>> modify_and_get(duration deltaT) | ||
std::pair<gsl::span<value_reference>, gsl::span<const T>> modify_and_get( | ||
duration deltaT, | ||
std::optional<std::function<bool(const value_reference&, const T&)>> filter = std::nullopt) | ||
{ | ||
if (!hasRunModifiers_) { | ||
for (const auto& entry : modifiers_) { | ||
|
@@ -233,6 +237,24 @@ class set_variable_cache | |
assert(references_.size() == values_.size()); | ||
hasRunModifiers_ = true; | ||
} | ||
|
||
if (filter) { | ||
references_filtered_.clear(); | ||
values_filtered_.clear(); | ||
|
||
for (size_t i = 0; i < references_.size(); i++) { | ||
auto& ref = references_.at(i); | ||
auto& value = values_.at(i); | ||
|
||
if ((*filter)(ref, value)) { | ||
references_filtered_.push_back(ref); | ||
values_filtered_.push_back(value); | ||
} | ||
} | ||
|
||
return std::pair(gsl::make_span(references_filtered_), gsl::make_span(values_filtered_)); | ||
} | ||
|
||
return std::pair(gsl::make_span(references_), gsl::make_span(values_)); | ||
} | ||
|
||
|
@@ -243,6 +265,8 @@ class set_variable_cache | |
} | ||
references_.clear(); | ||
values_.clear(); | ||
references_filtered_.clear(); | ||
values_filtered_.clear(); | ||
hasRunModifiers_ = false; | ||
} | ||
|
||
|
@@ -268,6 +292,10 @@ class set_variable_cache | |
// The references and values of the variables that will be set next. | ||
std::vector<value_reference> references_; | ||
boost::container::vector<T> values_; | ||
|
||
// Filtered references and values to be set next (if a filter is applied). | ||
std::vector<value_reference> references_filtered_; | ||
boost::container::vector<T> values_filtered_; | ||
}; | ||
|
||
|
||
|
@@ -518,6 +546,34 @@ class slave_simulator::impl | |
return result; | ||
} | ||
|
||
void initialize_start_values() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The functions are generally listed in the order they're supposed to be called, so this should come before |
||
{ | ||
auto deltaT = duration::zero(); | ||
auto filter = [this](const variable_type& vt) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, we have the |
||
return [this, &vt](const value_reference& vr, const std::variant<double, int, bool, std::string>&) { | ||
const auto& vd = this->find_variable_description(vr, vt); | ||
/// FMI Specification 2.0.4 - Section 4.2.4 | ||
return vd.variability != variable_variability::constant && | ||
vd.causality != variable_causality::input; | ||
}; | ||
}; | ||
|
||
const auto [realRefs, realValues] = realSetCache_.modify_and_get(deltaT, filter(variable_type::real)); | ||
const auto [integerRefs, integerValues] = integerSetCache_.modify_and_get(deltaT, filter(variable_type::integer)); | ||
const auto [booleanRefs, booleanValues] = booleanSetCache_.modify_and_get(deltaT, filter(variable_type::boolean)); | ||
const auto [stringRefs, stringValues] = stringSetCache_.modify_and_get(deltaT, filter(variable_type::string)); | ||
|
||
slave_->set_variables( | ||
gsl::make_span(realRefs), | ||
gsl::make_span(realValues), | ||
gsl::make_span(integerRefs), | ||
gsl::make_span(integerValues), | ||
gsl::make_span(booleanRefs), | ||
gsl::make_span(booleanValues), | ||
gsl::make_span(stringRefs), | ||
gsl::make_span(stringValues)); | ||
} | ||
|
||
private: | ||
void set_variables(duration deltaT) | ||
{ | ||
|
@@ -803,4 +859,9 @@ step_result slave_simulator::do_step( | |
} | ||
|
||
|
||
void slave_simulator::initialize_start_values() | ||
{ | ||
return pimpl_->initialize_start_values(); | ||
} | ||
|
||
} // namespace cosim |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When adding an FMU, you should update |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<OspSystemStructure xmlns="http://opensimulationplatform.com/MSMI/OSPSystemStructure" version="0.1"> | ||
<StartTime>0.0</StartTime> | ||
<BaseStepSize>0.01</BaseStepSize> | ||
<Algorithm>fixedStep</Algorithm> | ||
<Simulators> | ||
<Simulator name="example" source="../fmi2/StateInitExample.fmu"> | ||
<InitialValues> | ||
<InitialValue variable="Parameters.Integrator1_x0"> | ||
<Real value="10.0" /> | ||
</InitialValue> | ||
</InitialValues> | ||
</Simulator> | ||
</Simulators> | ||
<Connections> | ||
</Connections> | ||
</OspSystemStructure> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#include <cosim/algorithm/fixed_step_algorithm.hpp> | ||
#include <cosim/osp_config_parser.hpp> | ||
#include <cosim/observer/file_observer.hpp> | ||
#include <cosim/exception.hpp> | ||
#include <cosim/execution.hpp> | ||
#include <cosim/function/linear_transformation.hpp> | ||
#include <cosim/observer/last_value_observer.hpp> | ||
#include <cosim/system_structure.hpp> | ||
#include <algorithm> | ||
#include <iostream> | ||
|
||
#define REQUIRE(test) \ | ||
if (!(test)) throw std::runtime_error("Requirement not satisfied: " #test) | ||
|
||
int main() | ||
{ | ||
try { | ||
const auto testDataDir = std::getenv("TEST_DATA_DIR"); | ||
REQUIRE(!!testDataDir); | ||
|
||
cosim::filesystem::path configPath = testDataDir; | ||
|
||
auto resolver = cosim::default_model_uri_resolver(); | ||
const auto config = cosim::load_osp_config(configPath / "msmi" / "OspSystemStructure_StateInitExample.xml", *resolver); | ||
|
||
auto execution = cosim::execution( | ||
config.start_time, | ||
std::make_shared<cosim::fixed_step_algorithm>(config.step_size)); | ||
|
||
const auto entityMaps = cosim::inject_system_structure( | ||
execution, config.system_structure, config.initial_values); | ||
auto lvObserver = std::make_shared<cosim::last_value_observer>(); | ||
|
||
execution.add_observer(lvObserver); | ||
execution.simulate_until(cosim::to_time_point(0.1)); | ||
|
||
auto sim = entityMaps.simulators.at("example"); | ||
const auto paramRef = config.system_structure.get_variable_description({"example", "Parameters.Integrator1_x0"}).reference; | ||
const auto outRef = config.system_structure.get_variable_description({"example", "Integrator_out1"}).reference; | ||
|
||
double initialValue = 0.0; | ||
double outputValue = 0.0; | ||
lvObserver->get_real(sim, gsl::make_span(¶mRef, 1), gsl::make_span(&initialValue, 1)); | ||
lvObserver->get_real(sim, gsl::make_span(&outRef, 1), gsl::make_span(&outputValue, 1)); | ||
|
||
REQUIRE(std::fabs(initialValue - 10.0) < 1.0e-9); | ||
REQUIRE(std::fabs(outputValue - 10.1) < 1.0e-9); | ||
|
||
} catch (const std::exception& e) { | ||
std::cerr << "Error: " << e.what() << std::endl; | ||
return 1; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't made reference to the FMI spec anywhere else in this interface, nor in any other of our general interfaces, because they are supposed to be agnostic with respect to the underlying binary interface. All FMI-specific stuff is hidden away in the
cosim::fmi
module. (I admit that we're still leaking FMI details like a sieve, but we haven't been this explicit about it before.) I suggest writing a more general and extensive description of what the function is supposed to do instead.Also, as commented elsewhere, the functions are generally in call order, so this should go before
setup()
.