Skip to content

Commit 5c2c7c1

Browse files
restenbdavidhjp01kyllingstad
authored
Feature: ECCO Algorithm (#783)
* Initial attempt. * Working step size controller * Need to fix the issue. * Fix instability, print output as CSV * Merged master and fixed updated mock test. * working on ecco interface * Update * messing around * workshop mob session * tentative fix to ecco test * end of mob day 1 * Update * Update (back to timeseriesobserver) * Added fmus * Added xml * mobbing day 2 * Quarter Car update * Fix bug and get rid of warnings in ECCO algorithm An implementation of `algorithm::do_step()` is supposed to return the length of the step that was taken by the algorithm. In our ECCO implementation, it instead returned the size of the *next* step. This commit fixes that, plus gets rid of a couple of signed-unsigned comparison warnings. * remove failing ecco multibond test for now * ecco xml import support * ecco params parsing impl * done parsing * update xsd, tests * refactor * add power bond from inject system structure * simplified to input and output port variables * fix tests * remove a.html, whereever it came from * cleaning * conan changes * add to variable group connections * updating build in accord with state saving PR * review follow-up * review follow-up: fix build * reduce libzip version again * review follow-up: change u to input and y to output * test update * moving test FMUs, adding LICENSE & README for new FMUs. * deleting ground for now, doesn't have linux binary * use boost 1.81 * use newer proxyfmu version * boost 1.85 * merge in changes for zip.cpp due to libzip API changes * fix build * replaced with fmu compiled on ubuntu1604 * now with win binaries as well * remove decimation factor * re-fix review items * removed step_size from osp_config, added new constructor on fixed_step_algorithm to create algorithm from fixed_step_algorithm_params instead * updated test * final adjustments * replaced port with causality, cleanups * added convenience function for getting energies * fix merge issues with state saving branch * also change to causality in the xsd... * more elegant without [[maybe_unused]] * update xsd, added check to ensure uniqueness of powerbond names * fix mock_slave state saving for current time and step size * set maxOccurs=1 perhaps * fix proxyfmu_test, follow up last comment --------- Co-authored-by: David Heejong Park <hee.jong.park@dnv.com> Co-authored-by: Lars T. Kyllingstad <lars.kyllingstad@sintef.no>
1 parent 1d102d6 commit 5c2c7c1

36 files changed

Lines changed: 1656 additions & 76 deletions

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ build/
33
debug-build/
44
release-build/
55

6+
# CTest:
7+
Testing/
8+
69
# JetBrains CLion:
710
.idea/
811
cmake-build-*/
912

1013
# Visual Studio Code
1114
.vscode/
12-
.clj-kondo/
1315
.lsp/
16+
.clj-kondo
1417

1518
CMakeUserPresets.json

conanfile.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
class LibcosimConan(ConanFile):
1111
# Basic package info
1212
name = "libcosim"
13+
1314
def set_version(self):
14-
self.version = load(self, os.path.join(self.recipe_folder, "version.txt")).strip()
15+
self.version = load(
16+
self, os.path.join(self.recipe_folder, "version.txt")
17+
).strip()
1518

1619
# Metadata
1720
license = "MPL-2.0"
@@ -73,10 +76,8 @@ def generate(self):
7376
# Copy dependencies to the folder where executables (tests, mainly)
7477
# will be placed, so it's easier to run them.
7578
bindir = os.path.join(
76-
self.build_folder,
77-
"output",
78-
str(self.settings.build_type).lower(),
79-
"bin")
79+
self.build_folder, "output", str(self.settings.build_type).lower(), "bin"
80+
)
8081
for dep in self.dependencies.values():
8182
for depdir in dep.cpp_info.bindirs:
8283
copy(self, "*.dll", depdir, bindir, keep_path=False)
@@ -115,9 +116,14 @@ def package_info(self):
115116

116117
def validate(self):
117118
if self.options.shared and not self.dependencies["boost"].options.shared:
118-
raise ConanInvalidConfiguration("Option libcosim:shared=True also requires option boost:shared=True")
119+
raise ConanInvalidConfiguration(
120+
"Option libcosim:shared=True also requires option boost:shared=True"
121+
)
119122

120123
# Helper functions
121124

