Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ set(LIBCOSIMC_EXPORT_TARGET "${PROJECT_NAME}-targets")
# ==============================================================================
# Dependencies
# ==============================================================================

find_package(libcosim REQUIRED)
find_package(libcbor REQUIRED) # To avoid cmake config error

# ==============================================================================
# Targets
Expand Down Expand Up @@ -188,6 +188,7 @@ if(LIBCOSIMC_BUILD_TESTS)
"single_fmu_execution_test"
"time_series_observer_test"
"variable_metadata_test"
"ecco_algorithm_multi_bond_test"
)

foreach(testName IN LISTS tests)
Expand Down
6 changes: 3 additions & 3 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
from conan.tools.env import VirtualRunEnv
from conan.tools.files import load
from conan.tools.files import load, copy


class LibCosimCConan(ConanFile):
Expand Down Expand Up @@ -45,15 +45,15 @@ def configure(self):
"doxygen/1.9.1",
)
requires = (
"libcosim/0.10.4@osp/stable",
"libcosim/0.11.0@osp/stable",
)

# Exports
exports = "version.txt"
exports_sources = "*"

# Build steps
generators = "CMakeDeps", "CMakeToolchain"
generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv"

def layout(self):
cmake_layout(self)
Expand Down
83 changes: 82 additions & 1 deletion include/cosim.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ typedef uint32_t cosim_value_reference;
typedef int cosim_slave_index;

/// Step number
typedef long long cosim_step_number;
typedef int64_t cosim_step_number;

/// Error codes.
typedef enum
Expand Down Expand Up @@ -128,6 +128,11 @@ struct cosim_execution_s;
typedef struct cosim_execution_s cosim_execution;


struct cosim_algorithm_s;

/// An opaque object which contains the configuration for a cosimulation algorithm.
typedef struct cosim_algorithm_s cosim_algorithm;

/**
* Creates a new execution.
*
Expand All @@ -143,6 +148,82 @@ cosim_execution* cosim_execution_create(
cosim_time_point startTime,
cosim_duration stepSize);

/**
* Creates an ecco algorithm with the specified parameters.
* \param [in] safetyFactor Safety factor
* \param [in] stepSize Initial step size
* \param [in] minStepSize Minimum step size
* \param [in] maxStepSize Maximum step size
* \param [in] minChangeRate Minimum rate of change in step size
* \param [in] maxChangeRate Maximum rate of change in step size
* \param [in] absTolerance Absolute tolerance for deciding mismatch in the residual power
* \param [in] relTolerance Relative tolerance for deciding mismatch in the residual power
* \param [in] pGain Proportional value in the PI controller
* \param [in] iGain Integral value in the PI controller
* \returns A pointer to a new instance of cosim_algorithm, or NULL if an error occurred.
*/
cosim_algorithm* cosim_ecco_algorithm_create(
double safetyFactor,
double stepSize,
double minStepSize,
double maxStepSize,
double minChangeRate,
double maxChangeRate,
double absTolerance,
double relTolerance,
double pGain,
double iGain);

/**
* Creates a power bond between two instances of models
* \param [in] Ecco An algorithm instance
* \param [in] index1 Slave index for the first model
* \param [in] v1 The output of the first model
* \param [in] u1 The input of the first model
* \param [in] index2 Slave index Id for the second model
* \param [in] v2 The output of the second model
* \param [in] u2 The input of the second model
* \returns
* 0 on success and -1 on error.
*/
int cosim_ecco_add_power_bond(
cosim_algorithm* algo,
cosim_slave_index m1Index,
cosim_value_reference v1,
cosim_value_reference u1,
cosim_slave_index m2Index,
cosim_value_reference v2,
cosim_value_reference u2);

/**
* Creates a fixed step algorithm
* \param [in] stepSize
* The execution step size.
* \returns A pointer to a new instance of cosim_algorithm, or NULL if an error occurred.
*/
cosim_algorithm* cosim_fixed_step_algorithm_create(cosim_duration stepSize);

/**
* Destroys a co-simulation algorithm .
*
* \returns
* 0 on success and -1 on error.
*/
int cosim_algorithm_destroy(cosim_algorithm* algorithm);

/**
* Creates a new execution with the given co-simulation algorithm.
*
* \param [in] startTime
* The (logical) time point at which the simulation should start.
* \param [in] algo*
* Co-simulation algorithm object
* \returns
* A pointer to an object which holds the execution state,
* or NULL on error.
*/
cosim_execution* cosim_execution_create_with_algorithm(cosim_time_point startTime, cosim_algorithm* algo);

