Skip to content

Commit 8dfb6b4

Browse files
authored
expand testing to include fmi3 (#39)
1 parent 4494333 commit 8dfb6b4

7 files changed

Lines changed: 348 additions & 15 deletions

File tree

export/tests/CMakeLists.txt

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,42 @@
11

2-
function(make_test modelIdentifier sources)
3-
add_executable(${modelIdentifier} ${sources} "$<TARGET_OBJECTS:fmu4cpp_base>" "$<TARGET_OBJECTS:fmu4cpp_fmi2>")
4-
add_test(NAME ${modelIdentifier} COMMAND ${modelIdentifier})
5-
target_link_libraries(${modelIdentifier} PUBLIC Catch2::Catch2WithMain)
6-
target_compile_definitions(${modelIdentifier}
2+
function(make_test fmi_version modelIdentifier sources)
3+
set(target_name "${modelIdentifier}_${fmi_version}")
4+
add_executable(${target_name} ${sources} "$<TARGET_OBJECTS:fmu4cpp_base>" "$<TARGET_OBJECTS:fmu4cpp_${fmi_version}>")
5+
add_test(NAME ${target_name} COMMAND ${target_name})
6+
target_link_libraries(${target_name} PUBLIC Catch2::Catch2WithMain)
7+
target_compile_definitions(${target_name}
78
PRIVATE
89
FMU4CPP_MODEL_IDENTIFIER="${modelIdentifier}"
9-
TEST_CASE_RESOURCE_LOCATION="${CMAKE_CURRENT_SOURCE_DIR}/resources"
10+
TEST_CASE_RESOURCE_LOCATION="${PROJECT_SOURCE_DIR}/export/tests/resources"
1011
)
11-
target_include_directories(${modelIdentifier}
12+
target_include_directories(${target_name}
1213
PRIVATE
13-
"${CMAKE_CURRENT_SOURCE_DIR}/../include"
14-
"${CMAKE_CURRENT_SOURCE_DIR}/../src"
14+
"${PROJECT_SOURCE_DIR}/export/include"
15+
"${PROJECT_SOURCE_DIR}/export/src"
1516
)
1617

1718
endfunction()
1819

19-
make_test(array_test array_test.cpp)
20-
make_test(basic_test basic_test.cpp)
21-
make_test(identity_test identity_test.cpp)
22-
make_test(test_resource test_resource.cpp)
20+
function(make_generic_test modelIdentifier sources)
21+
set(target_name "${modelIdentifier}")
22+
add_executable(${target_name} ${sources} "$<TARGET_OBJECTS:fmu4cpp_base>")
23+
add_test(NAME ${target_name} COMMAND ${target_name})
24+
target_link_libraries(${target_name} PUBLIC Catch2::Catch2WithMain)
25+
target_compile_definitions(${target_name}
26+
PRIVATE
27+
FMU4CPP_MODEL_IDENTIFIER="${modelIdentifier}"
28+
TEST_CASE_RESOURCE_LOCATION="${PROJECT_SOURCE_DIR}/export/tests/resources"
29+
)
30+
target_include_directories(${target_name}
31+
PRIVATE
32+
"${PROJECT_SOURCE_DIR}/export/include"
33+
"${PROJECT_SOURCE_DIR}/export/src"
34+
)
35+
36+
endfunction()
37+
38+
make_generic_test(basic_test basic_test.cpp)
39+
make_generic_test(test_resource test_resource.cpp)
40+
41+
add_subdirectory(fmi2)
42+
add_subdirectory(fmi3)

export/tests/fmi2/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
make_test("fmi2" array_test array_test.cpp)
3+
make_test("fmi2" identity_test identity_test.cpp)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ TEST_CASE("test_array") {
6969
auto c = fmi2Instantiate("array", fmi2CoSimulation, guid.c_str(), "", &callbackFunctions, false, true);
7070
REQUIRE(c);
7171

72+
REQUIRE(fmi2SetupExperiment(c, false, 0, 0, false, 0) == fmi2OK);
7273
REQUIRE(fmi2EnterInitializationMode(c) == fmi2OK);
7374
REQUIRE(fmi2ExitInitializationMode(c) == fmi2OK);
74-
REQUIRE(fmi2SetupExperiment(c, false, 0, 0, false, 0) == fmi2OK);
7575

7676
std::vector<double> values(4);
7777
for (int i = 1; i <= 4; i++) {// account for time
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ TEST_CASE("test_identity") {
177177
const auto c = fmi2Instantiate("identity", fmi2CoSimulation, guid.c_str(), "", &callbackFunctions, false, true);
178178
REQUIRE(c);
179179

180+
REQUIRE(fmi2SetupExperiment(c, false, 0, 0, false, 0) == fmi2OK);
180181
REQUIRE(fmi2EnterInitializationMode(c) == fmi2OK);
181182
REQUIRE(fmi2ExitInitializationMode(c) == fmi2OK);
182-
REQUIRE(fmi2SetupExperiment(c, false, 0, 0, false, 0) == fmi2OK);
183183

184184
REQUIRE(readReal(c, realOut->value_reference()) == Catch::Approx(0));
185185
REQUIRE(readString(c, stringOut->value_reference()) == "empty");

export/tests/fmi3/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
make_test("fmi3" array_test array_test.cpp)
3+
make_test("fmi3" identity_test identity_test.cpp)

export/tests/fmi3/array_test.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
2+
#include "fmi3/fmi3Functions.h"
3+
#include "fmi3/fmi3Functions.h"
4+
5+
#include <catch2/catch_approx.hpp>
6+
#include <catch2/catch_test_macros.hpp>
7+
#include <catch2/matchers/catch_matchers_vector.hpp>
8+
9+
#include <cstdarg>
10+
#include <iostream>
11+
12+
#include <fmu4cpp/fmu_base.hpp>
13+
14+
class Model : public fmu4cpp::fmu_base {
15+
16+
public:
17+
explicit Model(const fmu4cpp::fmu_data &data)
18+
: fmu_base(data), reals_(4) {
19+
20+
for (int i = 0; i < reals_.size(); i++) {
21+
register_variable(
22+
real("real[" + std::to_string(i) + "]", [this, i] { return reals_[i]; }, [this, i](double val) { reals_[i] = val; }).setCausality(fmu4cpp::causality_t::PARAMETER).setVariability(fmu4cpp::variability_t::TUNABLE));
23+
}
24+
25+
Model::reset();
26+
}
27+
28+
bool do_step(double dt) override {
29+
return true;
30+
}
31+
32+
void reset() override {
33+
reals_.assign({1, 2, 3, 4});
34+
}
35+
36+
private:
37+
std::vector<double> reals_;
38+
};
39+
40+
fmu4cpp::model_info fmu4cpp::get_model_info() {
41+
model_info info;
42+
info.modelName = "Array";
43+
info.description = "A simple model with arrays";
44+
info.modelIdentifier = FMU4CPP_MODEL_IDENTIFIER;
45+
return info;
46+
}
47+
48+
FMU4CPP_INSTANTIATE(Model);
49+
50+
51+
void fmilogger(fmi3InstanceEnvironment, fmi3Status status, fmi3String /*category*/, fmi3String message) {
52+
53+
std::cout << status << ": " << message << std::endl;
54+
}
55+
56+
TEST_CASE("test_array") {
57+
58+
Model model({});
59+
const auto guid = model.guid();
60+
61+
auto c = fmi3InstantiateCoSimulation("array", guid.c_str(), "", false, true, false, false, nullptr, 0, nullptr, fmilogger, nullptr);
62+
REQUIRE(c);
63+
64+
REQUIRE(fmi3EnterInitializationMode(c,false, 0, 0, false, 0) == fmi3OK);
65+
REQUIRE(fmi3ExitInitializationMode(c) == fmi3OK);
66+
67+
68+
std::vector<double> values(4);
69+
for (int i = 1; i <= 4; i++) {// account for time
70+
fmi3ValueReference ref = i;
71+
fmi3GetFloat64(c, &ref, 1, &values[i - 1], 1);
72+
}
73+
REQUIRE(values == std::vector<double>{1, 2, 3, 4});
74+
75+
for (int i = 1; i <= 4; i++) {
76+
fmi3ValueReference ref = i;
77+
fmi3Float64 val = 9;
78+
fmi3SetFloat64(c, &ref, 1, &val, 1);
79+
}
80+
81+
for (int i = 1; i <= 4; i++) {
82+
fmi3ValueReference ref = i;
83+
fmi3GetFloat64(c, &ref, 1, &values[i - 1], 1);
84+
}
85+
REQUIRE(values == std::vector<double>{9, 9, 9, 9});
86+
87+
88+
REQUIRE(fmi3Terminate(c) == fmi3OK);
89+
90+
fmi3FreeInstance(c);
91+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
2+
#include "catch2/matchers/catch_matchers_vector.hpp"
3+
#include "fmi3/fmi3Functions.h"
4+
5+
6+
#include <catch2/catch_approx.hpp>
7+
#include <catch2/catch_test_macros.hpp>
8+
#include <cstdarg>
9+
#include <iostream>
10+
11+
#include <fmu4cpp/fmu_base.hpp>
12+
13+
class Model : public fmu4cpp::fmu_base {
14+
15+
public:
16+
explicit Model(const fmu4cpp::fmu_data &data) : fmu_base(data) {
17+
18+
register_variable(integer("integerIn", &integer_)
19+
.setCausality(fmu4cpp::causality_t::INPUT)
20+
.setVariability(fmu4cpp::variability_t::DISCRETE));
21+
22+
register_variable(real("realIn", &real_)
23+
.setCausality(fmu4cpp::causality_t::INPUT)
24+
.setVariability(fmu4cpp::variability_t::DISCRETE));
25+
26+
register_variable(boolean("booleanIn", &boolean_)
27+
.setCausality(fmu4cpp::causality_t::INPUT)
28+
.setVariability(fmu4cpp::variability_t::DISCRETE));
29+
30+
register_variable(string("stringIn", &string_)
31+
.setCausality(fmu4cpp::causality_t::INPUT)
32+
.setVariability(fmu4cpp::variability_t::DISCRETE));
33+
34+
35+
register_variable(integer("integerOut", &integer_)
36+
.setCausality(fmu4cpp::causality_t::OUTPUT)
37+
.setVariability(fmu4cpp::variability_t::DISCRETE)
38+
.setInitial(fmu4cpp::initial_t::CALCULATED)
39+
.setDependencies({"integerIn"}));
40+
41+
register_variable(real("realOut", &real_)
42+
.setCausality(fmu4cpp::causality_t::OUTPUT)
43+
.setVariability(fmu4cpp::variability_t::DISCRETE)
44+
.setInitial(fmu4cpp::initial_t::CALCULATED)
45+
.setDependencies({"realIn"}));
46+
47+
register_variable(boolean("booleanOut", &boolean_)
48+
.setCausality(fmu4cpp::causality_t::OUTPUT)
49+
.setVariability(fmu4cpp::variability_t::DISCRETE)
50+
.setInitial(fmu4cpp::initial_t::CALCULATED)
51+
.setDependencies({"booleanIn"}));
52+
53+
register_variable(string("stringOut", [this] { return string_; })
54+
.setCausality(fmu4cpp::causality_t::OUTPUT)
55+
.setVariability(fmu4cpp::variability_t::DISCRETE)
56+
.setInitial(fmu4cpp::initial_t::CALCULATED)
57+
.setDependencies({"stringIn"}));
58+
59+
Model::reset();
60+
}
61+
62+
bool do_step(double dt) override {
63+
return true;
64+
}
65+
66+
void reset() override {
67+
integer_ = 0;
68+
real_ = 0;
69+
boolean_ = false;
70+
string_ = "empty";
71+
}
72+
73+
private:
74+
int integer_;
75+
double real_;
76+
bool boolean_;
77+
std::string string_;
78+
};
79+
80+
fmu4cpp::model_info fmu4cpp::get_model_info() {
81+
model_info info;
82+
info.modelName = "Identity";
83+
info.description = "A simple feed-trough model";
84+
info.modelIdentifier = FMU4CPP_MODEL_IDENTIFIER;
85+
return info;
86+
}
87+
88+
FMU4CPP_INSTANTIATE(Model);
89+
90+
91+
int readInt(fmi3Instance c, fmi3ValueReference ref) {
92+
fmi3Int32 value;
93+
REQUIRE(fmi3GetInt32(c, &ref, 1, &value, 1) == fmi3OK);
94+
95+
return value;
96+
}
97+
98+
void setInt(fmi3Instance c, fmi3ValueReference ref, int value) {
99+
REQUIRE(fmi3SetInt32(c, &ref, 1, &value, 1) == fmi3OK);
100+
}
101+
102+
double readReal(fmi3Instance c, fmi3ValueReference ref) {
103+
fmi3Float64 value;
104+
REQUIRE(fmi3GetFloat64(c, &ref, 1, &value, 0) == fmi3OK);
105+
106+
return value;
107+
}
108+
109+
void setReal(fmi3Instance c, fmi3ValueReference ref, double value) {
110+
REQUIRE(fmi3SetFloat64(c, &ref, 1, &value, 0) == fmi3OK);
111+
}
112+
113+
bool readBool(fmi3Instance c, fmi3ValueReference ref) {
114+
fmi3Boolean value;
115+
REQUIRE(fmi3GetBoolean(c, &ref, 1, &value, 0) == fmi3OK);
116+
117+
return value;
118+
}
119+
120+
void setBool(fmi3Instance c, fmi3ValueReference ref, bool value) {
121+
fmi3Boolean value_ = value;
122+
REQUIRE(fmi3SetBoolean(c, &ref, 1, &value_, 0) == fmi3OK);
123+
}
124+
125+
126+
std::string readString(fmi3Instance c, fmi3ValueReference ref) {
127+
fmi3String value;
128+
REQUIRE(fmi3GetString(c, &ref, 1, &value, 0) == fmi3OK);
129+
130+
return value;
131+
}
132+
133+
void setString(fmi3Instance c, fmi3ValueReference ref, const std::string &value) {
134+
fmi3String value_ = value.c_str();
135+
REQUIRE(fmi3SetString(c, &ref, 1, &value_, 0) == fmi3OK);
136+
}
137+
138+
void setOutputFail(fmi3Instance c) {
139+
fmi3ValueReference ref = 999;// out of bounds
140+
fmi3Int32 i = 0;
141+
fmi3String s = "";
142+
fmi3Float64 r = 0;
143+
fmi3Boolean b = fmi3False;
144+
145+
REQUIRE(fmi3SetInt32(c, &ref, 1, &i, 0) == fmi3Error);
146+
REQUIRE(fmi3SetFloat64(c, &ref, 1, &r, 0) == fmi3Error);
147+
REQUIRE(fmi3SetString(c, &ref, 1, &s, 0) == fmi3Error);
148+
REQUIRE(fmi3SetBoolean(c, &ref, 1, &b, 0) == fmi3Error);
149+
}
150+
151+
void fmilogger(fmi3InstanceEnvironment, fmi3Status status, fmi3String /*category*/, fmi3String message) {
152+
153+
std::cout << status << ": " << message << std::endl;
154+
}
155+
156+
TEST_CASE("test_identity") {
157+
158+
Model model({});
159+
const auto guid = model.guid();
160+
161+
const auto realIn = model.get_real_variable("realIn");
162+
const auto stringIn = model.get_string_variable("stringIn");
163+
const auto integerIn = model.get_int_variable("integerIn");
164+
const auto booleanIn = model.get_bool_variable("booleanIn");
165+
166+
const auto realOut = model.get_real_variable("realOut");
167+
const auto stringOut = model.get_string_variable("stringOut");
168+
const auto integerOut = model.get_int_variable("integerOut");
169+
const auto booleanOut = model.get_bool_variable("booleanOut");
170+
171+
const auto c = fmi3InstantiateCoSimulation("identity", guid.c_str(), "", false, true, false, false, nullptr, 0, nullptr, fmilogger, nullptr);
172+
REQUIRE(c);
173+
174+
REQUIRE(fmi3EnterInitializationMode(c, false, 0, 0, false, 0) == fmi3OK);
175+
REQUIRE(fmi3ExitInitializationMode(c) == fmi3OK);
176+
177+
REQUIRE(readReal(c, realOut->value_reference()) == Catch::Approx(0));
178+
REQUIRE(readString(c, stringOut->value_reference()) == "empty");
179+
REQUIRE(readInt(c, integerOut->value_reference()) == 0);
180+
REQUIRE(readBool(c, booleanOut->value_reference()) == false);
181+
182+
double t{0};
183+
const double dt{0.1};
184+
185+
bool b{false};
186+
int counter{0};
187+
while (t < 1) {
188+
189+
setInt(c, integerIn->value_reference(), counter);
190+
setReal(c, realIn->value_reference(), t);
191+
setString(c, stringIn->value_reference(), std::to_string(t));
192+
setBool(c, booleanIn->value_reference(), b);
193+
194+
bool eventhandlingNeeded;
195+
bool terminateSimulation;
196+
bool earlyReturn;
197+
double lastSucessfulTime;
198+
199+
REQUIRE(fmi3DoStep(c, t, dt, true, &eventhandlingNeeded, &terminateSimulation, &earlyReturn, &lastSucessfulTime) == fmi3OK);
200+
201+
REQUIRE(readReal(c, realOut->value_reference()) == Catch::Approx(t));
202+
REQUIRE(readString(c, stringOut->value_reference()) == std::to_string(t));
203+
REQUIRE(readInt(c, integerOut->value_reference()) == counter);
204+
REQUIRE(readBool(c, booleanOut->value_reference()) == b);
205+
206+
t += dt;
207+
counter++;
208+
b = !b;
209+
}
210+
211+
setOutputFail(c);
212+
213+
REQUIRE(fmi3Terminate(c) == fmi3OK);
214+
215+
fmi3FreeInstance(c);
216+
}

0 commit comments

Comments
 (0)