122125
def _is_tests_enabled(self):
123-
return os.getenv("LIBCOSIM_RUN_TESTS_ON_CONAN_BUILD", "False").lower() in ("true", "1")
126+
return os.getenv("LIBCOSIM_RUN_TESTS_ON_CONAN_BUILD", "False").lower() in (
127+
"true",
128+
"1",
129+
)

data/xsd/OspSystemStructure.xsd

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99
<xs:element name="OspSystemStructure">
1010
<xs:complexType>
1111
<xs:sequence>
12-
<xs:element name="StartTime" minOccurs="0" default="0.0" type="xs:double"/>
13-
<xs:element name="EndTime" minOccurs="0" type="xs:double"/>
14-
<xs:element name="BaseStepSize" minOccurs="0" type="xs:double"/>
15-
<xs:element name="Algorithm" minOccurs="0" default="fixedStep" type="xs:string"/>
12+
<xs:element name="StartTime" minOccurs="0" maxOccurs="1" default="0.0" type="xs:double"/>
13+
<xs:element name="EndTime" minOccurs="0" maxOccurs="1" type="xs:double"/>
14+
<xs:element name="BaseStepSize" minOccurs="0" maxOccurs="1" type="xs:double"/>
15+
<xs:element name="Algorithm" minOccurs="0" maxOccurs="1" default="fixedStep" type="xs:string"/>
1616
<xs:element name="Simulators" type="osp:simulators"/>
1717
<xs:element name="Functions" type="osp:functions" minOccurs="0"/>
1818
<xs:element name="Connections" type="osp:connections" minOccurs="0"/>
19+
<xs:element name="EccoConfiguration" type="osp:eccoconfiguration" minOccurs="0" maxOccurs="1"/>
1920
</xs:sequence>
2021
<xs:attribute name="version" type="xs:normalizedString" use="required" fixed="0.1">
2122
<xs:annotation>
@@ -24,13 +25,28 @@
2425
</xs:attribute>
2526
</xs:complexType>
2627
</xs:element>
28+
<xs:complexType name="eccoconfiguration">
29+
<xs:all>
30+
<xs:element name="SafetyFactor" type="xs:double"/>
31+
<xs:element name="StepSize" type="xs:double"/>
32+
<xs:element name="MinimumStepSize" type="xs:double"/>
33+
<xs:element name="MaximumStepSize" type="xs:double"/>
34+
<xs:element name="MinimumChangeRate" type="xs:double"/>
35+
<xs:element name="MaximumChangeRate" type="xs:double"/>
36+
<xs:element name="ProportionalGain" type="xs:double"/>
37+
<xs:element name="IntegralGain" type="xs:double"/>
38+
<xs:element name="RelativeTolerance" type="xs:double"/>
39+
<xs:element name="AbsoluteTolerance" type="xs:double"/>
40+
</xs:all>
41+
</xs:complexType>
2742
<xs:complexType name="connections">
2843
<xs:choice maxOccurs="unbounded">
2944
<xs:element name="VariableConnection" minOccurs="0" maxOccurs="unbounded">
3045
<xs:complexType>
3146
<xs:sequence>
3247
<xs:element name="Variable" type="osp:variableEndpoint" minOccurs="2" maxOccurs="2"/>
3348
</xs:sequence>
49+
<xs:attribute name="powerBond" use="optional" type="xs:string"/>
3450
</xs:complexType>
3551
</xs:element>
3652
<xs:element name="SignalConnection" minOccurs="0" maxOccurs="unbounded">
@@ -46,6 +62,7 @@
4662
<xs:sequence>
4763
<xs:element name="VariableGroup" type="osp:variableEndpoint" minOccurs="2" maxOccurs="2"/>
4864
</xs:sequence>
65+
<xs:attribute name="powerBond" use="optional" type="xs:string"/>
4966
</xs:complexType>
5067
</xs:element>
5168
<xs:element name="SignalGroupConnection" minOccurs="0" maxOccurs="unbounded">
@@ -61,6 +78,7 @@
6178
<xs:complexType name="variableEndpoint">
6279
<xs:attribute name="simulator" use="required" type="xs:string"/>
6380
<xs:attribute name="name" use="required" type="xs:string"/>
81+
<xs:attribute name="causality" type="xs:string"/>
6482
</xs:complexType>
6583
<xs:complexType name="signalEndpoint">
6684
<xs:attribute name="function" use="required" type="xs:string"/>

include/cosim/algorithm.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define COSIM_ALGORITHM_HPP
1212

1313
#include <cosim/algorithm/algorithm.hpp>
14+
#include <cosim/algorithm/ecco_algorithm.hpp>
1415
#include <cosim/algorithm/fixed_step_algorithm.hpp>
1516
#include <cosim/algorithm/simulator.hpp>
1617

include/cosim/algorithm/algorithm.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace cosim
4141
* 2. `initialize()`
4242
* 3. `do_step()` (possibly repeatedly)
4343
*/
44+
4445
class algorithm
4546
{
4647
public:
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* \file
3+
* Defines the class for a ECCO (Energy-Conservation-based Co-Simulation) algorithm
4+
*
5+
* \copyright
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
9+
*/
10+
#ifndef LIBCOSIM_ALGORITHM_ECCO_ALGORITHM_HPP
11+
#define LIBCOSIM_ALGORITHM_ECCO_ALGORITHM_HPP
12+
13+
#include <cosim/algorithm/algorithm.hpp>
14+
15+
namespace cosim
16+
{
17+
18+
struct ecco_algorithm_params
19+
{
20+
double safety_factor;
21+
duration step_size;
22+
duration min_step_size;
23+
duration max_step_size;
24+
double min_change_rate;
25+
double max_change_rate;
26+
double abs_tolerance;
27+
double rel_tolerance;
28+
double p_gain;
29+
double i_gain;
30+
};
31+
32+
/**
33+
* A fixed-stepsize co-simulation algorithm.
34+
*
35+
* A simple implementation of `algorithm`. The simulation progresses
36+
* at a fixed base stepsize. Simulators are stepped in parallel at an optional
37+
* multiple of this base step size.
38+
*/
39+
class ecco_algorithm : public algorithm
40+
{
41+
public:
42+
/**
43+
* Constructor.
44+
*
45+
* \param baseStepSize
46+
* The base communication interval length.
47+
*
48+
* \param workerThreadCount
49+
* The number of worker threads to spawn for running FMUs
50+
*/
51+
explicit ecco_algorithm(ecco_algorithm_params params, std::optional<unsigned int> workerThreadCount = std::nullopt);
52+
53+
~ecco_algorithm() noexcept;
54+
55+
ecco_algorithm(const ecco_algorithm&) = delete;
56+
ecco_algorithm& operator=(const ecco_algorithm&) = delete;
57+
58+
ecco_algorithm(ecco_algorithm&&) noexcept;
59+
ecco_algorithm& operator=(ecco_algorithm&&) noexcept;
60+
61+
// `algorithm` methods
62+
void add_simulator(simulator_index i, simulator* s, duration stepSizeHint) override;
63+
void remove_simulator(simulator_index i) override;
64+
void add_function(function_index i, function* f) override;
65+
void connect_variables(variable_id output, variable_id input) override;
66+
void connect_variables(variable_id output, function_io_id input) override;
67+
void connect_variables(function_io_id output, variable_id input) override;
68+
void disconnect_variable(variable_id input) override;
69+
void disconnect_variable(function_io_id input) override;
70+
void setup(time_point startTime, std::optional<time_point> stopTime) override;
71+
void initialize() override;
72+
std::pair<duration, std::unordered_set<simulator_index>> do_step(time_point currentT) override;
73+
serialization::node export_current_state() const override;
74+
void import_state(const serialization::node& exportedState) override;
75+
76+
/**
77+
* Adds a variable pair for the power residual calculation.
78+
* \param uVec
79+
* The index of the variable.
80+
*/
81+
void add_power_bond(cosim::variable_id input_a, cosim::variable_id output_a, cosim::variable_id input_b, cosim::variable_id output_b);
82+
83+
/**
84+
* Retrieves the energies in the power bond for the given simulator index.add_variable_value
85+
* \param simulator_index
86+
* The index of the simulator.
87+
*/
88+
std::vector<double> get_powerbond_energies(cosim::simulator_index simulator_index);
89+
90+
private:
91+
class impl;
92+
std::unique_ptr<impl> pimpl_;
93+
};
94+
95+
} // namespace cosim
96+
97+
#endif

include/cosim/algorithm/fixed_step_algorithm.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
namespace cosim
1616
{
1717

18+
struct fixed_step_algorithm_params
19+
{
20+
duration stepSize;
21+
};
22+
1823
/**
1924
* A fixed-stepsize co-simulation algorithm.
2025
*
@@ -35,6 +40,7 @@ class fixed_step_algorithm : public algorithm
3540
* The number of worker threads to spawn for running FMUs
3641
*/
3742
explicit fixed_step_algorithm(duration baseStepSize, std::optional<unsigned int> workerThreadCount = std::nullopt);
43+
explicit fixed_step_algorithm(fixed_step_algorithm_params params, std::optional<unsigned int> workerThreadCount = std::nullopt);
3844

3945
~fixed_step_algorithm() noexcept;
4046

include/cosim/execution.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ class execution
342342
/// Returns a map of currently modified variables
343343
std::vector<variable_id> get_modified_variables() const;
344344

345+
/// Returns the algorithm used in this execution
346+
std::shared_ptr<cosim::algorithm> get_algorithm() const;
347+
345348
/// Set initial value for a variable of type real. Must be called before simulation is started.
346349
void set_real_initial_value(simulator_index sim, value_reference var, double value);
347350

include/cosim/osp_config_parser.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LIBCOSIM_OSP_CONFIG_PARSER_HPP
1010
#define LIBCOSIM_OSP_CONFIG_PARSER_HPP
1111

12+
#include <cosim/algorithm.hpp>
1213
#include <cosim/model_description.hpp>
1314
#include <cosim/orchestration.hpp>
1415
#include <cosim/system_structure.hpp>
@@ -24,15 +25,15 @@ struct osp_config
2425
/// The system structure
2526
cosim::system_structure system_structure;
2627

28+
// The algorithm
29+
std::variant<cosim::fixed_step_algorithm_params, cosim::ecco_algorithm_params> algorithm_configuration;
30+
2731
/// The default start time for a simulation
2832
time_point start_time;
2933

3034
/// The optional end time for a simulation
3135
std::optional<time_point> end_time = std::nullopt;
3236

33-
/// The default/recommended step size for a simulation
34-
duration step_size;
35-
3637
/// A set of default initial values
3738
variable_value_map initial_values;
3839
};

include/cosim/system_structure.hpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ namespace cosim
3838
*/
3939
struct full_variable_name
4040
{
41+
/// Default constructor
42+
full_variable_name() = default;
43+
4144
/// Constructor for simulator variables.
4245
full_variable_name(std::string simulatorName, std::string variableName)
4346
: entity_name(std::move(simulatorName))
@@ -213,8 +216,25 @@ class system_structure
213216
full_variable_name target;
214217
};
215218

219+
/// Information about a powerbond connection. For use with the ecco algorithm only.
220+
struct power_bond
221+
{
222+
/// The input_a variable in the bond, according to the description of the ecco algorithm.
223+
full_variable_name input_a;
224+
225+
/// The output_a variable in the bond, according to the description of the ecco algorithm.
226+
full_variable_name output_a;
227+
228+
/// The input_b variable in the bond, according to the description of the ecco algorithm.
229+
full_variable_name input_b;
230+
231+
/// The output_b variable in the bond, according to the description of the ecco algorithm.
232+
full_variable_name output_b;
233+
};
234+
216235
private:
217236
using entity_map = std::unordered_map<std::string, entity>;
237+
using power_bond_map = std::unordered_map<std::string, power_bond>;
218238
using connection_map =
219239
std::unordered_map<full_variable_name, full_variable_name>;
220240
using connection_transform =
@@ -251,6 +271,10 @@ class system_structure
251271
add_entity({std::string(name), type, {}, std::move(parameters)});
252272
}
253273

274+
power_bond_map get_power_bonds() const noexcept;
275+
void add_power_bond(std::string name, power_bond pb);
276+
277+
254278
/**
255279
* Returns a list of the entities in the system.
256280
*
@@ -328,6 +352,8 @@ class system_structure
328352
// Connections. Target is key, source is value.
329353
connection_map connections_;
330354

355+
power_bond_map powerBonds_;
356+
331357
// Cache for fast lookup of model info, indexed by model UUID.
332358
struct model_info
333359
{

0 commit comments

Comments
 (0)