/**
* Creates a new execution based on an OspSystemStructure.xml file.
*
Expand Down
135 changes: 134 additions & 1 deletion src/cosim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <cosim.h>
#include <cosim/algorithm.hpp>
#include <cosim/algorithm/ecco_algorithm.hpp>
#include <cosim/exception.hpp>
#include <cosim/execution.hpp>
#include <cosim/fmi/fmu.hpp>
Expand Down Expand Up @@ -145,6 +146,80 @@ const char* cosim_last_error_message()
return g_lastErrorMessage.c_str();
}


enum AlgorithmType
{
FIXED_STEP,
ECCO
};

struct cosim_algorithm_s
{
std::shared_ptr<cosim::algorithm> algorithm;
AlgorithmType type;
};

cosim_algorithm* cosim_ecco_algorithm_create(
double safetyFactor,
double stepSize,
double minStepSize,
double maxStepSize,
double minChangeRate,
double maxChangeRate,
double absTolerance,
double relTolerance,
double pGain,
double iGain)
{
try{
cosim::ecco_algorithm_params ecco_params = {
safetyFactor,
cosim::to_duration(stepSize),
cosim::to_duration(minStepSize),
cosim::to_duration(maxStepSize),
minChangeRate,
maxChangeRate,
absTolerance,
relTolerance,
pGain,
iGain};

auto algo = std::make_unique<cosim_algorithm>();
algo->algorithm = std::make_shared<cosim::ecco_algorithm>(ecco_params);
algo->type = ECCO;
return algo.release();
} catch (...) {
handle_current_exception();
return nullptr;
}
}

cosim_algorithm* cosim_fixed_step_algorithm_create(cosim_duration stepSize)
{
try{
auto algo = std::make_unique<cosim_algorithm>();
algo->algorithm = std::make_shared<cosim::fixed_step_algorithm>(to_duration(stepSize));
algo->type = FIXED_STEP;
return algo.release();
} catch (...) {
handle_current_exception();
return nullptr;
}
}

int cosim_algorithm_destroy(cosim_algorithm* algorithm)
{
try {
if (!algorithm) return success;
const auto owned = std::unique_ptr<cosim_algorithm>(algorithm);
return success;
} catch (...) {
handle_current_exception();
return failure;
}
}


struct cosim_execution_s
{
std::unique_ptr<cosim::execution> cpp_execution;
Expand All @@ -158,6 +233,28 @@ struct cosim_execution_s
int error_code;
};

cosim_execution* cosim_execution_create_with_algorithm(cosim_time_point startTime, cosim_algorithm* algo)
{
try {
// No exceptions are possible right now, so try...catch and unique_ptr
// are strictly unnecessary, but this will change soon enough.
auto execution = std::make_unique<cosim_execution>();

execution->cpp_execution = std::make_unique<cosim::execution>(
to_time_point(startTime),
algo->algorithm);
execution->real_time_config = execution->cpp_execution->get_real_time_config();
execution->real_time_metrics = execution->cpp_execution->get_real_time_metrics();
execution->error_code = COSIM_ERRC_SUCCESS;
execution->state = COSIM_EXECUTION_STOPPED;

return execution.release();
} catch (...) {
handle_current_exception();
return nullptr;
}
}

cosim_execution* cosim_execution_create(cosim_time_point startTime, cosim_duration stepSize)
{
try {
Expand Down Expand Up @@ -190,10 +287,20 @@ cosim_execution* cosim_osp_config_execution_create(

auto resolver = cosim::default_model_uri_resolver();
const auto config = cosim::load_osp_config(configPath, *resolver);
std::shared_ptr<cosim::algorithm> algorithm;

std::visit([&algorithm, &config](auto&& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, cosim::fixed_step_algorithm_params>) {
algorithm = std::make_shared<cosim::fixed_step_algorithm>(std::get<cosim::fixed_step_algorithm_params>(config.algorithm_configuration));
} else if constexpr (std::is_same_v<T, cosim::ecco_algorithm_params>) {
algorithm = std::make_shared<cosim::ecco_algorithm>(std::get<cosim::ecco_algorithm_params>(config.algorithm_configuration));
}
}, config.algorithm_configuration);

execution->cpp_execution = std::make_unique<cosim::execution>(
startTimeDefined ? to_time_point(startTime) : config.start_time,
std::make_shared<cosim::fixed_step_algorithm>(config.step_size));
algorithm);
execution->entity_maps = cosim::inject_system_structure(
*execution->cpp_execution,
config.system_structure,
Expand All @@ -210,6 +317,32 @@ cosim_execution* cosim_osp_config_execution_create(
}
}

int cosim_ecco_add_power_bond(
cosim_algorithm* algo,
cosim_slave_index index1,
cosim_value_reference v1,
cosim_value_reference u1,
cosim_slave_index index2,
cosim_value_reference v2,
cosim_value_reference u2)
{
try {
auto* ecco_algorithm = dynamic_cast<cosim::ecco_algorithm*>(algo->algorithm.get());
if (!ecco_algorithm) {
throw std::invalid_argument("Invalid algorithm type. Expected ecco_algorithm.");
}
auto v1id = cosim::variable_id{index1, cosim::variable_type::real, v1};
auto u1id = cosim::variable_id{index1, cosim::variable_type::real, u1};
auto v2id = cosim::variable_id{index2, cosim::variable_type::real, v2};
auto u2id = cosim::variable_id{index2, cosim::variable_type::real, u2};
ecco_algorithm->add_power_bond(v1id, u1id, v2id, u2id);
return success;
} catch (...) {
handle_current_exception();
return failure;
}
}


cosim_execution* cosim_ssp_execution_create(
const char* sspDir,
Expand Down
Binary file added tests/data/fmi2/quarter_truck/Chassis.fmu
Binary file not shown.
7 changes: 7 additions & 0 deletions tests/data/fmi2/quarter_truck/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright 2025 SINTEF Nordvest

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 changes: 13 additions & 0 deletions tests/data/fmi2/quarter_truck/LogConfig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<simulators>
<simulator name="chassis">
<variable name="position"/>
<variable name="force"/>
<variable name="velocity"/>
</simulator>
<simulator name="wheel">
<variable name="position"/>
<variable name="in_vel"/>
<variable name="out_spring_damper_f"/>
<variable name="velocity"/>
</simulator>
</simulators>
Loading