Skip to content
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

Merged
merged 16 commits into from
Dec 12, 2024

Conversation

davidhjp01
Copy link
Contributor

@davidhjp01 davidhjp01 commented Nov 16, 2024

According to FMI 2.0.4 spec (Section 4.2.4), start values are read during instantiated state via fmi2SetXXX . But libcosim does not run these functions in instantiated state and this makes some of FMUs exported (e.g. Simulink) not initializing values accordingly.

For example see below cosim_demo_app that runs an integrator whose initial condition is set via parameter Parameter.Integrator_x0:

image
image

The initial condition is not properly set via the parameter (i.e. not starting from 10).
On the other hand, fmpy properly initialize this value when set in the simulation setting:
image

To fix this, this PR added an additional function initialize_start_values() that runs before simulator::setup() in fixed_step_algorithm::cosim::impl::initialize()

Check state_init_test.cpp to observe different behaviours with the previous libcosim version

@davidhjp01 davidhjp01 added the bug Something isn't working label Nov 16, 2024
@davidhjp01 davidhjp01 self-assigned this Nov 16, 2024
@davidhjp01 davidhjp01 marked this pull request as draft November 16, 2024 10:28
@davidhjp01 davidhjp01 marked this pull request as ready for review November 16, 2024 12:52
@davidhjp01
Copy link
Contributor Author

I guess this PR contains some conflicts with #777, will need to update based on the merge :)

Copy link
Member

@kyllingstad kyllingstad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation looks good, and it's great to get this fixed. Correctness issues should always have high priority, IMO.

That said, I wonder if this really needs to be an entirely new interface function in simulator. Couldn't the things you do in the new simulator::initialize_start_values() function be done at the start of simulator::setup() instead, before it calls slave_->start_simulation()?

void initialize_start_values()
{
auto deltaT = duration::zero();
auto filter = [this](const variable_type& vt) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variable_type and value_reference are just integers in disguise, and should therefore be passed by value rather than by reference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we have the scalar_value alias which you can use instead of that variant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When adding an FMU, you should update tests/data/fmi2/README.md and possibly include a licence file, cf. #764.

@@ -518,6 +546,34 @@ class slave_simulator::impl
return result;
}

void initialize_start_values()
Copy link
Member

Choose a reason for hiding this comment

The 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 setup().

@@ -119,6 +119,11 @@ class simulator : public manipulable
*/
virtual void start_simulation() = 0;

/**
* Runs fmiXSetINI (4.2.4) functions to initialize variables' start values.
Copy link
Member

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().

Copy link
Member

@restenb restenb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@restenb
Copy link
Member

restenb commented Dec 10, 2024

That said, I wonder if this really needs to be an entirely new interface function in simulator. Couldn't the things you do in the new simulator::initialize_start_values() function be done at the start of simulator::setup() instead, before it calls slave_->start_simulation()?

Agree. I've moved the functionality into simulator::setup() instead now. Briefly updated docstring to point out that this function is for instantiating the FMUs and entering initialization mode. Also followed up your code comments.

@restenb
Copy link
Member

restenb commented Dec 10, 2024

Builds are running into test failures, all of which are something like this:

terminate called after throwing an instance of 'std::out_of_range'
  what():  Variable with value reference 0 and type 

Seems this is originating in FMILibrary.

@kyllingstad
Copy link
Member

terminate called after throwing an instance of 'std::out_of_range'
  what():  Variable with value reference 0 and type 

Seems this is originating in FMILibrary.

I don't think so. It is caused by a C++ exception (std::out_of_range), while FMI Library is a C library (and C doesn't have exceptions at all).

Copy link
Member

@kyllingstad kyllingstad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, provided the cause of the exception is found and fixed.

@davidhjp01
Copy link
Contributor Author

davidhjp01 commented Dec 11, 2024

Hei thanks for the follow up @restenb and @kyllingstad

  • Passing variable_type as value to filter to avoid dangling reference that causes the test fails.
  • StateInitExample.fmu was created using FMIKit (https://github.com/CATIA-Systems/FMIKit-Simulink/blob/main/LICENSE.txt), do we need to update the license to Clause-2 BSD? (i.e. because we are building the fmu using the source code from FMIKit).
  • Removed the pdb file in the tests directory.

@restenb
Copy link
Member

restenb commented Dec 11, 2024

Thanks for the fix. I completely missed that I had introduced this.

I was looking at the setup when your commit came in, wondering if our setup order was somehow incorrect. But by the way I read this, and the table on page 110 of the FMI 2.0.4 specification, this appears to be correct now. Variables are initialized between instantiate_slave and setup_experiment in the flow.

instantiated:
In this state, start and guess values (= variables that have initial = "exact" or "approx" and
variability ≠ "constant") can be set,

For calls to fmi2SetXXX:
for a variable with variability ≠ "constant" that has initial = "exact" or "approx"

I don't recall that we use this initial variable, so that means we are basically always assuming initial=exact?

@restenb
Copy link
Member

restenb commented Dec 11, 2024

Note that since we have users waiting on this fix on our side, I'm aiming to merge this & release libcosim tomorrow.

@davidhjp01
Copy link
Contributor Author

Thanks for the fix. I completely missed that I had introduced this.

I was looking at the setup when your commit came in, wondering if our setup order was somehow incorrect. But by the way I read this, and the table on page 110 of the FMI 2.0.4 specification, this appears to be correct now. Variables are initialized between instantiate_slave and setup_experiment in the flow.

instantiated:
In this state, start and guess values (= variables that have initial = "exact" or "approx" and
variability ≠ "constant") can be set,

For calls to fmi2SetXXX: for a variable with variability ≠ "constant" that has initial = "exact" or "approx"

I don't recall that we use this initial variable, so that means we are basically always assuming initial=exact?

Yeah, I also remember reading this one. I think libcosim currently does not parse initial attribute for ScalarVariable in model description file. So I just skipped checking it for now.

@kyllingstad
Copy link
Member

I don't recall that we use this initial variable, so that means we are basically always assuming initial=exact?

No, it's more that we don't need to worry about the initial attribute for the use cases we support. Per the spec, input variables don't have an initial attribute, and parameters can only have initial=exact. initial=approx is only used for output variables and calculated parameters which enter into algebraic loops that need to be resolved iteratively, which is something we don't handle. initial=calculated needs no special handling AFAICT.

@restenb restenb merged commit c49ebb6 into master Dec 12, 2024
20 checks passed
@restenb restenb deleted the bug/fmi-start-value branch December 12, 2024 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants