diff --git a/.gitignore b/.gitignore index aadaf1b1..9541e1f1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ cmake-build-*/ # Visual Studio Code .vscode/ -CMakeUserPresets.json \ No newline at end of file + +CMakeUserPresets.json diff --git a/data/xsd/OspSystemStructure.xsd b/data/xsd/OspSystemStructure.xsd index df4b864b..e3004db0 100644 --- a/data/xsd/OspSystemStructure.xsd +++ b/data/xsd/OspSystemStructure.xsd @@ -46,6 +46,7 @@ + diff --git a/src/cosim/osp_config_parser.cpp b/src/cosim/osp_config_parser.cpp index 674def3c..1bd118d8 100644 --- a/src/cosim/osp_config_parser.cpp +++ b/src/cosim/osp_config_parser.cpp @@ -120,20 +120,6 @@ class osp_config_parser const cosim::filesystem::path& configPath); ~osp_config_parser() noexcept; - struct EccoConfiguration - { - double safetyFactor = 0.99; - double stepSize = 1e-3; - double minimumStepSize = 1e-5; - double maximumStepSize = 1e-2; - double minimumChangeRate = 0.2; - double maximumChangeRate = 1.5; - double proportionalGain = 0.2; - double integralGain = 0.15; - double relativeTolerance = 1e-6; - double absoluteTolerance = 1e-6; - }; - struct SimulationInformation { std::string description; @@ -141,7 +127,7 @@ class osp_config_parser double stepSize = 0.1; double startTime = 0.0; std::optional endTime; - std::optional eccoConfiguration; + std::optional eccoConfiguration; }; const SimulationInformation& get_simulation_information() const; @@ -260,21 +246,6 @@ class osp_config_parser static bool parse_boolean_value(const std::string& s); }; -[[maybe_unused]] std::ostream& operator<<(std::ostream& os, const osp_config_parser::EccoConfiguration& cfg) -{ - return os << "Safety Factor: " << cfg.safetyFactor << std::endl - << "Step Size: " << cfg.stepSize << std::endl - << "Minimum Step Size: " << cfg.minimumStepSize << std::endl - << "Maximum Step Size: " << cfg.maximumStepSize << std::endl - << "Minimum Change Rate: " << cfg.minimumChangeRate << std::endl - << "Maximum Change Rate: " << cfg.maximumChangeRate << std::endl - << "Proportional Gain: " << cfg.proportionalGain << std::endl - << "Integral Gain: " << cfg.integralGain << std::endl - << "Relative Tolerance: " << cfg.relativeTolerance << std::endl - << "Absolute Tolerance: " << cfg.absoluteTolerance << std::endl; -} - - namespace { @@ -501,21 +472,20 @@ osp_config_parser::osp_config_parser( const auto relativeTolerance = eccoConfigurationElement->getElementsByTagName(tc("RelativeTolerance").get())->item(0)->getTextContent(); const auto absoluteTolerance = eccoConfigurationElement->getElementsByTagName(tc("AbsoluteTolerance").get())->item(0)->getTextContent(); - EccoConfiguration eccoConfig{}; - - eccoConfig.safetyFactor = boost::lexical_cast(tc(safetyFactor)); - eccoConfig.stepSize = boost::lexical_cast(tc(stepSize)); - eccoConfig.minimumStepSize = boost::lexical_cast(tc(minimumStepSize)); - eccoConfig.maximumStepSize = boost::lexical_cast(tc(maximumStepSize)); - eccoConfig.minimumChangeRate = boost::lexical_cast(tc(minimumChangeRate)); - eccoConfig.maximumChangeRate = boost::lexical_cast(tc(maximumChangeRate)); - eccoConfig.proportionalGain = boost::lexical_cast(tc(proportionalGain)); - eccoConfig.integralGain = boost::lexical_cast(tc(integralGain)); - eccoConfig.relativeTolerance = boost::lexical_cast(tc(relativeTolerance)); - eccoConfig.absoluteTolerance = boost::lexical_cast(tc(absoluteTolerance)); - - simulationInformation_.eccoConfiguration = eccoConfig; - std::cout << simulationInformation_.algorithm; + cosim::ecco_parameters eccoConfig{}; + + eccoConfig.safety_factor = boost::lexical_cast(tc(safetyFactor)); + eccoConfig.step_size = cosim::to_duration(boost::lexical_cast(tc(stepSize))); + eccoConfig.min_step_size = cosim::to_duration(boost::lexical_cast(tc(minimumStepSize))); + eccoConfig.max_step_size = cosim::to_duration(boost::lexical_cast(tc(maximumStepSize))); + eccoConfig.min_change_rate = boost::lexical_cast(tc(minimumChangeRate)); + eccoConfig.max_change_rate = boost::lexical_cast(tc(maximumChangeRate)); + eccoConfig.p_gain = boost::lexical_cast(tc(proportionalGain)); + eccoConfig.i_gain = boost::lexical_cast(tc(integralGain)); + eccoConfig.rel_tolerance = boost::lexical_cast(tc(relativeTolerance)); + eccoConfig.abs_tolerance = boost::lexical_cast(tc(absoluteTolerance)); + + simulationInformation_.eccoConfiguration = eccoConfig; } auto connectionsElement = static_cast(rootElement->getElementsByTagName(tc("Connections").get())->item(0)); @@ -1037,6 +1007,22 @@ osp_config load_osp_config( config.end_time = to_time_point(simInfo.endTime.value()); } + auto algorithm = simInfo.algorithm; + std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(), + [](unsigned char c) { return std::tolower(c); }); + + if (simInfo.algorithm == "ecco") { + if (simInfo.eccoConfiguration.has_value()) { + config.algorithm_configuration = simInfo.eccoConfiguration.value(); + } else { + throw std::invalid_argument("No configuration parameters found for ecco algorithm."); + } + } else if (simInfo.algorithm == "fixedstep") { + config.algorithm_configuration = cosim::fixed_step_configuration{simInfo.stepSize}; + } else { + throw std::invalid_argument("Invalid algorithm choice. Allowed values are fixedStep, ecco."); + } + auto simulators = parser.get_elements(); std::unordered_map emds; diff --git a/tests/data/ecco/quarter_truck/OspSystemStructure.xml b/tests/data/ecco/quarter_truck/OspSystemStructure.xml index e246e8db..22005133 100644 --- a/tests/data/ecco/quarter_truck/OspSystemStructure.xml +++ b/tests/data/ecco/quarter_truck/OspSystemStructure.xml @@ -34,13 +34,13 @@ - + - + - + @@ -51,6 +51,7 @@ 0.00001 0.01 0.2 + 1.5 0.2 0.15 diff --git a/tests/ecco_algorithm_from_system_structure_test.cpp b/tests/ecco_algorithm_from_system_structure_test.cpp index 879e34ca..118a7e41 100644 --- a/tests/ecco_algorithm_from_system_structure_test.cpp +++ b/tests/ecco_algorithm_from_system_structure_test.cpp @@ -1,16 +1,17 @@ #include +#include #include +#include #include #include -#include -#include #include #include +#include +#include #include #include #include -#include // A helper macro to test various assertions #define REQUIRE(test) \ @@ -24,11 +25,43 @@ int main() cosim::log::setup_simple_console_logging(); cosim::log::set_global_output_level(cosim::log::debug); + constexpr cosim::time_point endTime = cosim::to_time_point(10.0); + auto resolver = cosim::default_model_uri_resolver(); auto configPath = cosim::filesystem::path(testDataDir) / "ecco" / "quarter_truck"; - std::cout << "Config path is " << configPath << std::endl; - + const auto logXmlPath = cosim::filesystem::path(testDataDir) / "ecco" / "quarter_truck" / "LogConfig.xml"; const auto config = cosim::load_osp_config(configPath, *resolver); + + auto ecco_params = std::get(config.algorithm_configuration); + auto ecco_algo = std::make_shared(ecco_params); + + auto execution = cosim::execution(config.start_time, ecco_algo); + + const auto entityMaps = cosim::inject_system_structure(execution, config.system_structure, config.initial_values); + const auto realTimeConfig = execution.get_real_time_config(); + + REQUIRE(entityMaps.simulators.size() == 2); + REQUIRE(!realTimeConfig->real_time_simulation); + + auto chassisIndex = entityMaps.simulators.at("chassis"); + auto wheelIndex = entityMaps.simulators.at("wheel"); + + auto chassisForce = cosim::variable_id{chassisIndex, cosim::variable_type::real, 19}; + auto chassisVel = cosim::variable_id{chassisIndex, cosim::variable_type::real, 22}; + auto wheelCForce = cosim::variable_id{wheelIndex, cosim::variable_type::real, 26}; + auto wheelCVel = cosim::variable_id{wheelIndex, cosim::variable_type::real, 24}; + + ecco_algo->add_power_bond(chassisVel, chassisForce, wheelCForce, wheelCVel); + + const auto logPath = cosim::filesystem::current_path() / "logs"; + std::cout << "Log path:" << logPath.c_str(); + auto file_obs = std::make_unique(logPath, logXmlPath); + execution.add_observer(std::move(file_obs)); + + auto simResult = execution.simulate_until(endTime); + REQUIRE(simResult); + + } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; diff --git a/tests/ecco_algorithm_multi_bond_test.cpp b/tests/ecco_algorithm_multi_bond_test.cpp index d0679a30..a7d01b7f 100644 --- a/tests/ecco_algorithm_multi_bond_test.cpp +++ b/tests/ecco_algorithm_multi_bond_test.cpp @@ -2,9 +2,9 @@ #include #include +#include #include #include -#include #include #include @@ -23,7 +23,8 @@ int main() cosim::log::setup_simple_console_logging(); cosim::log::set_global_output_level(cosim::log::info); - constexpr cosim::time_point startTime = cosim::to_time_point(0.0);; + constexpr cosim::time_point startTime = cosim::to_time_point(0.0); + ; constexpr cosim::time_point midTime = cosim::to_time_point(4.0); auto ecco_params = cosim::ecco_parameters{ @@ -38,12 +39,8 @@ int main() 0.2, 0.15}; - // Set up an ecco algo auto ecco_algo = std::make_shared(ecco_params); -// auto fs_algo = std::make_shared(cosim::to_duration(1e-4)); - const auto testDataDir = std::getenv("TEST_DATA_DIR"); - //const auto configPath = cosim::filesystem::path("C:/dev/osp/demo-cases/quarter-truck/OspSystemStructure.xml"); const auto configPath = cosim::filesystem::path(testDataDir) / "ecco" / "quarter_truck" / "OspSystemStructure.xml"; const auto logXmlPath = cosim::filesystem::path(testDataDir) / "ecco" / "quarter_truck" / "LogConfig.xml"; @@ -52,50 +49,47 @@ int main() auto execution = cosim::execution( config.start_time, ecco_algo); -// fs_algo); - const auto entityMaps = cosim::inject_system_structure( - execution, config.system_structure, config.initial_values); + const auto entityMaps = cosim::inject_system_structure(execution, config.system_structure, config.initial_values); REQUIRE(entityMaps.simulators.size() == 2); -// REQUIRE(boost::size(config.system_structure.connections()) == expectedNumConnections); // Default should not be real time const auto realTimeConfig = execution.get_real_time_config(); REQUIRE(!realTimeConfig->real_time_simulation); - //auto chassisIndex = entityMaps.simulators.at("chassis"); - //auto wheelIndex = entityMaps.simulators.at("wheel"); - //auto groundIndex = entityMaps.simulators.at("ground"); + // auto chassisIndex = entityMaps.simulators.at("chassis"); + // auto wheelIndex = entityMaps.simulators.at("wheel"); + // auto groundIndex = entityMaps.simulators.at("ground"); auto chassisIndex = entityMaps.simulators.at("chassis"); auto wheelIndex = entityMaps.simulators.at("wheel"); - //auto gravity = cosim::variable_id{chassisIndex, cosim::variable_type::real, 2}; + // auto gravity = cosim::variable_id{chassisIndex, cosim::variable_type::real, 2}; auto chassisForce = cosim::variable_id{chassisIndex, cosim::variable_type::real, 19}; auto chassisVel = cosim::variable_id{chassisIndex, cosim::variable_type::real, 22}; auto wheelCForce = cosim::variable_id{wheelIndex, cosim::variable_type::real, 26}; auto wheelCVel = cosim::variable_id{wheelIndex, cosim::variable_type::real, 24}; - //auto wheelGForce = cosim::variable_id{wheelIndex, cosim::variable_type::real, 17}; - //auto wheelGVel = cosim::variable_id{wheelIndex, cosim::variable_type::real, 18}; - //auto groundForce = cosim::variable_id{groundIndex, cosim::variable_type::real, 11}; - //auto groundVel = cosim::variable_id{groundIndex, cosim::variable_type::real, 12}; + // auto wheelGForce = cosim::variable_id{wheelIndex, cosim::variable_type::real, 17}; + // auto wheelGVel = cosim::variable_id{wheelIndex, cosim::variable_type::real, 18}; + // auto groundForce = cosim::variable_id{groundIndex, cosim::variable_type::real, 11}; + // auto groundVel = cosim::variable_id{groundIndex, cosim::variable_type::real, 12}; // a = chassis b = wheel // a = wheel b = ground // u_a = y_b --- u_b = y_a // u_a y_a u_b y_b ecco_algo->add_power_bond(chassisVel, chassisForce, wheelCForce, wheelCVel); // chassis -> wheel (chassis port) - //ecco_algo->add_power_bond(wheelGVel, wheelGForce, groundForce, groundVel); // wheel -> ground (ground port) + // ecco_algo->add_power_bond(wheelGVel, wheelGForce, groundForce, groundVel); // wheel -> ground (ground port) - auto file_obs = std::make_unique("./logDir", logXmlPath); - execution.add_observer(std::move(file_obs)); + auto file_obs = std::make_unique("./logDir", logXmlPath); + execution.add_observer(std::move(file_obs)); - // auto simResult = execution.simulate_until(midTime); - // REQUIRE(simResult); + // auto simResult = execution.simulate_until(midTime); + // REQUIRE(simResult); - // Add an observer that watches the last slave + // Add an observer that watches the last slave auto t_observer = std::make_shared(50000); execution.add_observer(t_observer); t_observer->start_observing(chassisVel);