open-simulation-platform / cosim-cli

Command-line interface for libcosim
https://open-simulation-platform.github.io/cosim
Mozilla Public License 2.0
8 stars 2 forks source link

run-single initializes FMU before setting initial values #110

Closed eidekrist closed 5 months ago

eidekrist commented 5 months ago

cosim run OspSystemStructure.xml will set user defined initial values before cosim::simulator::setup() is called.

cosim run-single MyFmu.fmu foo=bar will set user defined initial values after cosim::simulator::setup() is called, see run_single.cpp:275.

cosim::simulator::setup() eventually calls the FMI initialize() method which means, in the latter case, that the FMU will be initialized before the desired initial values have been set.

I have a number of FMUs which are vitally dependent on a variable having a certain value before fmi::initialize() is called, the prime example being the path of a resource file not known at FMU generation time. Being able to use cosim run-single for debugging would be great. Until now I've had to create a simple OspSystemStructure.xml and use cosim run for this purpose.

kyllingstad commented 5 months ago

It is easy enough to just move the setup() call to after initial values have been set, and I'll do that, but I suspect that the root cause of the problem is that your FMUs don't quite conform to the FMI specification. Let me explain why I think so.

First, note that the run-single command doesn't go via cosim::simulator. The (perhaps poorly named) variable simulator in run_single.cpp, which your link refers to, is in fact of type std::shared_ptr<cosim::slave>. It points to a cosim::fmi::v1::slave_instance or a cosim::fmi::v2::slave_instance, depending on which FMI version your FMUs use.

Here is how the initialisation functions map to FMI functions for the two versions. For FMI 1,

  1. cosim::fmi::v1::setup() does not call any function in the FMU
  2. cosim::fmi::v1::start_simulation() calls:
    1. fmiInitializeSlave()

In other words, it doesn't really matter when setup() is called, because it doesn't interact with the FMU code in any way. So this makes me think that your FMU uses FMI 2, despite the fact that you mention an initialize() method. Is this correct?

For FMI 2,

  1. cosim::fmi::v2::setup() calls:
    1. fmi2SetupExperiment()
    2. fmi2EnterInitializationMode()
  2. cosim::fmi::v2::start_simulation() calls:
    1. fmi2ExitInitializationMode()

Here, there could in principle be an issue, because certain types of variables cannot be set after fmi2EnterInitializationMode() has been called – specifically, variables for which the initial attribute in the model description is approx. But the kind of variable that you mention sounds very much like something that has causality = parameter, which always implies initial = exact, and so it must be possible to set it after fmi2EnterInitializationMode() has been called. Could it be that your FMUs are not entirely conforming to this part of the spec?

eidekrist commented 5 months ago

Well, this is embarrassing. My FMUs are using FMI version 1, and after reading your comments and taking a nice little dive into the code I've come to the same conclusion as you:

it doesn't really matter when setup() is called

I'm also not able to reproduce the behavior I originally described, so this must be down to a good old user error. Apologies!