diff --git a/docs/src/examples/manipulation.md b/docs/src/examples/manipulation.md
new file mode 100644
index 00000000..adea4344
--- /dev/null
+++ b/docs/src/examples/manipulation.md
@@ -0,0 +1,289 @@
+# Manipulate a function
+Tutorial by Johannes Stoljar, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+This example shows how to overwrite a library function with an own function. For this the FMU model is simulated first without changes. Then the function `fmi2GetReal()` is overwritten and simulated again. Both simulations are displayed in a graph to show the change caused by overwriting the function. The model used is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.
+
+![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true)
+
+
+## Target group
+The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/manipulation.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/manipulation.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/manipulation.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+| 4. | Install FMICore via | add FMICore | add " https://github.com/ThummeTo/FMICore.jl " |
+| 5. | Install Plots via | add Plots | |
+
+## Code section
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+using FMI
+using FMIZoo
+using FMICore
+using Plots
+```
+
+### Simulation setup
+
+Next, the start time and end time of the simulation are set.
+
+
+```julia
+tStart = 0.0
+tStop = 8.0
+```
+
+
+
+
+ 8.0
+
+
+
+### Import FMU
+
+In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown.
+
+
+```julia
+# we use an FMU from the FMIZoo.jl
+pathToFMU = get_model_filename("SpringFrictionPendulum1D", "Dymola", "2022x")
+
+myFMU = fmiLoad(pathToFMU)
+
+fmiInfo(myFMU)
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_HiWCsS/SpringFrictionPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_HiWCsS/SpringFrictionPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193
+
+
+ #################### Begin information for FMU ####################
+ Model name: SpringFrictionPendulum1D
+ FMI-Version: 2.0
+ GUID: {df491d8d-0598-4495-913e-5b025e54d7f2}
+ Generation tool: Dymola Version 2022x (64-bit), 2021-10-08
+ Generation time: 2022-03-03T15:09:18Z
+ Var. naming conv.: structured
+ Event indicators: 24
+ Inputs: 0
+ Outputs: 0
+ States: 2
+ 33554432 ["mass.s"]
+ 33554433 ["mass.v", "mass.v_relfric"]
+ Supports Co-Simulation: true
+ Model identifier: SpringFrictionPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ Var. com. steps: true
+ Input interpol.: true
+ Max order out. der.: 1
+ Supports Model-Exchange: true
+ Model identifier: SpringFrictionPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ ##################### End information for FMU #####################
+
+
+### Simulate FMU
+
+In the next steps the recorded value is defined. The recorded value is the position of the mass. In the function `fmiSimulateME()` the FMU is simulated in model-exchange mode (ME) with an adaptive step size. In addition, the start and end time and the recorded variables are specified.
+
+
+```julia
+vrs = ["mass.s"]
+
+simData = fmiSimulateME(myFMU, tStart, tStop; recordValues=vrs)
+```
+
+ [34mSimulating ME-FMU ... 100%|██████████████████████████████| Time: 0:00:10[39m
+
+
+
+
+
+ Model name:
+ SpringFrictionPendulum1D
+ Success:
+ true
+ States [110]:
+ 0.0 [0.5, 0.0]
+ 2.3529411764719727e-11 [0.5, 1.0e-10]
+ 1.0000023529411766e-5 [0.5000000002125017, 4.250030186348503e-5]
+ 0.00011000002352941177 [0.5000000257134062, 0.0004675245102952289]
+ 0.0011100000235294118 [0.5000026191281834, 0.004719970360497647]
+ 0.011110000023529413 [0.5002631686645611, 0.047449647283752144]
+ 0.03982466736770743 [0.5034050123596227, 0.17168075110950387]
+ 0.09972054285078226 [0.5215006197560228, 0.43204954153497455]
+ 0.16481836271111422 [0.5585747156842935, 0.703758265932321]
+ ...
+ 8.0 [1.0668213438183276, -1.0000099359121942e-10]
+ Values [110]:
+ 0.0 (0.5,)
+ 2.3529411764719727e-11 (0.5,)
+ 1.0000023529411766e-5 (0.5000000002125017,)
+ 0.00011000002352941177 (0.5000000257134062,)
+ 0.0011100000235294118 (0.5000026191281834,)
+ 0.011110000023529413 (0.5002631686645611,)
+ 0.03982466736770743 (0.5034050123596227,)
+ 0.09972054285078226 (0.5215006197560228,)
+ 0.16481836271111422 (0.5585747156842935,)
+ ...
+ 8.0 (1.0668213438183276,)
+ Events [6]:
+ State-Event #11 @ 0.0s
+ State-Event #11 @ 0.9939s
+ State-Event #19 @ 1.9881s
+ State-Event #11 @ 2.9829s
+ State-Event #19 @ 3.9787s
+ State-Event #11 @ 4.9768s
+
+
+
+
+### Plotting FMU
+
+After the simulation is finished, the result of the FMU for the model-exchange mode can be plotted. In the plot for the FMU it can be seen that the oscillation continues to decrease due to the effect of the friction. If you simulate long enough, the oscillation comes to a standstill in a certain time.
+
+
+```julia
+fig = fmiPlot(simData, states=false)
+```
+
+
+
+
+
+![svg](manipulation_files/manipulation_10_0.svg)
+
+
+
+
+### Override Function
+
+After overwriting a function, the previous one is no longer accessible. The original function `fmi2GetReal()` is cached by storing the address of the pointer. The addresses of the pointers are kept in the FMU and are thus accessible.
+
+
+```julia
+# save, where the original `fmi2GetReal` function was stored, so we can access it in our new function
+originalGetReal = myFMU.cGetReal
+```
+
+
+
+
+ Ptr{Nothing} @0x00007f52e9530faf
+
+
+
+To overwrite the function `fmi2GetReal!()`, the function header of the new custom function must be identical to the previous one. The function header looks like `fmi2GetReal!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{Array{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{Array{fmi2Real}, Ptr{fmi2Real}})::fmi2Status`. The information how the FMI2 function are structured can be seen from [FMICore.jl](https://github.com/ThummeTo/FMICore.jl/blob/main/src/FMI2_c.jl#L718) or the FMI2.0.3-specification.
+
+In the new implementation the original function is called by the previously stored pointer. Next there is a special handling if `value` is a pointer to an array. In this case the pointer is treated as an array, so that the entries are accessible. Otherwise, each value in `value` is multiplied by two. Finally, the original state of the original function is output.
+
+
+```julia
+function myGetReal!(c::fmi2Component, vr::Union{Array{fmi2ValueReference}, Ptr{fmi2ValueReference}},
+ nvr::Csize_t, value::Union{Array{fmi2Real}, Ptr{fmi2Real}})
+ # first, we do what the original function does
+ status = fmi2GetReal!(originalGetReal, c, vr, nvr, value)
+
+ # if we have a pointer to an array, we must interprete it as array to access elements
+ if isa(value, Ptr{fmi2Real})
+ value = unsafe_wrap(Array{fmi2Real}, value, nvr, own=false)
+ end
+
+ # now, we multiply every value by two (just for fun!)
+ for i in 1:nvr
+ value[i] *= 2.0
+ end
+
+ # return the original status
+ return status
+end
+```
+
+
+
+
+ myGetReal! (generic function with 1 method)
+
+
+
+In the next command the original function is overwritten with the new defined function, for which the command `fmiSetFctGetReal()` is called.
+
+
+```julia
+# no we overwrite the original function
+fmiSetFctGetReal(myFMU, myGetReal!)
+```
+
+
+
+
+ Ptr{Nothing} @0x00007f533e325fc0
+
+
+
+### Simulate and Plot FMU with modified function
+
+As before, the identical command is called here for simulation. This is also a model exchange simulation. Immediately afterwards, the results are added to the previous graph as a dashed line.
+
+
+```julia
+simData = fmiSimulateME(myFMU, tStart, tStop; recordValues=vrs)
+fmiPlot!(fig, simData; states=false, style=:dash)
+```
+
+
+
+
+
+![svg](manipulation_files/manipulation_18_0.svg)
+
+
+
+
+As expected by overwriting the function, all values are doubled.
+
+### Unload FMU
+
+After plotting the data, the FMU is unloaded and all unpacked data on disc is removed.
+
+
+```julia
+fmiUnload(myFMU)
+```
+
+### Summary
+
+In this tutorial it is shown how an existing function of the library can be replaced by an own implementation. Through this possibility, there are almost no limits for the user, whereby the user can customize the function to his liking.
diff --git a/docs/src/examples/manipulation_files/manipulation_10_0.svg b/docs/src/examples/manipulation_files/manipulation_10_0.svg
new file mode 100644
index 00000000..b86d3be2
--- /dev/null
+++ b/docs/src/examples/manipulation_files/manipulation_10_0.svg
@@ -0,0 +1,134 @@
+
+
diff --git a/docs/src/examples/manipulation_files/manipulation_18_0.svg b/docs/src/examples/manipulation_files/manipulation_18_0.svg
new file mode 100644
index 00000000..d9cf606b
--- /dev/null
+++ b/docs/src/examples/manipulation_files/manipulation_18_0.svg
@@ -0,0 +1,172 @@
+
+
diff --git a/docs/src/examples/modelica_conference_2021.md b/docs/src/examples/modelica_conference_2021.md
new file mode 100644
index 00000000..c77e978a
--- /dev/null
+++ b/docs/src/examples/modelica_conference_2021.md
@@ -0,0 +1,219 @@
+# Advanced Simulation of an FMU
+Tutorial by Johannes Stoljar, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+In this example we would like to show that besides the simple simulation of an FMU there is also a more advanced version of the simulation. The advantage of the more advanced variant is that there are more possibilities to intervene in the simulation to make changes. After the FMU has been simulated, the simulation results are displayed in a graph. The used model is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.
+
+![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true)
+
+
+## Target group
+The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/modelica_conference_2021.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/modelica_conference_2021.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/modelica_conference_2021.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+| 4. | Install Plots via | add Plots | |
+
+## Code section
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+using FMI
+using FMIZoo
+using Plots
+```
+
+### Simulation setup
+
+Next, the start time and end time of the simulation are set. Finally, a step size is specified to store the results of the simulation at these time steps.
+
+
+```julia
+tStart = 0.0
+tStep = 0.1
+tStop = 8.0
+tSave = tStart:tStep:tStop
+```
+
+
+
+
+ 0.0:0.1:8.0
+
+
+
+### Simple FMU Simulation
+
+In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown.
+
+
+```julia
+# we use an FMU from the FMIZoo.jl
+pathToFMU = get_model_filename("SpringFrictionPendulum1D", "Dymola", "2022x")
+
+myFMU = fmiLoad(pathToFMU)
+fmiInfo(myFMU)
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_ksTsah/SpringFrictionPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_ksTsah/SpringFrictionPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193
+
+
+ #################### Begin information for FMU ####################
+ Model name: SpringFrictionPendulum1D
+ FMI-Version: 2.0
+ GUID: {df491d8d-0598-4495-913e-5b025e54d7f2}
+ Generation tool: Dymola Version 2022x (64-bit), 2021-10-08
+ Generation time: 2022-03-03T15:09:18Z
+ Var. naming conv.: structured
+ Event indicators: 24
+ Inputs: 0
+ Outputs: 0
+ States: 2
+ 33554432 ["mass.s"]
+ 33554433 ["mass.v", "mass.v_relfric"]
+ Supports Co-Simulation: true
+ Model identifier: SpringFrictionPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ Var. com. steps: true
+ Input interpol.: true
+ Max order out. der.: 1
+ Supports Model-Exchange: true
+ Model identifier: SpringFrictionPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ ##################### End information for FMU #####################
+
+
+In the next commands the FMU is simulated, for which the start and end time and recorded variables are declared. Afterwards the simulation result is shown in a graph. In the plot for the FMU, it can be seen that the oscillation keeps decreasing due to the effect of friction. If one simulates long enough, the oscillation comes to a standstill after a certain time.
+
+
+```julia
+simData = fmiSimulate(myFMU, tStart, tStop; recordValues=["mass.s"], saveat=tSave)
+fmiPlot(simData)
+```
+
+
+
+
+
+![svg](modelica_conference_2021_files/modelica_conference_2021_8_0.svg)
+
+
+
+
+After plotting the data, the FMU is unloaded and all unpacked data on disc is removed.
+
+
+```julia
+fmiUnload(myFMU)
+```
+
+### Advanced FMU Simulation
+
+In the following type of simulation a more advanced variant is presented, which allows intervening more in the simulation process. Analogous to the simple variant, an FMU model must be loaded.
+
+
+```julia
+myFMU = fmiLoad(pathToFMU);
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_NQAhCe/SpringFrictionPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_NQAhCe/SpringFrictionPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193
+
+
+Next, it is necessary to create an instance of the FMU, this is achieved by the command `fmiInstantiate!()`.
+
+
+```julia
+instanceFMU = fmiInstantiate!(myFMU)
+```
+
+
+
+
+ FMU: SpringFrictionPendulum1D
+ InstanceName: [not defined]
+ Address: Ptr{Nothing} @0x00000000064853f0
+ State: fmi2ComponentStateInstantiated
+ Logging: false
+ FMU time: -Inf
+ FMU states: nothing
+
+
+
+In the following code block, start and end time for the simulation is set by the `fmiSetupExperiment()` command. Next, the FMU is initialized by the calls of `fmiEnterInitializationMode()` and `fmiExitInitializationMode()`. It would also be possible to set initial states for the FMU between these two commands.
+
+
+```julia
+fmiSetupExperiment(instanceFMU, tStart, tStop)
+fmiEnterInitializationMode(instanceFMU)
+# set initial model states
+fmiExitInitializationMode(instanceFMU)
+```
+
+
+
+
+ 0x00000000
+
+
+
+The actual simulation loop is shown in the following block. Here a simulation step `fmiDoStep()` with the fixed step size `tStep` is executed. As indicated in the code by the comments, the input values and output values of the FMU could be changed in the simulation loop as desired, whereby the higher possibility of adjustments arises.
+
+
+```julia
+for t in tSave
+ # set model inputs
+ # ...
+ fmiDoStep(instanceFMU, tStep)
+ # get model outputs
+ # ...
+end
+```
+
+The instantiated FMU must be terminated and then the memory area for the instance can also be deallocated. The last step is to unload the FMU to remove all unpacked data on disc.
+
+
+```julia
+fmiTerminate(instanceFMU)
+fmiFreeInstance!(instanceFMU)
+fmiUnload(myFMU)
+```
+
+### Summary
+
+The tutorial has shown that besides the usual simple variant of simulating an FMU, there is another way to make more adjustments.
diff --git a/docs/src/examples/modelica_conference_2021_files/modelica_conference_2021_8_0.svg b/docs/src/examples/modelica_conference_2021_files/modelica_conference_2021_8_0.svg
new file mode 100644
index 00000000..c714d93e
--- /dev/null
+++ b/docs/src/examples/modelica_conference_2021_files/modelica_conference_2021_8_0.svg
@@ -0,0 +1,110 @@
+
+
diff --git a/docs/src/examples/multiple_instances.md b/docs/src/examples/multiple_instances.md
new file mode 100644
index 00000000..d20d0c7f
--- /dev/null
+++ b/docs/src/examples/multiple_instances.md
@@ -0,0 +1,226 @@
+# Multiple Instances of an FMU
+Tutorial by Johannes Stoljar, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+In this example we want to show that it is possible to create different instances of an FMU. The different instances can then be used to run independent simulations. After the FMU has been simulated, the simulation results are displayed in a graph. The used model is a one-dimensional spring pendulum without friction. The object-orientated structure of the *SpringPendulum1D* can be seen in the following graphic.
+
+![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringPendulum1D.svg?raw=true)
+
+
+## Target group
+The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/multiple_instances.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/multiple_instances.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/multiple_instances.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+| 4. | Install Plots via | add Plots | |
+
+## Code section
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+using FMI
+using FMIZoo
+using Plots
+```
+
+### Simulation setup
+
+Next, the start time and end time of the simulation are set. Finally, the recorded values are specified to store the results of the simulation.
+
+
+```julia
+tStart = 0.0
+tStop = 8.0
+
+vrs = ["mass.s"]
+```
+
+
+
+
+ 1-element Vector{String}:
+ "mass.s"
+
+
+
+### Import FMU
+
+In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown.
+
+
+```julia
+# we use an FMU from the FMIZoo.jl
+pathToFMU = get_model_filename("SpringPendulum1D", "Dymola", "2022x")
+
+myFMU = fmiLoad(pathToFMU)
+fmiInfo(myFMU)
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_RGs05M/SpringPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_RGs05M/SpringPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193
+
+
+ #################### Begin information for FMU ####################
+ Model name: SpringPendulum1D
+ FMI-Version: 2.0
+ GUID: {a3e886fa-675c-4308-8e91-3490e598ba11}
+ Generation tool: Dymola Version 2022x (64-bit), 2021-10-08
+ Generation time: 2022-03-03T15:08:57Z
+ Var. naming conv.: structured
+ Event indicators: 0
+ Inputs: 0
+ Outputs: 0
+ States: 2
+ 33554432 ["mass.s"]
+ 33554433 ["mass.v"]
+ Supports Co-Simulation: true
+ Model identifier: SpringPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ Var. com. steps: true
+ Input interpol.: true
+ Max order out. der.: 1
+ Supports Model-Exchange: true
+ Model identifier: SpringPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ ##################### End information for FMU #####################
+
+
+### First Instance
+
+To create an instance of the FMU it is necessary to call the command `fmiInstantiate!()`. With the component address you now have a unique instance of the FMU.
+
+
+```julia
+comp1 = fmiInstantiate!(myFMU; loggingOn=true)
+comp1Address= comp1.compAddr
+println(comp1)
+```
+
+ FMU: SpringPendulum1D
+ InstanceName: [not defined]
+ Address: Ptr{Nothing} @0x0000000003954580
+ State: fmi2ComponentStateInstantiated
+ Logging: false
+ FMU time: -Inf
+ FMU states: nothing
+
+
+Next, a dictionary for the parameters is created. With this dictionary you can set the initial states of the variables of the FMU. For the spring constant `spring.c` a value of $10.0 \frac{N}{m}$ and for the position of the mass `mass.s` a value of $1.0 m$ is set. The created dictionary with the specified variables for recording are passed to the command for simulation.
+
+
+```julia
+param1 = Dict("spring.c"=>10.0, "mass_s0"=>1.0)
+data1 = fmiSimulate(comp1, tStart, tStop; parameters=param1, recordValues=vrs, instantiate=false)
+fig = fmiPlot(data1)
+```
+
+
+
+
+
+![svg](multiple_instances_files/multiple_instances_10_0.svg)
+
+
+
+
+For control, you can compare again the address of the instance to the previous address, and it should be the same address. As soon as this is not the case an error would be thrown by the macro `@assert`.
+
+
+```julia
+@assert comp1.compAddr === comp1Address
+```
+
+### Second Instance
+
+To create a second instance of the FMU it is necessary to call the command `fmiInstantiate!()`. With the component address you now have a unique instance of the FMU.
+
+
+```julia
+comp2 = fmiInstantiate!(myFMU; loggingOn=true)
+comp2Address= comp2.compAddr
+println(comp2)
+```
+
+ FMU: SpringPendulum1D
+ InstanceName: [not defined]
+ Address: Ptr{Nothing} @0x000000000529d440
+ State: fmi2ComponentStateInstantiated
+ Logging: false
+ FMU time: -Inf
+ FMU states: nothing
+
+
+The addresses of the instantiated FMUs must differ, and you can see that in the comparison below.
+
+
+```julia
+@assert comp1Address !== comp2Address
+```
+
+Again, a dictionary for the parameters is created. With this dictionary you can set the initial states of the variables of the FMU. For the spring constant `spring.c` a value of $1.0 \frac{N}{m}$ and for the position of the mass `mass.s` a value of $2.0 m$ is set. The created dictionary with the specified variables for recording are passed to the command for simulation.
+
+
+```julia
+param2 = Dict("spring.c"=>1.0, "mass.s"=>2.0)
+data2 = fmiSimulateCS(comp2, tStart, tStop; parameters=param2, recordValues=vrs, instantiate=false)
+fmiPlot!(fig, data2)
+```
+
+
+
+
+
+![svg](multiple_instances_files/multiple_instances_18_0.svg)
+
+
+
+
+For control, you can compare again the address of the instance `comp2` to the previous address `comp2Address` and it should be the same address.
+
+
+```julia
+@assert comp2.compAddr === comp2Address
+```
+
+### Unload FMU
+
+After plotting the data, the FMU is unloaded and all unpacked data on disc is removed.
+
+
+```julia
+fmiUnload(myFMU)
+```
+
+### Summary
+
+Based on the example it can be seen that it is possible to create different instances of an FMU. The different instances can then be used to perform different simulations.
diff --git a/docs/src/examples/multiple_instances_files/multiple_instances_10_0.svg b/docs/src/examples/multiple_instances_files/multiple_instances_10_0.svg
new file mode 100644
index 00000000..cf0fca99
--- /dev/null
+++ b/docs/src/examples/multiple_instances_files/multiple_instances_10_0.svg
@@ -0,0 +1,902 @@
+
+
diff --git a/docs/src/examples/multiple_instances_files/multiple_instances_18_0.svg b/docs/src/examples/multiple_instances_files/multiple_instances_18_0.svg
new file mode 100644
index 00000000..8004ffcf
--- /dev/null
+++ b/docs/src/examples/multiple_instances_files/multiple_instances_18_0.svg
@@ -0,0 +1,1708 @@
+
+
diff --git a/docs/src/examples/multiprocessing.md b/docs/src/examples/multiprocessing.md
new file mode 100644
index 00000000..365cdee4
--- /dev/null
+++ b/docs/src/examples/multiprocessing.md
@@ -0,0 +1,279 @@
+# Multiprocessing
+Tutorial by Jonas Wilfert, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar, Jonas Wilfert
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+This example shows how to parallelize the computation of an FMU in FMI.jl. We can compute a batch of FMU-evaluations in parallel with different initial settings.
+Parallelization can be achieved using multithreading or using multiprocessing. This example shows **multiprocessing**, check `multithreading.ipynb` for multithreading.
+Advantage of multithreading is a lower communication overhead as well as lower RAM usage.
+However in some cases multiprocessing can be faster as the garbage collector is not shared.
+
+
+The model used is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.
+
+![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true)
+
+
+## Target group
+The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/distributed.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/distributed.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/distributed.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+| 4. | Install FMICore via | add FMICore | add " https://github.com/ThummeTo/FMICore.jl " |
+| 5. | Install BenchmarkTools via | add BenchmarkTools | |
+
+## Code section
+
+
+
+Adding your desired amount of processes:
+
+
+```julia
+using Distributed
+n_procs = 4
+addprocs(n_procs; exeflags=`--project=$(Base.active_project()) --threads=auto`, restrict=false)
+```
+
+
+
+
+ 4-element Vector{Int64}:
+ 2
+ 3
+ 4
+ 5
+
+
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+@everywhere using FMI
+@everywhere using FMIZoo
+@everywhere using BenchmarkTools
+```
+
+Checking that we workers have been correctly initialized:
+
+
+```julia
+workers()
+
+@everywhere println("Hello World!")
+
+# The following lines can be uncommented for more advanced informations about the subprocesses
+# @everywhere println(pwd())
+# @everywhere println(Base.active_project())
+# @everywhere println(gethostname())
+# @everywhere println(VERSION)
+# @everywhere println(Threads.nthreads())
+```
+
+ Hello World!
+ From worker 2: Hello World!
+ From worker 4: Hello World!
+ From worker 3: Hello World!
+ From worker 5: Hello World!
+
+
+### Simulation setup
+
+Next, the batch size and input values are defined.
+
+
+```julia
+
+# Best if batchSize is a multiple of the threads/cores
+batchSize = 16
+
+# Define an array of arrays randomly
+input_values = collect(collect.(eachrow(rand(batchSize,2))))
+```
+
+
+
+
+ 16-element Vector{Vector{Float64}}:
+ [0.053272555584304104, 0.45774751559489646]
+ [0.5896778258750763, 0.12435199368996086]
+ [0.5651662129022856, 0.8764873667695812]
+ [0.6176612662918868, 0.2785804850450695]
+ [0.9274233385552415, 0.38275426899967724]
+ [0.46094177953642124, 0.6554519159504497]
+ [0.73421590884579, 0.28177572874256906]
+ [0.5575198504784948, 0.3212214708846175]
+ [0.271126562512229, 0.6499895887749823]
+ [0.5289045741655536, 0.4873381265273107]
+ [0.8565770844036877, 0.2312687408548988]
+ [0.06332369482582334, 0.44807066874864065]
+ [0.08611017776660712, 0.960898209225902]
+ [0.3191189843443898, 0.30014344015893846]
+ [0.028884098958377402, 0.5285860311385517]
+ [0.7282727650515579, 0.7584435212215901]
+
+
+
+### Shared Module
+For Distributed we need to embed the FMU into its own `module`. This prevents Distributed from trying to serialize and send the FMU over the network, as this can cause issues. This module needs to be made available on all processes using `@everywhere`.
+
+
+```julia
+@everywhere module SharedModule
+ using FMIZoo
+ using FMI
+
+ t_start = 0.0
+ t_step = 0.1
+ t_stop = 10.0
+ tspan = (t_start, t_stop)
+ tData = collect(t_start:t_step:t_stop)
+
+ model_fmu = FMIZoo.fmiLoad("SpringPendulum1D", "Dymola", "2022x")
+end
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_R8823R/SpringPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_R8823R/SpringPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:193
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_xI4R81/SpringPendulum1D`.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_qUcQP8/SpringPendulum1D`.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_S33uq0/SpringPendulum1D`.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_M7VjAQ/SpringPendulum1D`.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_xI4R81/SpringPendulum1D/resources`
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_qUcQP8/SpringPendulum1D/resources`
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_S33uq0/SpringPendulum1D/resources`
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_M7VjAQ/SpringPendulum1D/resources`
+ [36m[1m[ [22m[39m[36m[1mInfo: [22m[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+
+
+We define a helper function to calculate the FMU and combine it into an Matrix.
+
+
+```julia
+@everywhere function runCalcFormatted(fmu, x0, recordValues=["mass.s", "mass.v"])
+ data = fmiSimulateME(fmu, SharedModule.t_start, SharedModule.t_stop; recordValues=recordValues, saveat=SharedModule.tData, x0=x0, showProgress=false, dtmax=1e-4)
+ return reduce(hcat, data.states.u)
+end
+```
+
+Running a single evaluation is pretty quick, therefore the speed can be better tested with BenchmarkTools.
+
+
+```julia
+@benchmark data = runCalcFormatted(SharedModule.model_fmu, rand(2))
+```
+
+
+
+
+ BenchmarkTools.Trial: 17 samples with 1 evaluation.
+ Range [90m([39m[36m[1mmin[22m[39m … [35mmax[39m[90m): [39m[36m[1m304.291 ms[22m[39m … [35m325.332 ms[39m [90m┊[39m GC [90m([39mmin … max[90m): [39m7.41% … 6.89%
+ Time [90m([39m[34m[1mmedian[22m[39m[90m): [39m[34m[1m307.274 ms [22m[39m[90m┊[39m GC [90m([39mmedian[90m): [39m7.37%
+ Time [90m([39m[32m[1mmean[22m[39m ± [32mσ[39m[90m): [39m[32m[1m309.397 ms[22m[39m ± [32m 5.860 ms[39m [90m┊[39m GC [90m([39mmean ± σ[90m): [39m7.94% ± 1.32%
+
+ [39m█[39m▁[39m▁[39m [39m▁[39m█[39m [39m▁[34m▁[39m[39m [39m [39m█[39m▁[39m▁[39m [32m [39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m▁[39m [39m▁[39m [39m▁[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m▁[39m [39m
+ [39m█[39m█[39m█[39m▁[39m█[39m█[39m▁[39m█[34m█[39m[39m▁[39m▁[39m█[39m█[39m█[39m▁[32m▁[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m█[39m▁[39m█[39m▁[39m█[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m█[39m [39m▁
+ 304 ms[90m Histogram: frequency by time[39m 325 ms [0m[1m<[22m
+
+ Memory estimate[90m: [39m[33m146.80 MiB[39m, allocs estimate[90m: [39m[33m3002433[39m.
+
+
+
+### Single Threaded Batch Execution
+To compute a batch we can collect multiple evaluations. In a single threaded context we can use the same FMU for every call.
+
+
+```julia
+println("Single Threaded")
+@benchmark collect(runCalcFormatted(SharedModule.model_fmu, i) for i in input_values)
+```
+
+ Single Threaded
+
+
+
+
+
+ BenchmarkTools.Trial: 2 samples with 1 evaluation.
+ Range [90m([39m[36m[1mmin[22m[39m … [35mmax[39m[90m): [39m[36m[1m4.966 s[22m[39m … [35m 4.967 s[39m [90m┊[39m GC [90m([39mmin … max[90m): [39m8.58% … 8.12%
+ Time [90m([39m[34m[1mmedian[22m[39m[90m): [39m[34m[1m4.966 s [22m[39m[90m┊[39m GC [90m([39mmedian[90m): [39m8.35%
+ Time [90m([39m[32m[1mmean[22m[39m ± [32mσ[39m[90m): [39m[32m[1m4.966 s[22m[39m ± [32m1.039 ms[39m [90m┊[39m GC [90m([39mmean ± σ[90m): [39m8.35% ± 0.33%
+
+ [34m█[39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [32m [39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m█[39m [39m
+ [34m█[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[32m▁[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m█[39m [39m▁
+ 4.97 s[90m Histogram: frequency by time[39m 4.97 s [0m[1m<[22m
+
+ Memory estimate[90m: [39m[33m2.29 GiB[39m, allocs estimate[90m: [39m[33m48038916[39m.
+
+
+
+### Multithreaded Batch Execution
+In a multithreaded context we have to provide each thread it's own fmu, as they are not thread safe.
+To spread the execution of a function to multiple processes, the function `pmap` can be used.
+
+
+```julia
+println("Multi Threaded")
+@benchmark pmap(i -> runCalcFormatted(SharedModule.model_fmu, i), input_values)
+```
+
+ Multi Threaded
+
+
+
+
+
+ BenchmarkTools.Trial: 2 samples with 1 evaluation.
+ Range [90m([39m[36m[1mmin[22m[39m … [35mmax[39m[90m): [39m[36m[1m3.069 s[22m[39m … [35m 3.078 s[39m [90m┊[39m GC [90m([39mmin … max[90m): [39m0.00% … 0.00%
+ Time [90m([39m[34m[1mmedian[22m[39m[90m): [39m[34m[1m3.074 s [22m[39m[90m┊[39m GC [90m([39mmedian[90m): [39m0.00%
+ Time [90m([39m[32m[1mmean[22m[39m ± [32mσ[39m[90m): [39m[32m[1m3.074 s[22m[39m ± [32m6.467 ms[39m [90m┊[39m GC [90m([39mmean ± σ[90m): [39m0.00% ± 0.00%
+
+ [34m█[39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [32m [39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m█[39m [39m
+ [34m█[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[32m▁[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m█[39m [39m▁
+ 3.07 s[90m Histogram: frequency by time[39m 3.08 s [0m[1m<[22m
+
+ Memory estimate[90m: [39m[33m81.31 KiB[39m, allocs estimate[90m: [39m[33m1192[39m.
+
+
+
+As you can see, there is a significant speed-up in the median execution time. But: The speed-up is often much smaller than `n_procs` (or the number of physical cores of your CPU), this has different reasons. For a rule of thumb, the speed-up should be around `n/2` on a `n`-core-processor with `n` Julia processes.
+
+### Unload FMU
+
+After calculating the data, the FMU is unloaded and all unpacked data on disc is removed.
+
+
+```julia
+@everywhere fmiUnload(SharedModule.model_fmu)
+```
+
+### Summary
+
+In this tutorial it is shown how multi processing with `Distributed.jl` can be used to improve the performance for calculating a Batch of FMUs.
diff --git a/docs/src/examples/multithreading.md b/docs/src/examples/multithreading.md
new file mode 100644
index 00000000..3f1d0979
--- /dev/null
+++ b/docs/src/examples/multithreading.md
@@ -0,0 +1,254 @@
+# Multithreading
+Tutorial by Jonas Wilfert, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar, Jonas Wilfert
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+This example shows how to parallelize the computation of an FMU in FMI.jl. We can compute a batch of FMU-evaluations in parallel with different initial settings.
+Parallelization can be achieved using multithreading or using multiprocessing. This example shows **multithreading**, check `multiprocessing.ipynb` for multiprocessing.
+Advantage of multithreading is a lower communication overhead as well as lower RAM usage.
+However in some cases multiprocessing can be faster as the garbage collector is not shared.
+
+
+The model used is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.
+
+![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true)
+
+
+## Target group
+The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/parallel.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/parallel.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/parallel.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+| 4. | Install FMICore via | add FMICore | add " https://github.com/ThummeTo/FMICore.jl " |
+| 5. | Install Folds via | add Folds | |
+| 6. | Install BenchmarkTools via | add BenchmarkTools | |
+
+## Code section
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+using FMI
+using FMIZoo
+using Folds
+using BenchmarkTools
+```
+
+First, check the amount of available threads:
+
+
+```julia
+Threads.nthreads()
+```
+
+
+
+
+ 1
+
+
+
+If the number of available threads doesn't match your expections, you can increase the number of threads available to the Julia process like described [here](https://docs.julialang.org/en/v1/manual/multi-threading/#Starting-Julia-with-multiple-threads).
+
+### Simulation setup
+
+Next, the start time and end time of the simulation are set. Here we also decide the size of the batch.
+
+
+```julia
+t_start = 0.0
+t_step = 0.1
+t_stop = 10.0
+tspan = (t_start, t_stop)
+tData = collect(t_start:t_step:t_stop)
+
+# Best if batchSize is a multiple of the threads/cores
+batchSize = Threads.nthreads()
+
+# Define an array of arrays randomly
+input_values = collect(collect.(eachrow(rand(batchSize,2))))
+
+```
+
+
+
+
+ 1-element Vector{Vector{Float64}}:
+ [0.769193342919982, 0.5516183754569333]
+
+
+
+We need to instantiate one FMU for each parallel execution, as they cannot be easily shared among different threads.
+
+
+```julia
+# a single FMU to compare the performance
+realFMU = fmiLoad("SpringPendulum1D", "Dymola", "2022x")
+
+# the FMU batch
+realFMUBatch = [fmiLoad("SpringPendulum1D", "Dymola", "2022x") for _ in 1:batchSize]
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_guexAq/SpringPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_guexAq/SpringPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:193
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_83Dthm/SpringPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_83Dthm/SpringPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:193
+
+
+
+
+
+ 1-element Vector{FMU2}:
+ Model name: SpringPendulum1D
+ Type: 1
+
+
+
+We define a helper function to calculate the FMU solution and combine it into an Matrix.
+
+
+```julia
+function runCalcFormatted(fmu::FMU2, x0::Vector{Float64}, recordValues::Vector{String}=["mass.s", "mass.v"])
+ data = fmiSimulateME(fmu, t_start, t_stop; recordValues=recordValues, saveat=tData, x0=x0, showProgress=false, dtmax=1e-4)
+ return reduce(hcat, data.states.u)
+end
+```
+
+
+
+
+ runCalcFormatted (generic function with 2 methods)
+
+
+
+Running a single evaluation is pretty quick, therefore the speed can be better tested with BenchmarkTools.
+
+
+```julia
+@benchmark data = runCalcFormatted(realFMU, rand(2))
+```
+
+
+
+
+ BenchmarkTools.Trial: 15 samples with 1 evaluation.
+ Range [90m([39m[36m[1mmin[22m[39m … [35mmax[39m[90m): [39m[36m[1m337.943 ms[22m[39m … [35m357.340 ms[39m [90m┊[39m GC [90m([39mmin … max[90m): [39m7.03% … 6.55%
+ Time [90m([39m[34m[1mmedian[22m[39m[90m): [39m[34m[1m339.287 ms [22m[39m[90m┊[39m GC [90m([39mmedian[90m): [39m7.00%
+ Time [90m([39m[32m[1mmean[22m[39m ± [32mσ[39m[90m): [39m[32m[1m342.299 ms[22m[39m ± [32m 5.982 ms[39m [90m┊[39m GC [90m([39mmean ± σ[90m): [39m7.39% ± 1.12%
+
+ [39m▃[39m█[39m [34m [39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [32m [39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m
+ [39m█[39m█[39m▇[34m▇[39m[39m▇[39m▇[39m▁[39m▁[39m▇[39m▁[39m▇[39m▁[39m▁[39m▁[32m▁[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▇[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▇[39m▇[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▇[39m [39m▁
+ 338 ms[90m Histogram: frequency by time[39m 357 ms [0m[1m<[22m
+
+ Memory estimate[90m: [39m[33m146.80 MiB[39m, allocs estimate[90m: [39m[33m3002433[39m.
+
+
+
+### Single Threaded Batch Execution
+To compute a batch we can collect multiple evaluations. In a single threaded context we can use the same FMU for every call.
+
+
+```julia
+println("Single Threaded")
+@benchmark collect(runCalcFormatted(realFMU, i) for i in input_values)
+```
+
+ Single Threaded
+
+
+
+
+
+ BenchmarkTools.Trial: 15 samples with 1 evaluation.
+ Range [90m([39m[36m[1mmin[22m[39m … [35mmax[39m[90m): [39m[36m[1m338.941 ms[22m[39m … [35m359.792 ms[39m [90m┊[39m GC [90m([39mmin … max[90m): [39m7.42% … 7.05%
+ Time [90m([39m[34m[1mmedian[22m[39m[90m): [39m[34m[1m340.880 ms [22m[39m[90m┊[39m GC [90m([39mmedian[90m): [39m7.45%
+ Time [90m([39m[32m[1mmean[22m[39m ± [32mσ[39m[90m): [39m[32m[1m343.682 ms[22m[39m ± [32m 6.291 ms[39m [90m┊[39m GC [90m([39mmean ± σ[90m): [39m7.89% ± 1.20%
+
+ [39m█[39m▁[39m▁[39m▁[39m▁[34m█[39m[39m [39m [39m▁[39m▁[39m▁[39m [39m [39m [32m [39m[39m▁[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m▁[39m▁[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m▁[39m [39m
+ [39m█[39m█[39m█[39m█[39m█[34m█[39m[39m▁[39m▁[39m█[39m█[39m█[39m▁[39m▁[39m▁[32m▁[39m[39m█[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m█[39m█[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m█[39m [39m▁
+ 339 ms[90m Histogram: frequency by time[39m 360 ms [0m[1m<[22m
+
+ Memory estimate[90m: [39m[33m146.80 MiB[39m, allocs estimate[90m: [39m[33m3002436[39m.
+
+
+
+### Multithreaded Batch Execution
+In a multithreaded context we have to provide each thread it's own fmu, as they are not thread safe.
+To spread the execution of a function to multiple threads, the library `Folds` can be used.
+
+
+```julia
+println("Multi Threaded")
+@benchmark Folds.collect(runCalcFormatted(fmu, i) for (fmu, i) in zip(realFMUBatch, input_values))
+```
+
+ Multi Threaded
+
+
+
+
+
+ BenchmarkTools.Trial: 17 samples with 1 evaluation.
+ Range [90m([39m[36m[1mmin[22m[39m … [35mmax[39m[90m): [39m[36m[1m307.665 ms[22m[39m … [35m325.644 ms[39m [90m┊[39m GC [90m([39mmin … max[90m): [39m8.44% … 8.13%
+ Time [90m([39m[34m[1mmedian[22m[39m[90m): [39m[34m[1m308.448 ms [22m[39m[90m┊[39m GC [90m([39mmedian[90m): [39m8.55%
+ Time [90m([39m[32m[1mmean[22m[39m ± [32mσ[39m[90m): [39m[32m[1m310.319 ms[22m[39m ± [32m 4.970 ms[39m [90m┊[39m GC [90m([39mmean ± σ[90m): [39m8.72% ± 0.86%
+
+ [39m█[39m [34m█[39m[39m [39m▁[39m [39m [39m [39m▁[32m [39m[39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m [39m
+ [39m█[39m▆[34m█[39m[39m▆[39m█[39m▁[39m▆[39m▁[39m█[32m▁[39m[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▆[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▁[39m▆[39m [39m▁
+ 308 ms[90m Histogram: frequency by time[39m 326 ms [0m[1m<[22m
+
+ Memory estimate[90m: [39m[33m146.80 MiB[39m, allocs estimate[90m: [39m[33m3002440[39m.
+
+
+
+As you can see, there is a significant speed-up in the median execution time. But: The speed-up is often much smaller than `Threads.nthreads()`, this has different reasons. For a rule of thumb, the speed-up should be around `n/2` on a `n`-core-processor with `n` threads for the Julia process.
+
+### Unload FMU
+
+After calculating the data, the FMU is unloaded and all unpacked data on disc is removed.
+
+
+```julia
+fmiUnload(realFMU)
+fmiUnload.(realFMUBatch)
+```
+
+
+
+
+ 1-element Vector{Nothing}:
+ nothing
+
+
+
+### Summary
+
+In this tutorial it is shown how multi threading with `Folds.jl` can be used to improve the performance for calculating a Batch of FMUs.
diff --git a/docs/src/examples/parameterize.md b/docs/src/examples/parameterize.md
new file mode 100644
index 00000000..67856f28
--- /dev/null
+++ b/docs/src/examples/parameterize.md
@@ -0,0 +1,312 @@
+# Manually parameterize an FMU
+Tutorial by Johannes Stoljar, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+This example shows how the manually parameterization of an FMU works if very specific adjustments during system initialization is needed. For this purpose, an IO-FMU model is loaded and the various commands for parameterization are shown on the basis of this model. With this example the user shall be guided how to make certain settings at an FMU. Please note, that parameterization of a simulation is possible in a much easier fashion: Using `fmiSimulate`, `fmiSimulateME` or `fmiSimulateCS` together with a parameter dictionary for the keyword `parameters`.
+
+## Target group
+The example is primarily intended for users who work in the field of simulation exchange. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/CS_simulate.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/CS_simulate.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/CS_simulate.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+
+## Code section
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+using FMI
+using FMIZoo
+```
+
+### Simulation setup
+
+Next, the start time and end time of the simulation are set.
+
+
+```julia
+tStart = 0.0
+tStop = 8.0
+```
+
+
+
+
+ 8.0
+
+
+
+### Import FMU
+
+In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown.
+
+
+```julia
+# we use an FMU from the FMIZoo.jl
+pathToFMU = get_model_filename("IO", "Dymola", "2022x")
+
+myFMU = fmiLoad(pathToFMU)
+fmiInfo(myFMU)
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_sXqX3p/IO`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_sXqX3p/IO/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193
+
+
+ #################### Begin information for FMU ####################
+ Model name: IO
+ FMI-Version: 2.0
+ GUID: {ac3b4a99-4908-40f7-89da-2d5c08b3c4ac}
+ Generation tool: Dymola Version 2022x (64-bit), 2021-10-08
+ Generation time: 2022-03-17T07:40:55Z
+ Var. naming conv.: structured
+ Event indicators: 4
+ Inputs: 3
+ 352321536 ["u_real"]
+ 352321537 ["u_boolean"]
+ 352321538 ["u_integer"]
+ Outputs: 3
+ 335544320 ["y_real"]
+ 335544321 ["y_boolean"]
+ 335544322 ["y_integer"]
+ States: 0
+ Supports Co-Simulation: true
+ Model identifier: IO
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ Var. com. steps: true
+ Input interpol.: true
+ Max order out. der.: 1
+ Supports Model-Exchange: true
+ Model identifier: IO
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ ##################### End information for FMU #####################
+
+
+### Instantiate and Setup FMU
+
+Next it is necessary to create an instance of the FMU. This is achieved by the command `fmiInstantiate!()`.
+
+
+```julia
+fmiInstantiate!(myFMU; loggingOn=true)
+```
+
+
+
+
+ FMU: IO
+ InstanceName: [not defined]
+ Address: Ptr{Nothing} @0x00000000029c12e0
+ State: fmi2ComponentStateInstantiated
+ Logging: true
+ FMU time: -Inf
+ FMU states: nothing
+
+
+
+In the following code block, start and end time for the simulation is set by the `fmiSetupExperiment()` command.
+
+
+```julia
+fmiSetupExperiment(myFMU, tStart, tStop)
+```
+
+
+
+
+ 0x00000000
+
+
+
+### Parameterize FMU
+
+To parameterize an FMU, the FMU must be in the initialization mode, which is reached with the `fmiEnterInitializationMode()` command.
+
+
+```julia
+fmiEnterInitializationMode(myFMU)
+```
+
+
+
+
+ 0x00000000
+
+
+
+Within this mode it is then possible to change the different parameters. In this example, for each data type (`real`, `integer`, `boolean` and `string)` a corresponding parameter is selected. At the beginning the initial state of these parameters is displayed with `fmiGet()`.
+
+
+```julia
+params = ["p_real", "p_integer", "p_boolean", "p_string"]
+fmiGet(myFMU, params)
+```
+
+
+
+
+ 4-element Vector{Any}:
+ 0.0
+ 0
+ 0
+ "Hello World!"
+
+
+
+In the next step, a function is defined that generates a random value for each parameter. For the parameter `p_string` a random number is inserted into the string. All parameters are combined to a vector and output.
+
+
+```julia
+function generateRandomNumbers()
+ rndReal = 100 * rand()
+ rndInteger = round(Integer, 100 * rand())
+ rndBoolean = rand() > 0.5
+ rndString = "Random number $(100 * rand())!"
+
+ randValues = [rndReal, rndInteger, rndBoolean, rndString]
+ println(randValues)
+ return randValues
+end
+```
+
+
+
+
+ generateRandomNumbers (generic function with 1 method)
+
+
+
+The previously defined function is called and the results are displayed in the console.
+
+
+```julia
+paramsVal = generateRandomNumbers();
+```
+
+ Any[27.27027116078753, 50, true, "Random number 83.09640957659121!"]
+
+
+#### First variant
+
+With this variant it is quickly possible to set all parameters at once. Even different data types can be set with only one command. The command `fmiSet()` selects itself which function is chosen for which data type. After setting the parameters, it is checked whether the corresponding parameters were set correctly. For this the function `fmiGet()` is used as above and afterwards with the macro `@assert` also tested whether the correct values are set.
+
+
+```julia
+fmiSet(myFMU, params, paramsVal)
+values = fmiGet(myFMU, params)
+print(values)
+
+@assert paramsVal == values
+```
+
+ Any[27.27027116078753, 50, 1, "Random number 83.09640957659121!"]
+
+#### Second variant
+
+To make sure that the functions work it is necessary to generate random numbers again. As shown already, we call the defined function `generateRandomNumbers()` and output the values.
+
+
+```julia
+rndReal, rndInteger, rndBoolean, rndString = generateRandomNumbers();
+```
+
+ Any[54.98169316988619, 76, true, "Random number 39.54384075977397!"]
+
+
+In the second variant, the value for each data type is set separately by the corresponding command. By this variant one has the maximum control and can be sure that also the correct data type is set. To illustrate the functionality of the parameterization with the separate functions, the corresponding get function is also called separately for each data type:
+* `fmiSetReal()` <---> `fmiGetReal()`
+* `fmiSetInteger()` <---> `fmiGetInteger()`
+* `fmiSetBoolean()` <---> `fmiGetBoolean()`
+* `fmiSetString()` <---> `fmiGetString()`.
+
+
+```julia
+fmiSetReal(myFMU, "p_real", rndReal)
+display("$rndReal == $(fmiGetReal(myFMU, "p_real"))")
+
+fmiSetInteger(myFMU, "p_integer", rndInteger)
+display("$rndInteger == $(fmiGetInteger(myFMU, "p_integer"))")
+
+fmiSetBoolean(myFMU, "p_boolean", rndBoolean)
+display("$rndBoolean == $(fmiGetBoolean(myFMU, "p_boolean"))")
+
+fmiSetString(myFMU, "p_string", rndString)
+display("$rndString == $(fmiGetString(myFMU, "p_string"))")
+```
+
+
+ "54.98169316988619 == 54.98169316988619"
+
+
+
+ "76 == 76"
+
+
+
+ "true == 1"
+
+
+
+ "Random number 39.54384075977397! == Random number 39.54384075977397!"
+
+
+After seeing that both variants set the parameters correctly, the initialization mode is terminated with the function `fmiExitInitializationMode()`.
+
+
+```julia
+fmiExitInitializationMode(myFMU)
+```
+
+
+
+
+ 0x00000000
+
+
+
+From here on, you may want to simulate the FMU. Please note, that with the default `executionConfig`, it is necessary to prevent a new instantiation using the keyword `instantiate=false`. Otherwise, a new instance is allocated for the simulation-call and the parameters set for the previous instance are not transfered.
+
+Example:
+`fmiSimulate(...; instantiate=false, ...)`
+
+### Unload FMU
+
+The FMU will be unloaded and all unpacked data on disc will be removed.
+
+
+```julia
+fmiUnload(myFMU)
+```
+
+### Summary
+
+Based on this tutorial it can be seen that there are two different variants to set and get parameters.These examples should make it clear to the user how parameters can also be set with different data types.
diff --git a/docs/src/examples/simulate.md b/docs/src/examples/simulate.md
new file mode 100644
index 00000000..b208de41
--- /dev/null
+++ b/docs/src/examples/simulate.md
@@ -0,0 +1,245 @@
+# Simulate an FMU in different modes
+Tutorial by Johannes Stoljar, Tobias Thummerer
+
+## License
+Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar
+
+Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.
+
+## Motivation
+This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.
+
+## Introduction to the example
+In this example we want to show how fast and easy the simulation for an FMU is. For this purpose, the FMU is simulated in co-simulation mode and in model-exchange mode. After the FMU has been simulated, the simulation results are displayed in a graph. The graphs of the different modes are compared with each other. The used model is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.
+
+![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true)
+
+
+## Target group
+The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.
+
+
+## Other formats
+Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/simulate.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/simulate.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/simulate.md) corresponding to the notebook.
+
+
+## Getting started
+
+### Installation prerequisites
+| | Description | Command | Alternative |
+|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|
+| 1. | Enter Package Manager via | ] | |
+| 2. | Install FMI via | add FMI | add " https://github.com/ThummeTo/FMI.jl " |
+| 3. | Install FMIZoo via | add FMIZoo | add " https://github.com/ThummeTo/FMIZoo.jl " |
+| 4. | Install Plots via | add Plots | |
+
+## Code section
+
+To run the example, the previously installed packages must be included.
+
+
+```julia
+# imports
+using FMI
+using FMIZoo
+using Plots
+```
+
+### Simulation setup
+
+Next, the start time and end time of the simulation are set. Finally, a step size is specified to store the results of the simulation at these time steps.
+
+
+```julia
+tStart = 0.0
+tStep = 0.01
+tStop = 8.0
+tSave = tStart:tStep:tStop
+```
+
+
+
+
+ 0.0:0.01:8.0
+
+
+
+### Import FMU
+
+In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown.
+
+
+```julia
+# we use an FMU from the FMIZoo.jl
+pathToFMU = get_model_filename("SpringFrictionPendulum1D", "Dymola", "2022x")
+
+myFMU = fmiLoad(pathToFMU)
+# fmiLoad("path/to/myFMU.fmu"; unpackPath = "path/to/unpacked/fmu/")
+
+fmiInfo(myFMU)
+```
+
+ ┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_OQnfcn/SpringFrictionPendulum1D`.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75
+ ┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_OQnfcn/SpringFrictionPendulum1D/resources`
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190
+ ┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.
+ └ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193
+
+
+ #################### Begin information for FMU ####################
+ Model name: SpringFrictionPendulum1D
+ FMI-Version: 2.0
+ GUID: {df491d8d-0598-4495-913e-5b025e54d7f2}
+ Generation tool: Dymola Version 2022x (64-bit), 2021-10-08
+ Generation time: 2022-03-03T15:09:18Z
+ Var. naming conv.: structured
+ Event indicators: 24
+ Inputs: 0
+ Outputs: 0
+ States: 2
+ 33554432 ["mass.s"]
+ 33554433 ["mass.v", "mass.v_relfric"]
+ Supports Co-Simulation: true
+ Model identifier: SpringFrictionPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ Var. com. steps: true
+ Input interpol.: true
+ Max order out. der.: 1
+ Supports Model-Exchange: true
+ Model identifier: SpringFrictionPendulum1D
+ Get/Set State: true
+ Serialize State: true
+ Dir. Derivatives: true
+ ##################### End information for FMU #####################
+
+
+### Simulate FMU
+
+In the following, the FMU is simulated in the two different simulation modes.
+
+#### Simulate as Co-Simulation
+
+In the next steps the recorded values are defined. The first state is the position of the mass and the second state is the velocity. In the function `fmiSimulateCS()` the FMU is simulated in co-simulation mode (CS) with an adaptive step size but with fixed save points `tSave`. In addition, the start and end time and the recorded variables are specified.
+
+
+```julia
+vrs = ["mass.s", "mass.v"]
+
+dataCS = fmiSimulateCS(myFMU, tStart, tStop; recordValues=vrs, saveat=tSave)
+```
+
+
+
+
+ Model name:
+ SpringFrictionPendulum1D
+ Success:
+ true
+ Values [801]:
+ 0.0 (0.5, 0.0)
+ 0.01 (0.5002235448486548, 0.042692491939260585)
+ 0.02 (0.5008715291319449, 0.08568000508550636)
+ 0.03 (0.5019478597521578, 0.12892136998736314)
+ 0.04 (0.5034570452098334, 0.17232325681284336)
+ 0.05 (0.5053993458877354, 0.2158440857658765)
+ 0.06 (0.5077764240578201, 0.259420181133082)
+ 0.07 (0.5105886522837868, 0.30295578207463486)
+ 0.08 (0.5138351439717114, 0.3464184707972189)
+ ...
+ 8.0 (1.071367253976742, -1.000814138594347e-10)
+ Events [0]:
+
+
+
+
+#### Simulate as Model-Exchange
+
+In the function `fmiSimulateME()` the FMU is simulated in model-exchange mode (ME) with an adaptive step size but with fixed save points `tSave`. In addition, the start and end time are specified. In contrast to the co-simulation, the values to be stored are not specified here, since the states and events of the FMU are always output as well. The identifiers given above just correspond to the states of the FMU.
+
+
+```julia
+dataME = fmiSimulateME(myFMU, tStart, tStop; saveat=tSave)
+```
+
+ [34mSimulating ME-FMU ... 100%|██████████████████████████████| Time: 0:00:09[39m
+
+
+
+
+
+ Model name:
+ SpringFrictionPendulum1D
+ Success:
+ true
+ States [801]:
+ 0.0 [0.5, 0.0]
+ 0.01 [0.5002131418344649, 0.042689450666241]
+ 0.02 [0.5008548874805565, 0.08570846215523381]
+ 0.03 [0.5019281653120716, 0.12898389312495082]
+ 0.04 [0.5034351805057593, 0.17244393475170294]
+ 0.05 [0.5053774287967188, 0.2160182432854046]
+ 0.06 [0.5077556967622916, 0.25963796675862466]
+ 0.07 [0.5105701003205937, 0.3032358690888718]
+ 0.08 [0.5138201049458624, 0.34674641820324037]
+ ...
+ 8.0 [1.0668213438183276, -1.0000099359121942e-10]
+ Events [6]:
+ State-Event #11 @ 0.0s
+ State-Event #11 @ 0.9939s
+ State-Event #19 @ 1.9881s
+ State-Event #11 @ 2.9829s
+ State-Event #19 @ 3.9787s
+ State-Event #11 @ 4.9768s
+
+
+
+
+### Plotting FMU
+
+After the simulation is finished the results of the FMU for the co-simulation and model-exchange mode can be plotted. In the plot for the FMU it can be seen that the oscillation continues to decrease due to the effect of the friction. If you simulate long enough, the oscillation comes to a standstill in a certain time.
+
+
+```julia
+fmiPlot(dataCS)
+```
+
+
+
+
+
+![svg](simulate_files/simulate_12_0.svg)
+
+
+
+
+
+```julia
+fmiPlot(dataME)
+```
+
+
+
+
+
+![svg](simulate_files/simulate_13_0.svg)
+
+
+
+
+From both graphs it can be seen that the simulation calculates exactly the same results.
+
+### Unload FMU
+
+After plotting the data, the FMU is unloaded and all unpacked data on disc is removed.
+
+
+```julia
+fmiUnload(myFMU)
+```
+
+### Summary
+
+Based on this tutorial it can be seen that simulating in the different mode is very easy, and it only takes a few commands to simulate the FMU.
diff --git a/docs/src/examples/simulate_files/simulate_12_0.svg b/docs/src/examples/simulate_files/simulate_12_0.svg
new file mode 100644
index 00000000..6f603526
--- /dev/null
+++ b/docs/src/examples/simulate_files/simulate_12_0.svg
@@ -0,0 +1,274 @@
+
+
diff --git a/docs/src/examples/simulate_files/simulate_13_0.svg b/docs/src/examples/simulate_files/simulate_13_0.svg
new file mode 100644
index 00000000..2dc08e6e
--- /dev/null
+++ b/docs/src/examples/simulate_files/simulate_13_0.svg
@@ -0,0 +1,295 @@
+
+
diff --git a/example/manipulation.ipynb b/example/manipulation.ipynb
new file mode 100644
index 00000000..eb98a208
--- /dev/null
+++ b/example/manipulation.ipynb
@@ -0,0 +1,839 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Manipulate a function\n",
+ "Tutorial by Johannes Stoljar, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "This example shows how to overwrite a library function with an own function. For this the FMU model is simulated first without changes. Then the function `fmi2GetReal()` is overwritten and simulated again. Both simulations are displayed in a graph to show the change caused by overwriting the function. The model used is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.\n",
+ "\n",
+ "![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true) \n",
+ "\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/manipulation.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/manipulation.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/manipulation.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |\n",
+ "| 4. | Install FMICore via | add FMICore | add \" https://github.com/ThummeTo/FMICore.jl \" |\n",
+ "| 5. | Install Plots via | add Plots | |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n",
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:15:09.601000Z",
+ "iopub.status.busy": "2022-06-01T15:15:08.923000Z",
+ "iopub.status.idle": "2022-06-01T15:15:32.386000Z",
+ "shell.execute_reply": "2022-06-01T15:15:32.310000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "using FMI\n",
+ "using FMIZoo\n",
+ "using FMICore\n",
+ "using Plots"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the start time and end time of the simulation are set."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:15:41.170000Z",
+ "iopub.status.busy": "2022-06-01T15:15:32.388000Z",
+ "iopub.status.idle": "2022-06-01T15:15:41.848000Z",
+ "shell.execute_reply": "2022-06-01T15:15:41.848000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "8.0"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "tStart = 0.0\n",
+ "tStop = 8.0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Import FMU\n",
+ "\n",
+ "In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:15:41.877000Z",
+ "iopub.status.busy": "2022-06-01T15:15:41.877000Z",
+ "iopub.status.idle": "2022-06-01T15:15:46.248000Z",
+ "shell.execute_reply": "2022-06-01T15:15:46.248000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_HiWCsS/SpringFrictionPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_HiWCsS/SpringFrictionPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "#################### Begin information for FMU ####################\n",
+ "\tModel name:\t\t\tSpringFrictionPendulum1D\n",
+ "\tFMI-Version:\t\t\t2.0\n",
+ "\tGUID:\t\t\t\t{df491d8d-0598-4495-913e-5b025e54d7f2}\n",
+ "\tGeneration tool:\t\tDymola Version 2022x (64-bit), 2021-10-08\n",
+ "\tGeneration time:\t\t2022-03-03T15:09:18Z\n",
+ "\tVar. naming conv.:\t\tstructured\n",
+ "\tEvent indicators:\t\t24\n",
+ "\tInputs:\t\t\t\t0\n",
+ "\tOutputs:\t\t\t0\n",
+ "\tStates:\t\t\t\t2\n",
+ "\t\t33554432 [\"mass.s\"]\n",
+ "\t\t33554433 [\"mass.v\", \"mass.v_relfric\"]\n",
+ "\tSupports Co-Simulation:\t\ttrue\n",
+ "\t\tModel identifier:\tSpringFrictionPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "\t\tVar. com. steps:\ttrue\n",
+ "\t\tInput interpol.:\ttrue\n",
+ "\t\tMax order out. der.:\t1\n",
+ "\tSupports Model-Exchange:\ttrue\n",
+ "\t\tModel identifier:\tSpringFrictionPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "##################### End information for FMU #####################\n"
+ ]
+ }
+ ],
+ "source": [
+ "# we use an FMU from the FMIZoo.jl\n",
+ "pathToFMU = get_model_filename(\"SpringFrictionPendulum1D\", \"Dymola\", \"2022x\")\n",
+ "\n",
+ "myFMU = fmiLoad(pathToFMU)\n",
+ "\n",
+ "fmiInfo(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulate FMU\n",
+ "\n",
+ "In the next steps the recorded value is defined. The recorded value is the position of the mass. In the function `fmiSimulateME()` the FMU is simulated in model-exchange mode (ME) with an adaptive step size. In addition, the start and end time and the recorded variables are specified."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:15:46.250000Z",
+ "iopub.status.busy": "2022-06-01T15:15:46.250000Z",
+ "iopub.status.idle": "2022-06-01T15:16:04.451000Z",
+ "shell.execute_reply": "2022-06-01T15:16:04.450000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\u001b[34mSimulating ME-FMU ... 100%|██████████████████████████████| Time: 0:00:10\u001b[39m\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "Model name:\n",
+ "\tSpringFrictionPendulum1D\n",
+ "Success:\n",
+ "\ttrue\n",
+ "States [110]:\n",
+ "\t0.0\t[0.5, 0.0]\n",
+ "\t2.3529411764719727e-11\t[0.5, 1.0e-10]\n",
+ "\t1.0000023529411766e-5\t[0.5000000002125017, 4.250030186348503e-5]\n",
+ "\t0.00011000002352941177\t[0.5000000257134062, 0.0004675245102952289]\n",
+ "\t0.0011100000235294118\t[0.5000026191281834, 0.004719970360497647]\n",
+ "\t0.011110000023529413\t[0.5002631686645611, 0.047449647283752144]\n",
+ "\t0.03982466736770743\t[0.5034050123596227, 0.17168075110950387]\n",
+ "\t0.09972054285078226\t[0.5215006197560228, 0.43204954153497455]\n",
+ "\t0.16481836271111422\t[0.5585747156842935, 0.703758265932321]\n",
+ "\t...\n",
+ "\t8.0\t[1.0668213438183276, -1.0000099359121942e-10]\n",
+ "Values [110]:\n",
+ "\t0.0\t(0.5,)\n",
+ "\t2.3529411764719727e-11\t(0.5,)\n",
+ "\t1.0000023529411766e-5\t(0.5000000002125017,)\n",
+ "\t0.00011000002352941177\t(0.5000000257134062,)\n",
+ "\t0.0011100000235294118\t(0.5000026191281834,)\n",
+ "\t0.011110000023529413\t(0.5002631686645611,)\n",
+ "\t0.03982466736770743\t(0.5034050123596227,)\n",
+ "\t0.09972054285078226\t(0.5215006197560228,)\n",
+ "\t0.16481836271111422\t(0.5585747156842935,)\n",
+ "\t...\n",
+ "\t8.0\t(1.0668213438183276,)\n",
+ "Events [6]:\n",
+ "\tState-Event #11 @ 0.0s\n",
+ "\tState-Event #11 @ 0.9939s\n",
+ "\tState-Event #19 @ 1.9881s\n",
+ "\tState-Event #11 @ 2.9829s\n",
+ "\tState-Event #19 @ 3.9787s\n",
+ "\tState-Event #11 @ 4.9768s\n"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "vrs = [\"mass.s\"]\n",
+ "\n",
+ "simData = fmiSimulateME(myFMU, tStart, tStop; recordValues=vrs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Plotting FMU\n",
+ "\n",
+ "After the simulation is finished, the result of the FMU for the model-exchange mode can be plotted. In the plot for the FMU it can be seen that the oscillation continues to decrease due to the effect of the friction. If you simulate long enough, the oscillation comes to a standstill in a certain time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:04.454000Z",
+ "iopub.status.busy": "2022-06-01T15:16:04.454000Z",
+ "iopub.status.idle": "2022-06-01T15:16:23.184000Z",
+ "shell.execute_reply": "2022-06-01T15:16:23.184000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fig = fmiPlot(simData, states=false)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Override Function\n",
+ "\n",
+ "After overwriting a function, the previous one is no longer accessible. The original function `fmi2GetReal()` is cached by storing the address of the pointer. The addresses of the pointers are kept in the FMU and are thus accessible."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:23.189000Z",
+ "iopub.status.busy": "2022-06-01T15:16:23.188000Z",
+ "iopub.status.idle": "2022-06-01T15:16:23.780000Z",
+ "shell.execute_reply": "2022-06-01T15:16:23.780000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Ptr{Nothing} @0x00007f52e9530faf"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# save, where the original `fmi2GetReal` function was stored, so we can access it in our new function\n",
+ "originalGetReal = myFMU.cGetReal"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To overwrite the function `fmi2GetReal!()`, the function header of the new custom function must be identical to the previous one. The function header looks like `fmi2GetReal!(cfunc::Ptr{Nothing}, c::fmi2Component, vr::Union{Array{fmi2ValueReference}, Ptr{fmi2ValueReference}}, nvr::Csize_t, value::Union{Array{fmi2Real}, Ptr{fmi2Real}})::fmi2Status`. The information how the FMI2 function are structured can be seen from [FMICore.jl](https://github.com/ThummeTo/FMICore.jl/blob/main/src/FMI2_c.jl#L718) or the FMI2.0.3-specification.\n",
+ "\n",
+ "In the new implementation the original function is called by the previously stored pointer. Next there is a special handling if `value` is a pointer to an array. In this case the pointer is treated as an array, so that the entries are accessible. Otherwise, each value in `value` is multiplied by two. Finally, the original state of the original function is output."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:23.784000Z",
+ "iopub.status.busy": "2022-06-01T15:16:23.784000Z",
+ "iopub.status.idle": "2022-06-01T15:16:24.562000Z",
+ "shell.execute_reply": "2022-06-01T15:16:24.562000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "myGetReal! (generic function with 1 method)"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "function myGetReal!(c::fmi2Component, vr::Union{Array{fmi2ValueReference}, Ptr{fmi2ValueReference}}, \n",
+ " nvr::Csize_t, value::Union{Array{fmi2Real}, Ptr{fmi2Real}})\n",
+ " # first, we do what the original function does\n",
+ " status = fmi2GetReal!(originalGetReal, c, vr, nvr, value)\n",
+ "\n",
+ " # if we have a pointer to an array, we must interprete it as array to access elements\n",
+ " if isa(value, Ptr{fmi2Real})\n",
+ " value = unsafe_wrap(Array{fmi2Real}, value, nvr, own=false)\n",
+ " end\n",
+ "\n",
+ " # now, we multiply every value by two (just for fun!)\n",
+ " for i in 1:nvr \n",
+ " value[i] *= 2.0 \n",
+ " end \n",
+ "\n",
+ " # return the original status\n",
+ " return status\n",
+ "end"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the next command the original function is overwritten with the new defined function, for which the command `fmiSetFctGetReal()` is called."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:24.566000Z",
+ "iopub.status.busy": "2022-06-01T15:16:24.565000Z",
+ "iopub.status.idle": "2022-06-01T15:16:24.578000Z",
+ "shell.execute_reply": "2022-06-01T15:16:24.578000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Ptr{Nothing} @0x00007f533e325fc0"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# no we overwrite the original function\n",
+ "fmiSetFctGetReal(myFMU, myGetReal!)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulate and Plot FMU with modified function\n",
+ "\n",
+ "As before, the identical command is called here for simulation. This is also a model exchange simulation. Immediately afterwards, the results are added to the previous graph as a dashed line."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:24.581000Z",
+ "iopub.status.busy": "2022-06-01T15:16:24.581000Z",
+ "iopub.status.idle": "2022-06-01T15:16:24.963000Z",
+ "shell.execute_reply": "2022-06-01T15:16:24.963000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "simData = fmiSimulateME(myFMU, tStart, tStop; recordValues=vrs)\n",
+ "fmiPlot!(fig, simData; states=false, style=:dash)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As expected by overwriting the function, all values are doubled."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unload FMU\n",
+ "\n",
+ "After plotting the data, the FMU is unloaded and all unpacked data on disc is removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:24.967000Z",
+ "iopub.status.busy": "2022-06-01T15:16:24.967000Z",
+ "iopub.status.idle": "2022-06-01T15:16:25.011000Z",
+ "shell.execute_reply": "2022-06-01T15:16:25.011000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "fmiUnload(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "In this tutorial it is shown how an existing function of the library can be replaced by an own implementation. Through this possibility, there are almost no limits for the user, whereby the user can customize the function to his liking."
+ ]
+ }
+ ],
+ "metadata": {
+ "interpreter": {
+ "hash": "037537ff7419c497b9325f7d495147943224d408cf5d5ed915294a5b960167b0"
+ },
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "comment_magics": "false",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "Julia 1.6.6",
+ "language": "julia",
+ "name": "julia-1.6"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ },
+ "nteract": {
+ "version": "0.28.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/example/modelica_conference_2021.ipynb b/example/modelica_conference_2021.ipynb
new file mode 100644
index 00000000..7210b368
--- /dev/null
+++ b/example/modelica_conference_2021.ipynb
@@ -0,0 +1,560 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Advanced Simulation of an FMU\n",
+ "Tutorial by Johannes Stoljar, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "In this example we would like to show that besides the simple simulation of an FMU there is also a more advanced version of the simulation. The advantage of the more advanced variant is that there are more possibilities to intervene in the simulation to make changes. After the FMU has been simulated, the simulation results are displayed in a graph. The used model is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.\n",
+ "\n",
+ "![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true) \n",
+ "\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/modelica_conference_2021.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/modelica_conference_2021.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/modelica_conference_2021.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |\n",
+ "| 4. | Install Plots via | add Plots | |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n",
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:16:34.235000Z",
+ "iopub.status.busy": "2022-06-01T15:16:33.558000Z",
+ "iopub.status.idle": "2022-06-01T15:16:56.948000Z",
+ "shell.execute_reply": "2022-06-01T15:16:56.869000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "using FMI\n",
+ "using FMIZoo\n",
+ "using Plots"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the start time and end time of the simulation are set. Finally, a step size is specified to store the results of the simulation at these time steps."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:05.805000Z",
+ "iopub.status.busy": "2022-06-01T15:16:56.951000Z",
+ "iopub.status.idle": "2022-06-01T15:17:06.600000Z",
+ "shell.execute_reply": "2022-06-01T15:17:06.600000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.0:0.1:8.0"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "tStart = 0.0\n",
+ "tStep = 0.1\n",
+ "tStop = 8.0\n",
+ "tSave = tStart:tStep:tStop"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simple FMU Simulation\n",
+ "\n",
+ "In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:06.604000Z",
+ "iopub.status.busy": "2022-06-01T15:17:06.604000Z",
+ "iopub.status.idle": "2022-06-01T15:17:10.951000Z",
+ "shell.execute_reply": "2022-06-01T15:17:10.951000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_ksTsah/SpringFrictionPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_ksTsah/SpringFrictionPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "#################### Begin information for FMU ####################\n",
+ "\tModel name:\t\t\tSpringFrictionPendulum1D\n",
+ "\tFMI-Version:\t\t\t2.0\n",
+ "\tGUID:\t\t\t\t{df491d8d-0598-4495-913e-5b025e54d7f2}\n",
+ "\tGeneration tool:\t\tDymola Version 2022x (64-bit), 2021-10-08\n",
+ "\tGeneration time:\t\t2022-03-03T15:09:18Z\n",
+ "\tVar. naming conv.:\t\tstructured\n",
+ "\tEvent indicators:\t\t24\n",
+ "\tInputs:\t\t\t\t0\n",
+ "\tOutputs:\t\t\t0\n",
+ "\tStates:\t\t\t\t2\n",
+ "\t\t33554432 [\"mass.s\"]\n",
+ "\t\t33554433 [\"mass.v\", \"mass.v_relfric\"]\n",
+ "\tSupports Co-Simulation:\t\ttrue\n",
+ "\t\tModel identifier:\tSpringFrictionPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "\t\tVar. com. steps:\ttrue\n",
+ "\t\tInput interpol.:\ttrue\n",
+ "\t\tMax order out. der.:\t1\n",
+ "\tSupports Model-Exchange:\ttrue\n",
+ "\t\tModel identifier:\tSpringFrictionPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "##################### End information for FMU #####################\n"
+ ]
+ }
+ ],
+ "source": [
+ "# we use an FMU from the FMIZoo.jl\n",
+ "pathToFMU = get_model_filename(\"SpringFrictionPendulum1D\", \"Dymola\", \"2022x\")\n",
+ "\n",
+ "myFMU = fmiLoad(pathToFMU)\n",
+ "fmiInfo(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the next commands the FMU is simulated, for which the start and end time and recorded variables are declared. Afterwards the simulation result is shown in a graph. In the plot for the FMU, it can be seen that the oscillation keeps decreasing due to the effect of friction. If one simulates long enough, the oscillation comes to a standstill after a certain time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:10.954000Z",
+ "iopub.status.busy": "2022-06-01T15:17:10.953000Z",
+ "iopub.status.idle": "2022-06-01T15:17:37.913000Z",
+ "shell.execute_reply": "2022-06-01T15:17:37.913000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "simData = fmiSimulate(myFMU, tStart, tStop; recordValues=[\"mass.s\"], saveat=tSave)\n",
+ "fmiPlot(simData)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "After plotting the data, the FMU is unloaded and all unpacked data on disc is removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:37.918000Z",
+ "iopub.status.busy": "2022-06-01T15:17:37.917000Z",
+ "iopub.status.idle": "2022-06-01T15:17:37.960000Z",
+ "shell.execute_reply": "2022-06-01T15:17:37.960000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "fmiUnload(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Advanced FMU Simulation\n",
+ "\n",
+ "In the following type of simulation a more advanced variant is presented, which allows intervening more in the simulation process. Analogous to the simple variant, an FMU model must be loaded."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:37.962000Z",
+ "iopub.status.busy": "2022-06-01T15:17:37.962000Z",
+ "iopub.status.idle": "2022-06-01T15:17:38.024000Z",
+ "shell.execute_reply": "2022-06-01T15:17:38.024000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_NQAhCe/SpringFrictionPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_NQAhCe/SpringFrictionPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193\n"
+ ]
+ }
+ ],
+ "source": [
+ "myFMU = fmiLoad(pathToFMU);"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, it is necessary to create an instance of the FMU, this is achieved by the command `fmiInstantiate!()`. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:38.028000Z",
+ "iopub.status.busy": "2022-06-01T15:17:38.027000Z",
+ "iopub.status.idle": "2022-06-01T15:17:38.349000Z",
+ "shell.execute_reply": "2022-06-01T15:17:38.349000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "FMU: SpringFrictionPendulum1D\n",
+ "InstanceName: [not defined]\n",
+ "Address: Ptr{Nothing} @0x00000000064853f0\n",
+ "State: fmi2ComponentStateInstantiated\n",
+ "Logging: false\n",
+ "FMU time: -Inf\n",
+ "FMU states: nothing"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "instanceFMU = fmiInstantiate!(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the following code block, start and end time for the simulation is set by the `fmiSetupExperiment()` command. Next, the FMU is initialized by the calls of `fmiEnterInitializationMode()` and `fmiExitInitializationMode()`. It would also be possible to set initial states for the FMU between these two commands. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:38.353000Z",
+ "iopub.status.busy": "2022-06-01T15:17:38.352000Z",
+ "iopub.status.idle": "2022-06-01T15:17:38.617000Z",
+ "shell.execute_reply": "2022-06-01T15:17:38.617000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0x00000000"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiSetupExperiment(instanceFMU, tStart, tStop)\n",
+ "fmiEnterInitializationMode(instanceFMU)\n",
+ "# set initial model states\n",
+ "fmiExitInitializationMode(instanceFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The actual simulation loop is shown in the following block. Here a simulation step `fmiDoStep()` with the fixed step size `tStep` is executed. As indicated in the code by the comments, the input values and output values of the FMU could be changed in the simulation loop as desired, whereby the higher possibility of adjustments arises."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:38.620000Z",
+ "iopub.status.busy": "2022-06-01T15:17:38.620000Z",
+ "iopub.status.idle": "2022-06-01T15:17:38.657000Z",
+ "shell.execute_reply": "2022-06-01T15:17:38.657000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "for t in tSave\n",
+ " # set model inputs \n",
+ " # ...\n",
+ " fmiDoStep(instanceFMU, tStep)\n",
+ " # get model outputs\n",
+ " # ...\n",
+ "end"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The instantiated FMU must be terminated and then the memory area for the instance can also be deallocated. The last step is to unload the FMU to remove all unpacked data on disc. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:38.660000Z",
+ "iopub.status.busy": "2022-06-01T15:17:38.660000Z",
+ "iopub.status.idle": "2022-06-01T15:17:38.668000Z",
+ "shell.execute_reply": "2022-06-01T15:17:38.667000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "fmiTerminate(instanceFMU)\n",
+ "fmiFreeInstance!(instanceFMU)\n",
+ "fmiUnload(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "The tutorial has shown that besides the usual simple variant of simulating an FMU, there is another way to make more adjustments."
+ ]
+ }
+ ],
+ "metadata": {
+ "interpreter": {
+ "hash": "037537ff7419c497b9325f7d495147943224d408cf5d5ed915294a5b960167b0"
+ },
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "comment_magics": "false",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "Julia 1.6.6",
+ "language": "julia",
+ "name": "julia-1.6"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ },
+ "nteract": {
+ "version": "0.28.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/example/multiple_instances.ipynb b/example/multiple_instances.ipynb
new file mode 100644
index 00000000..93f49f93
--- /dev/null
+++ b/example/multiple_instances.ipynb
@@ -0,0 +1,3081 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Multiple Instances of an FMU\n",
+ "Tutorial by Johannes Stoljar, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "In this example we want to show that it is possible to create different instances of an FMU. The different instances can then be used to run independent simulations. After the FMU has been simulated, the simulation results are displayed in a graph. The used model is a one-dimensional spring pendulum without friction. The object-orientated structure of the *SpringPendulum1D* can be seen in the following graphic.\n",
+ "\n",
+ "![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringPendulum1D.svg?raw=true) \n",
+ "\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/multiple_instances.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/multiple_instances.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/multiple_instances.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |\n",
+ "| 4. | Install Plots via | add Plots | |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n",
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:17:48.035000Z",
+ "iopub.status.busy": "2022-06-01T15:17:47.343000Z",
+ "iopub.status.idle": "2022-06-01T15:18:10.300000Z",
+ "shell.execute_reply": "2022-06-01T15:18:10.226000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "using FMI\n",
+ "using FMIZoo\n",
+ "using Plots"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the start time and end time of the simulation are set. Finally, the recorded values are specified to store the results of the simulation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:19.079000Z",
+ "iopub.status.busy": "2022-06-01T15:18:10.303000Z",
+ "iopub.status.idle": "2022-06-01T15:18:21.932000Z",
+ "shell.execute_reply": "2022-06-01T15:18:21.932000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1-element Vector{String}:\n",
+ " \"mass.s\""
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "tStart = 0.0\n",
+ "tStop = 8.0\n",
+ "\n",
+ "vrs = [\"mass.s\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Import FMU\n",
+ "\n",
+ "In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:21.935000Z",
+ "iopub.status.busy": "2022-06-01T15:18:21.935000Z",
+ "iopub.status.idle": "2022-06-01T15:18:26.345000Z",
+ "shell.execute_reply": "2022-06-01T15:18:26.345000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_RGs05M/SpringPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_RGs05M/SpringPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "#################### Begin information for FMU ####################\n",
+ "\tModel name:\t\t\tSpringPendulum1D\n",
+ "\tFMI-Version:\t\t\t2.0\n",
+ "\tGUID:\t\t\t\t{a3e886fa-675c-4308-8e91-3490e598ba11}\n",
+ "\tGeneration tool:\t\tDymola Version 2022x (64-bit), 2021-10-08\n",
+ "\tGeneration time:\t\t2022-03-03T15:08:57Z\n",
+ "\tVar. naming conv.:\t\tstructured\n",
+ "\tEvent indicators:\t\t0\n",
+ "\tInputs:\t\t\t\t0\n",
+ "\tOutputs:\t\t\t0\n",
+ "\tStates:\t\t\t\t2\n",
+ "\t\t33554432 [\"mass.s\"]\n",
+ "\t\t33554433 [\"mass.v\"]\n",
+ "\tSupports Co-Simulation:\t\ttrue\n",
+ "\t\tModel identifier:\tSpringPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "\t\tVar. com. steps:\ttrue\n",
+ "\t\tInput interpol.:\ttrue\n",
+ "\t\tMax order out. der.:\t1\n",
+ "\tSupports Model-Exchange:\ttrue\n",
+ "\t\tModel identifier:\tSpringPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "##################### End information for FMU #####################\n"
+ ]
+ }
+ ],
+ "source": [
+ "# we use an FMU from the FMIZoo.jl\n",
+ "pathToFMU = get_model_filename(\"SpringPendulum1D\", \"Dymola\", \"2022x\")\n",
+ "\n",
+ "myFMU = fmiLoad(pathToFMU)\n",
+ "fmiInfo(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### First Instance\n",
+ "\n",
+ "To create an instance of the FMU it is necessary to call the command `fmiInstantiate!()`. With the component address you now have a unique instance of the FMU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:26.348000Z",
+ "iopub.status.busy": "2022-06-01T15:18:26.348000Z",
+ "iopub.status.idle": "2022-06-01T15:18:26.776000Z",
+ "shell.execute_reply": "2022-06-01T15:18:26.776000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "FMU: SpringPendulum1D\n",
+ "InstanceName: [not defined]\n",
+ "Address: Ptr{Nothing} @0x0000000003954580\n",
+ "State: fmi2ComponentStateInstantiated\n",
+ "Logging: false\n",
+ "FMU time: -Inf\n",
+ "FMU states: nothing\n"
+ ]
+ }
+ ],
+ "source": [
+ "comp1 = fmiInstantiate!(myFMU; loggingOn=true)\n",
+ "comp1Address= comp1.compAddr\n",
+ "println(comp1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next, a dictionary for the parameters is created. With this dictionary you can set the initial states of the variables of the FMU. For the spring constant `spring.c` a value of $10.0 \\frac{N}{m}$ and for the position of the mass `mass.s` a value of $1.0 m$ is set. The created dictionary with the specified variables for recording are passed to the command for simulation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:26.779000Z",
+ "iopub.status.busy": "2022-06-01T15:18:26.779000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.116000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.116000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "param1 = Dict(\"spring.c\"=>10.0, \"mass_s0\"=>1.0)\n",
+ "data1 = fmiSimulate(comp1, tStart, tStop; parameters=param1, recordValues=vrs, instantiate=false)\n",
+ "fig = fmiPlot(data1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For control, you can compare again the address of the instance to the previous address, and it should be the same address. As soon as this is not the case an error would be thrown by the macro `@assert`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:52.122000Z",
+ "iopub.status.busy": "2022-06-01T15:18:52.122000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.160000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.160000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "@assert comp1.compAddr === comp1Address"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Second Instance\n",
+ "\n",
+ "To create a second instance of the FMU it is necessary to call the command `fmiInstantiate!()`. With the component address you now have a unique instance of the FMU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:52.163000Z",
+ "iopub.status.busy": "2022-06-01T15:18:52.163000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.166000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.166000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "FMU: SpringPendulum1D\n",
+ "InstanceName: [not defined]\n",
+ "Address: Ptr{Nothing} @0x000000000529d440\n",
+ "State: fmi2ComponentStateInstantiated\n",
+ "Logging: false\n",
+ "FMU time: -Inf\n",
+ "FMU states: nothing\n"
+ ]
+ }
+ ],
+ "source": [
+ "comp2 = fmiInstantiate!(myFMU; loggingOn=true)\n",
+ "comp2Address= comp2.compAddr\n",
+ "println(comp2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The addresses of the instantiated FMUs must differ, and you can see that in the comparison below."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:52.168000Z",
+ "iopub.status.busy": "2022-06-01T15:18:52.168000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.169000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.169000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "@assert comp1Address !== comp2Address"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Again, a dictionary for the parameters is created. With this dictionary you can set the initial states of the variables of the FMU. For the spring constant `spring.c` a value of $1.0 \\frac{N}{m}$ and for the position of the mass `mass.s` a value of $2.0 m$ is set. The created dictionary with the specified variables for recording are passed to the command for simulation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:52.171000Z",
+ "iopub.status.busy": "2022-06-01T15:18:52.171000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.280000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.280000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "param2 = Dict(\"spring.c\"=>1.0, \"mass.s\"=>2.0)\n",
+ "data2 = fmiSimulateCS(comp2, tStart, tStop; parameters=param2, recordValues=vrs, instantiate=false)\n",
+ "fmiPlot!(fig, data2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For control, you can compare again the address of the instance `comp2` to the previous address `comp2Address` and it should be the same address."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:52.288000Z",
+ "iopub.status.busy": "2022-06-01T15:18:52.288000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.289000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.289000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "@assert comp2.compAddr === comp2Address"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unload FMU\n",
+ "\n",
+ "After plotting the data, the FMU is unloaded and all unpacked data on disc is removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:18:52.291000Z",
+ "iopub.status.busy": "2022-06-01T15:18:52.291000Z",
+ "iopub.status.idle": "2022-06-01T15:18:52.333000Z",
+ "shell.execute_reply": "2022-06-01T15:18:52.333000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "fmiUnload(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "Based on the example it can be seen that it is possible to create different instances of an FMU. The different instances can then be used to perform different simulations."
+ ]
+ }
+ ],
+ "metadata": {
+ "interpreter": {
+ "hash": "037537ff7419c497b9325f7d495147943224d408cf5d5ed915294a5b960167b0"
+ },
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "comment_magics": "false",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "Julia 1.6.6",
+ "language": "julia",
+ "name": "julia-1.6"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ },
+ "nteract": {
+ "version": "0.28.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/example/multiprocessing.ipynb b/example/multiprocessing.ipynb
new file mode 100644
index 00000000..736d07d5
--- /dev/null
+++ b/example/multiprocessing.ipynb
@@ -0,0 +1,516 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Multiprocessing\n",
+ "Tutorial by Jonas Wilfert, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar, Jonas Wilfert\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "This example shows how to parallelize the computation of an FMU in FMI.jl. We can compute a batch of FMU-evaluations in parallel with different initial settings.\n",
+ "Parallelization can be achieved using multithreading or using multiprocessing. This example shows **multiprocessing**, check `multithreading.ipynb` for multithreading.\n",
+ "Advantage of multithreading is a lower communication overhead as well as lower RAM usage.\n",
+ "However in some cases multiprocessing can be faster as the garbage collector is not shared.\n",
+ "\n",
+ "\n",
+ "The model used is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.\n",
+ "\n",
+ "![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true) \n",
+ "\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/distributed.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/distributed.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/distributed.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |\n",
+ "| 4. | Install FMICore via | add FMICore | add \" https://github.com/ThummeTo/FMICore.jl \" |\n",
+ "| 5. | Install BenchmarkTools via | add BenchmarkTools | |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Adding your desired amount of processes:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:20:33.662000Z",
+ "iopub.status.busy": "2022-06-18T14:20:32.980000Z",
+ "iopub.status.idle": "2022-06-18T14:20:45.287000Z",
+ "shell.execute_reply": "2022-06-18T14:20:45.259000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "4-element Vector{Int64}:\n",
+ " 2\n",
+ " 3\n",
+ " 4\n",
+ " 5"
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "using Distributed\n",
+ "n_procs = 4\n",
+ "addprocs(n_procs; exeflags=`--project=$(Base.active_project()) --threads=auto`, restrict=false)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:20:45.290000Z",
+ "iopub.status.busy": "2022-06-18T14:20:45.289000Z",
+ "iopub.status.idle": "2022-06-18T14:21:31.517000Z",
+ "shell.execute_reply": "2022-06-18T14:21:31.517000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "@everywhere using FMI\n",
+ "@everywhere using FMIZoo\n",
+ "@everywhere using BenchmarkTools"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Checking that we workers have been correctly initialized:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:21:31.917000Z",
+ "iopub.status.busy": "2022-06-18T14:21:31.520000Z",
+ "iopub.status.idle": "2022-06-18T14:21:32.071000Z",
+ "shell.execute_reply": "2022-06-18T14:21:32.071000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Hello World!\n",
+ " From worker 2:\tHello World!\n",
+ " From worker 4:\tHello World!\n",
+ " From worker 3:\tHello World!\n",
+ " From worker 5:\tHello World!\n"
+ ]
+ }
+ ],
+ "source": [
+ "workers()\n",
+ "\n",
+ "@everywhere println(\"Hello World!\")\n",
+ "\n",
+ "# The following lines can be uncommented for more advanced informations about the subprocesses\n",
+ "# @everywhere println(pwd())\n",
+ "# @everywhere println(Base.active_project())\n",
+ "# @everywhere println(gethostname())\n",
+ "# @everywhere println(VERSION)\n",
+ "# @everywhere println(Threads.nthreads())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the batch size and input values are defined."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:21:32.074000Z",
+ "iopub.status.busy": "2022-06-18T14:21:32.074000Z",
+ "iopub.status.idle": "2022-06-18T14:21:35.419000Z",
+ "shell.execute_reply": "2022-06-18T14:21:35.419000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "16-element Vector{Vector{Float64}}:\n",
+ " [0.053272555584304104, 0.45774751559489646]\n",
+ " [0.5896778258750763, 0.12435199368996086]\n",
+ " [0.5651662129022856, 0.8764873667695812]\n",
+ " [0.6176612662918868, 0.2785804850450695]\n",
+ " [0.9274233385552415, 0.38275426899967724]\n",
+ " [0.46094177953642124, 0.6554519159504497]\n",
+ " [0.73421590884579, 0.28177572874256906]\n",
+ " [0.5575198504784948, 0.3212214708846175]\n",
+ " [0.271126562512229, 0.6499895887749823]\n",
+ " [0.5289045741655536, 0.4873381265273107]\n",
+ " [0.8565770844036877, 0.2312687408548988]\n",
+ " [0.06332369482582334, 0.44807066874864065]\n",
+ " [0.08611017776660712, 0.960898209225902]\n",
+ " [0.3191189843443898, 0.30014344015893846]\n",
+ " [0.028884098958377402, 0.5285860311385517]\n",
+ " [0.7282727650515579, 0.7584435212215901]"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\n",
+ "# Best if batchSize is a multiple of the threads/cores\n",
+ "batchSize = 16\n",
+ "\n",
+ "# Define an array of arrays randomly\n",
+ "input_values = collect(collect.(eachrow(rand(batchSize,2))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Shared Module\n",
+ "For Distributed we need to embed the FMU into its own `module`. This prevents Distributed from trying to serialize and send the FMU over the network, as this can cause issues. This module needs to be made available on all processes using `@everywhere`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:21:35.423000Z",
+ "iopub.status.busy": "2022-06-18T14:21:35.422000Z",
+ "iopub.status.idle": "2022-06-18T14:21:47.166000Z",
+ "shell.execute_reply": "2022-06-18T14:21:47.166000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_R8823R/SpringPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_R8823R/SpringPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:193\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_xI4R81/SpringPendulum1D`.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_qUcQP8/SpringPendulum1D`.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_S33uq0/SpringPendulum1D`.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_M7VjAQ/SpringPendulum1D`.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_xI4R81/SpringPendulum1D/resources`\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_qUcQP8/SpringPendulum1D/resources`\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_S33uq0/SpringPendulum1D/resources`\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU resources location is `file:////tmp/fmijl_M7VjAQ/SpringPendulum1D/resources`\n",
+ "\u001b[36m\u001b[1m[ \u001b[22m\u001b[39m\u001b[36m\u001b[1mInfo: \u001b[22m\u001b[39mfmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n"
+ ]
+ }
+ ],
+ "source": [
+ "@everywhere module SharedModule\n",
+ " using FMIZoo\n",
+ " using FMI\n",
+ "\n",
+ " t_start = 0.0\n",
+ " t_step = 0.1\n",
+ " t_stop = 10.0\n",
+ " tspan = (t_start, t_stop)\n",
+ " tData = collect(t_start:t_step:t_stop)\n",
+ "\n",
+ " model_fmu = FMIZoo.fmiLoad(\"SpringPendulum1D\", \"Dymola\", \"2022x\")\n",
+ "end"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We define a helper function to calculate the FMU and combine it into an Matrix."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:21:47.169000Z",
+ "iopub.status.busy": "2022-06-18T14:21:47.169000Z",
+ "iopub.status.idle": "2022-06-18T14:21:47.459000Z",
+ "shell.execute_reply": "2022-06-18T14:21:47.458000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "@everywhere function runCalcFormatted(fmu, x0, recordValues=[\"mass.s\", \"mass.v\"])\n",
+ " data = fmiSimulateME(fmu, SharedModule.t_start, SharedModule.t_stop; recordValues=recordValues, saveat=SharedModule.tData, x0=x0, showProgress=false, dtmax=1e-4)\n",
+ " return reduce(hcat, data.states.u)\n",
+ "end"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running a single evaluation is pretty quick, therefore the speed can be better tested with BenchmarkTools."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:21:47.461000Z",
+ "iopub.status.busy": "2022-06-18T14:21:47.461000Z",
+ "iopub.status.idle": "2022-06-18T14:22:23.804000Z",
+ "shell.execute_reply": "2022-06-18T14:22:23.804000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "BenchmarkTools.Trial: 17 samples with 1 evaluation.\n",
+ " Range \u001b[90m(\u001b[39m\u001b[36m\u001b[1mmin\u001b[22m\u001b[39m … \u001b[35mmax\u001b[39m\u001b[90m): \u001b[39m\u001b[36m\u001b[1m304.291 ms\u001b[22m\u001b[39m … \u001b[35m325.332 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmin … max\u001b[90m): \u001b[39m7.41% … 6.89%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[34m\u001b[1mmedian\u001b[22m\u001b[39m\u001b[90m): \u001b[39m\u001b[34m\u001b[1m307.274 ms \u001b[22m\u001b[39m\u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmedian\u001b[90m): \u001b[39m7.37%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[32m\u001b[1mmean\u001b[22m\u001b[39m ± \u001b[32mσ\u001b[39m\u001b[90m): \u001b[39m\u001b[32m\u001b[1m309.397 ms\u001b[22m\u001b[39m ± \u001b[32m 5.860 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmean ± σ\u001b[90m): \u001b[39m7.94% ± 1.32%\n",
+ "\n",
+ " \u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m \u001b[39m▁\u001b[39m█\u001b[39m \u001b[39m▁\u001b[34m▁\u001b[39m\u001b[39m \u001b[39m \u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m \u001b[32m \u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m▁\u001b[39m \u001b[39m▁\u001b[39m \u001b[39m▁\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m▁\u001b[39m \u001b[39m \n",
+ " \u001b[39m█\u001b[39m█\u001b[39m█\u001b[39m▁\u001b[39m█\u001b[39m█\u001b[39m▁\u001b[39m█\u001b[34m█\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m█\u001b[39m█\u001b[39m▁\u001b[32m▁\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m▁\u001b[39m█\u001b[39m▁\u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m \u001b[39m▁\n",
+ " 304 ms\u001b[90m Histogram: frequency by time\u001b[39m 325 ms \u001b[0m\u001b[1m<\u001b[22m\n",
+ "\n",
+ " Memory estimate\u001b[90m: \u001b[39m\u001b[33m146.80 MiB\u001b[39m, allocs estimate\u001b[90m: \u001b[39m\u001b[33m3002433\u001b[39m."
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "@benchmark data = runCalcFormatted(SharedModule.model_fmu, rand(2))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Single Threaded Batch Execution\n",
+ "To compute a batch we can collect multiple evaluations. In a single threaded context we can use the same FMU for every call."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:22:23.808000Z",
+ "iopub.status.busy": "2022-06-18T14:22:23.807000Z",
+ "iopub.status.idle": "2022-06-18T14:23:01.441000Z",
+ "shell.execute_reply": "2022-06-18T14:23:01.440000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Single Threaded\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "BenchmarkTools.Trial: 2 samples with 1 evaluation.\n",
+ " Range \u001b[90m(\u001b[39m\u001b[36m\u001b[1mmin\u001b[22m\u001b[39m … \u001b[35mmax\u001b[39m\u001b[90m): \u001b[39m\u001b[36m\u001b[1m4.966 s\u001b[22m\u001b[39m … \u001b[35m 4.967 s\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmin … max\u001b[90m): \u001b[39m8.58% … 8.12%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[34m\u001b[1mmedian\u001b[22m\u001b[39m\u001b[90m): \u001b[39m\u001b[34m\u001b[1m4.966 s \u001b[22m\u001b[39m\u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmedian\u001b[90m): \u001b[39m8.35%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[32m\u001b[1mmean\u001b[22m\u001b[39m ± \u001b[32mσ\u001b[39m\u001b[90m): \u001b[39m\u001b[32m\u001b[1m4.966 s\u001b[22m\u001b[39m ± \u001b[32m1.039 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmean ± σ\u001b[90m): \u001b[39m8.35% ± 0.33%\n",
+ "\n",
+ " \u001b[34m█\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[32m \u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m█\u001b[39m \u001b[39m \n",
+ " \u001b[34m█\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[32m▁\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m \u001b[39m▁\n",
+ " 4.97 s\u001b[90m Histogram: frequency by time\u001b[39m 4.97 s \u001b[0m\u001b[1m<\u001b[22m\n",
+ "\n",
+ " Memory estimate\u001b[90m: \u001b[39m\u001b[33m2.29 GiB\u001b[39m, allocs estimate\u001b[90m: \u001b[39m\u001b[33m48038916\u001b[39m."
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "println(\"Single Threaded\")\n",
+ "@benchmark collect(runCalcFormatted(SharedModule.model_fmu, i) for i in input_values)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Multithreaded Batch Execution\n",
+ "In a multithreaded context we have to provide each thread it's own fmu, as they are not thread safe.\n",
+ "To spread the execution of a function to multiple processes, the function `pmap` can be used."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:23:01.444000Z",
+ "iopub.status.busy": "2022-06-18T14:23:01.444000Z",
+ "iopub.status.idle": "2022-06-18T14:24:04.804000Z",
+ "shell.execute_reply": "2022-06-18T14:24:04.804000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Multi Threaded\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "BenchmarkTools.Trial: 2 samples with 1 evaluation.\n",
+ " Range \u001b[90m(\u001b[39m\u001b[36m\u001b[1mmin\u001b[22m\u001b[39m … \u001b[35mmax\u001b[39m\u001b[90m): \u001b[39m\u001b[36m\u001b[1m3.069 s\u001b[22m\u001b[39m … \u001b[35m 3.078 s\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmin … max\u001b[90m): \u001b[39m0.00% … 0.00%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[34m\u001b[1mmedian\u001b[22m\u001b[39m\u001b[90m): \u001b[39m\u001b[34m\u001b[1m3.074 s \u001b[22m\u001b[39m\u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmedian\u001b[90m): \u001b[39m0.00%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[32m\u001b[1mmean\u001b[22m\u001b[39m ± \u001b[32mσ\u001b[39m\u001b[90m): \u001b[39m\u001b[32m\u001b[1m3.074 s\u001b[22m\u001b[39m ± \u001b[32m6.467 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmean ± σ\u001b[90m): \u001b[39m0.00% ± 0.00%\n",
+ "\n",
+ " \u001b[34m█\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[32m \u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m█\u001b[39m \u001b[39m \n",
+ " \u001b[34m█\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[32m▁\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m \u001b[39m▁\n",
+ " 3.07 s\u001b[90m Histogram: frequency by time\u001b[39m 3.08 s \u001b[0m\u001b[1m<\u001b[22m\n",
+ "\n",
+ " Memory estimate\u001b[90m: \u001b[39m\u001b[33m81.31 KiB\u001b[39m, allocs estimate\u001b[90m: \u001b[39m\u001b[33m1192\u001b[39m."
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "println(\"Multi Threaded\")\n",
+ "@benchmark pmap(i -> runCalcFormatted(SharedModule.model_fmu, i), input_values)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you can see, there is a significant speed-up in the median execution time. But: The speed-up is often much smaller than `n_procs` (or the number of physical cores of your CPU), this has different reasons. For a rule of thumb, the speed-up should be around `n/2` on a `n`-core-processor with `n` Julia processes."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unload FMU\n",
+ "\n",
+ "After calculating the data, the FMU is unloaded and all unpacked data on disc is removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:04.807000Z",
+ "iopub.status.busy": "2022-06-18T14:24:04.807000Z",
+ "iopub.status.idle": "2022-06-18T14:24:04.974000Z",
+ "shell.execute_reply": "2022-06-18T14:24:04.974000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "@everywhere fmiUnload(SharedModule.model_fmu)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "In this tutorial it is shown how multi processing with `Distributed.jl` can be used to improve the performance for calculating a Batch of FMUs."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Julia 1.6.6",
+ "language": "julia",
+ "name": "julia-1.6"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/example/multithreading.ipynb b/example/multithreading.ipynb
new file mode 100644
index 00000000..7076dfb7
--- /dev/null
+++ b/example/multithreading.ipynb
@@ -0,0 +1,481 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Multithreading\n",
+ "Tutorial by Jonas Wilfert, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar, Jonas Wilfert\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "This example shows how to parallelize the computation of an FMU in FMI.jl. We can compute a batch of FMU-evaluations in parallel with different initial settings.\n",
+ "Parallelization can be achieved using multithreading or using multiprocessing. This example shows **multithreading**, check `multiprocessing.ipynb` for multiprocessing.\n",
+ "Advantage of multithreading is a lower communication overhead as well as lower RAM usage.\n",
+ "However in some cases multiprocessing can be faster as the garbage collector is not shared.\n",
+ "\n",
+ "\n",
+ "The model used is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.\n",
+ "\n",
+ "![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true) \n",
+ "\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/parallel.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/parallel.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/parallel.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |\n",
+ "| 4. | Install FMICore via | add FMICore | add \" https://github.com/ThummeTo/FMICore.jl \" |\n",
+ "| 5. | Install Folds via | add Folds | |\n",
+ "| 6. | Install BenchmarkTools via | add BenchmarkTools | |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n",
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:15.415000Z",
+ "iopub.status.busy": "2022-06-18T14:24:14.716000Z",
+ "iopub.status.idle": "2022-06-18T14:24:29.648000Z",
+ "shell.execute_reply": "2022-06-18T14:24:29.574000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "using FMI\n",
+ "using FMIZoo\n",
+ "using Folds\n",
+ "using BenchmarkTools"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "First, check the amount of available threads:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:29.989000Z",
+ "iopub.status.busy": "2022-06-18T14:24:29.651000Z",
+ "iopub.status.idle": "2022-06-18T14:24:30.845000Z",
+ "shell.execute_reply": "2022-06-18T14:24:30.845000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "Threads.nthreads()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "If the number of available threads doesn't match your expections, you can increase the number of threads available to the Julia process like described [here](https://docs.julialang.org/en/v1/manual/multi-threading/#Starting-Julia-with-multiple-threads)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the start time and end time of the simulation are set. Here we also decide the size of the batch."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:30.849000Z",
+ "iopub.status.busy": "2022-06-18T14:24:30.849000Z",
+ "iopub.status.idle": "2022-06-18T14:24:34.809000Z",
+ "shell.execute_reply": "2022-06-18T14:24:34.809000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1-element Vector{Vector{Float64}}:\n",
+ " [0.769193342919982, 0.5516183754569333]"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "t_start = 0.0\n",
+ "t_step = 0.1\n",
+ "t_stop = 10.0\n",
+ "tspan = (t_start, t_stop)\n",
+ "tData = collect(t_start:t_step:t_stop)\n",
+ "\n",
+ "# Best if batchSize is a multiple of the threads/cores\n",
+ "batchSize = Threads.nthreads()\n",
+ "\n",
+ "# Define an array of arrays randomly\n",
+ "input_values = collect(collect.(eachrow(rand(batchSize,2))))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We need to instantiate one FMU for each parallel execution, as they cannot be easily shared among different threads."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:34.812000Z",
+ "iopub.status.busy": "2022-06-18T14:24:34.812000Z",
+ "iopub.status.idle": "2022-06-18T14:24:40.193000Z",
+ "shell.execute_reply": "2022-06-18T14:24:40.193000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_guexAq/SpringPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_guexAq/SpringPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:193\n",
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 153 files at `/tmp/fmijl_83Dthm/SpringPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_83Dthm/SpringPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/DJ6oi/src/FMI2_ext.jl:193\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "1-element Vector{FMU2}:\n",
+ " Model name: SpringPendulum1D\n",
+ "Type: 1"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# a single FMU to compare the performance\n",
+ "realFMU = fmiLoad(\"SpringPendulum1D\", \"Dymola\", \"2022x\")\n",
+ "\n",
+ "# the FMU batch\n",
+ "realFMUBatch = [fmiLoad(\"SpringPendulum1D\", \"Dymola\", \"2022x\") for _ in 1:batchSize]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We define a helper function to calculate the FMU solution and combine it into an Matrix."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:40.198000Z",
+ "iopub.status.busy": "2022-06-18T14:24:40.197000Z",
+ "iopub.status.idle": "2022-06-18T14:24:40.840000Z",
+ "shell.execute_reply": "2022-06-18T14:24:40.839000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "runCalcFormatted (generic function with 2 methods)"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "function runCalcFormatted(fmu::FMU2, x0::Vector{Float64}, recordValues::Vector{String}=[\"mass.s\", \"mass.v\"])\n",
+ " data = fmiSimulateME(fmu, t_start, t_stop; recordValues=recordValues, saveat=tData, x0=x0, showProgress=false, dtmax=1e-4)\n",
+ " return reduce(hcat, data.states.u)\n",
+ "end"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Running a single evaluation is pretty quick, therefore the speed can be better tested with BenchmarkTools."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:24:40.843000Z",
+ "iopub.status.busy": "2022-06-18T14:24:40.843000Z",
+ "iopub.status.idle": "2022-06-18T14:25:15.703000Z",
+ "shell.execute_reply": "2022-06-18T14:25:15.703000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "BenchmarkTools.Trial: 15 samples with 1 evaluation.\n",
+ " Range \u001b[90m(\u001b[39m\u001b[36m\u001b[1mmin\u001b[22m\u001b[39m … \u001b[35mmax\u001b[39m\u001b[90m): \u001b[39m\u001b[36m\u001b[1m337.943 ms\u001b[22m\u001b[39m … \u001b[35m357.340 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmin … max\u001b[90m): \u001b[39m7.03% … 6.55%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[34m\u001b[1mmedian\u001b[22m\u001b[39m\u001b[90m): \u001b[39m\u001b[34m\u001b[1m339.287 ms \u001b[22m\u001b[39m\u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmedian\u001b[90m): \u001b[39m7.00%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[32m\u001b[1mmean\u001b[22m\u001b[39m ± \u001b[32mσ\u001b[39m\u001b[90m): \u001b[39m\u001b[32m\u001b[1m342.299 ms\u001b[22m\u001b[39m ± \u001b[32m 5.982 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmean ± σ\u001b[90m): \u001b[39m7.39% ± 1.12%\n",
+ "\n",
+ " \u001b[39m▃\u001b[39m█\u001b[39m \u001b[34m \u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[32m \u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \n",
+ " \u001b[39m█\u001b[39m█\u001b[39m▇\u001b[34m▇\u001b[39m\u001b[39m▇\u001b[39m▇\u001b[39m▁\u001b[39m▁\u001b[39m▇\u001b[39m▁\u001b[39m▇\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[32m▁\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▇\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▇\u001b[39m▇\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▇\u001b[39m \u001b[39m▁\n",
+ " 338 ms\u001b[90m Histogram: frequency by time\u001b[39m 357 ms \u001b[0m\u001b[1m<\u001b[22m\n",
+ "\n",
+ " Memory estimate\u001b[90m: \u001b[39m\u001b[33m146.80 MiB\u001b[39m, allocs estimate\u001b[90m: \u001b[39m\u001b[33m3002433\u001b[39m."
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "@benchmark data = runCalcFormatted(realFMU, rand(2))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Single Threaded Batch Execution\n",
+ "To compute a batch we can collect multiple evaluations. In a single threaded context we can use the same FMU for every call."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:25:15.707000Z",
+ "iopub.status.busy": "2022-06-18T14:25:15.707000Z",
+ "iopub.status.idle": "2022-06-18T14:25:29.068000Z",
+ "shell.execute_reply": "2022-06-18T14:25:29.068000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Single Threaded\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "BenchmarkTools.Trial: 15 samples with 1 evaluation.\n",
+ " Range \u001b[90m(\u001b[39m\u001b[36m\u001b[1mmin\u001b[22m\u001b[39m … \u001b[35mmax\u001b[39m\u001b[90m): \u001b[39m\u001b[36m\u001b[1m338.941 ms\u001b[22m\u001b[39m … \u001b[35m359.792 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmin … max\u001b[90m): \u001b[39m7.42% … 7.05%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[34m\u001b[1mmedian\u001b[22m\u001b[39m\u001b[90m): \u001b[39m\u001b[34m\u001b[1m340.880 ms \u001b[22m\u001b[39m\u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmedian\u001b[90m): \u001b[39m7.45%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[32m\u001b[1mmean\u001b[22m\u001b[39m ± \u001b[32mσ\u001b[39m\u001b[90m): \u001b[39m\u001b[32m\u001b[1m343.682 ms\u001b[22m\u001b[39m ± \u001b[32m 6.291 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmean ± σ\u001b[90m): \u001b[39m7.89% ± 1.20%\n",
+ "\n",
+ " \u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[34m█\u001b[39m\u001b[39m \u001b[39m \u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m \u001b[39m \u001b[39m \u001b[32m \u001b[39m\u001b[39m▁\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m▁\u001b[39m▁\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m▁\u001b[39m \u001b[39m \n",
+ " \u001b[39m█\u001b[39m█\u001b[39m█\u001b[39m█\u001b[39m█\u001b[34m█\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m█\u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[32m▁\u001b[39m\u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m█\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m█\u001b[39m \u001b[39m▁\n",
+ " 339 ms\u001b[90m Histogram: frequency by time\u001b[39m 360 ms \u001b[0m\u001b[1m<\u001b[22m\n",
+ "\n",
+ " Memory estimate\u001b[90m: \u001b[39m\u001b[33m146.80 MiB\u001b[39m, allocs estimate\u001b[90m: \u001b[39m\u001b[33m3002436\u001b[39m."
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "println(\"Single Threaded\")\n",
+ "@benchmark collect(runCalcFormatted(realFMU, i) for i in input_values)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Multithreaded Batch Execution\n",
+ "In a multithreaded context we have to provide each thread it's own fmu, as they are not thread safe.\n",
+ "To spread the execution of a function to multiple threads, the library `Folds` can be used."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:25:29.072000Z",
+ "iopub.status.busy": "2022-06-18T14:25:29.072000Z",
+ "iopub.status.idle": "2022-06-18T14:25:46.159000Z",
+ "shell.execute_reply": "2022-06-18T14:25:46.159000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Multi Threaded\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "BenchmarkTools.Trial: 17 samples with 1 evaluation.\n",
+ " Range \u001b[90m(\u001b[39m\u001b[36m\u001b[1mmin\u001b[22m\u001b[39m … \u001b[35mmax\u001b[39m\u001b[90m): \u001b[39m\u001b[36m\u001b[1m307.665 ms\u001b[22m\u001b[39m … \u001b[35m325.644 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmin … max\u001b[90m): \u001b[39m8.44% … 8.13%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[34m\u001b[1mmedian\u001b[22m\u001b[39m\u001b[90m): \u001b[39m\u001b[34m\u001b[1m308.448 ms \u001b[22m\u001b[39m\u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmedian\u001b[90m): \u001b[39m8.55%\n",
+ " Time \u001b[90m(\u001b[39m\u001b[32m\u001b[1mmean\u001b[22m\u001b[39m ± \u001b[32mσ\u001b[39m\u001b[90m): \u001b[39m\u001b[32m\u001b[1m310.319 ms\u001b[22m\u001b[39m ± \u001b[32m 4.970 ms\u001b[39m \u001b[90m┊\u001b[39m GC \u001b[90m(\u001b[39mmean ± σ\u001b[90m): \u001b[39m8.72% ± 0.86%\n",
+ "\n",
+ " \u001b[39m█\u001b[39m \u001b[34m█\u001b[39m\u001b[39m \u001b[39m▁\u001b[39m \u001b[39m \u001b[39m \u001b[39m▁\u001b[32m \u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \n",
+ " \u001b[39m█\u001b[39m▆\u001b[34m█\u001b[39m\u001b[39m▆\u001b[39m█\u001b[39m▁\u001b[39m▆\u001b[39m▁\u001b[39m█\u001b[32m▁\u001b[39m\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▆\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▁\u001b[39m▆\u001b[39m \u001b[39m▁\n",
+ " 308 ms\u001b[90m Histogram: frequency by time\u001b[39m 326 ms \u001b[0m\u001b[1m<\u001b[22m\n",
+ "\n",
+ " Memory estimate\u001b[90m: \u001b[39m\u001b[33m146.80 MiB\u001b[39m, allocs estimate\u001b[90m: \u001b[39m\u001b[33m3002440\u001b[39m."
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "println(\"Multi Threaded\")\n",
+ "@benchmark Folds.collect(runCalcFormatted(fmu, i) for (fmu, i) in zip(realFMUBatch, input_values))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As you can see, there is a significant speed-up in the median execution time. But: The speed-up is often much smaller than `Threads.nthreads()`, this has different reasons. For a rule of thumb, the speed-up should be around `n/2` on a `n`-core-processor with `n` threads for the Julia process."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unload FMU\n",
+ "\n",
+ "After calculating the data, the FMU is unloaded and all unpacked data on disc is removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-18T14:25:46.162000Z",
+ "iopub.status.busy": "2022-06-18T14:25:46.162000Z",
+ "iopub.status.idle": "2022-06-18T14:25:47.297000Z",
+ "shell.execute_reply": "2022-06-18T14:25:47.296000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1-element Vector{Nothing}:\n",
+ " nothing"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiUnload(realFMU)\n",
+ "fmiUnload.(realFMUBatch)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "In this tutorial it is shown how multi threading with `Folds.jl` can be used to improve the performance for calculating a Batch of FMUs."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Julia 1.7.1",
+ "language": "julia",
+ "name": "julia-1.7"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/example/parameterize.ipynb b/example/parameterize.ipynb
new file mode 100644
index 00000000..65099b87
--- /dev/null
+++ b/example/parameterize.ipynb
@@ -0,0 +1,659 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Manually parameterize an FMU\n",
+ "Tutorial by Johannes Stoljar, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "This example shows how the manually parameterization of an FMU works if very specific adjustments during system initialization is needed. For this purpose, an IO-FMU model is loaded and the various commands for parameterization are shown on the basis of this model. With this example the user shall be guided how to make certain settings at an FMU. Please note, that parameterization of a simulation is possible in a much easier fashion: Using `fmiSimulate`, `fmiSimulateME` or `fmiSimulateCS` together with a parameter dictionary for the keyword `parameters`.\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulation exchange. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/CS_simulate.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/CS_simulate.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/CS_simulate.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n",
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:01.571000Z",
+ "iopub.status.busy": "2022-06-01T15:19:00.892000Z",
+ "iopub.status.idle": "2022-06-01T15:19:15.010000Z",
+ "shell.execute_reply": "2022-06-01T15:19:14.933000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "using FMI\n",
+ "using FMIZoo"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the start time and end time of the simulation are set."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:15.205000Z",
+ "iopub.status.busy": "2022-06-01T15:19:15.013000Z",
+ "iopub.status.idle": "2022-06-01T15:19:16.256000Z",
+ "shell.execute_reply": "2022-06-01T15:19:16.255000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "8.0"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "tStart = 0.0\n",
+ "tStop = 8.0"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Import FMU\n",
+ "\n",
+ "In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:16.259000Z",
+ "iopub.status.busy": "2022-06-01T15:19:16.259000Z",
+ "iopub.status.idle": "2022-06-01T15:19:20.487000Z",
+ "shell.execute_reply": "2022-06-01T15:19:20.486000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_sXqX3p/IO`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_sXqX3p/IO/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "#################### Begin information for FMU ####################\n",
+ "\tModel name:\t\t\tIO\n",
+ "\tFMI-Version:\t\t\t2.0\n",
+ "\tGUID:\t\t\t\t{ac3b4a99-4908-40f7-89da-2d5c08b3c4ac}\n",
+ "\tGeneration tool:\t\tDymola Version 2022x (64-bit), 2021-10-08\n",
+ "\tGeneration time:\t\t2022-03-17T07:40:55Z\n",
+ "\tVar. naming conv.:\t\tstructured\n",
+ "\tEvent indicators:\t\t4\n",
+ "\tInputs:\t\t\t\t3\n",
+ "\t\t352321536 [\"u_real\"]\n",
+ "\t\t352321537 [\"u_boolean\"]\n",
+ "\t\t352321538 [\"u_integer\"]\n",
+ "\tOutputs:\t\t\t3\n",
+ "\t\t335544320 [\"y_real\"]\n",
+ "\t\t335544321 [\"y_boolean\"]\n",
+ "\t\t335544322 [\"y_integer\"]\n",
+ "\tStates:\t\t\t\t0\n",
+ "\tSupports Co-Simulation:\t\ttrue\n",
+ "\t\tModel identifier:\tIO\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "\t\tVar. com. steps:\ttrue\n",
+ "\t\tInput interpol.:\ttrue\n",
+ "\t\tMax order out. der.:\t1\n",
+ "\tSupports Model-Exchange:\ttrue\n",
+ "\t\tModel identifier:\tIO\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "##################### End information for FMU #####################\n"
+ ]
+ }
+ ],
+ "source": [
+ "# we use an FMU from the FMIZoo.jl\n",
+ "pathToFMU = get_model_filename(\"IO\", \"Dymola\", \"2022x\")\n",
+ "\n",
+ "myFMU = fmiLoad(pathToFMU)\n",
+ "fmiInfo(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Instantiate and Setup FMU\n",
+ "\n",
+ "Next it is necessary to create an instance of the FMU. This is achieved by the command `fmiInstantiate!()`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:20.489000Z",
+ "iopub.status.busy": "2022-06-01T15:19:20.489000Z",
+ "iopub.status.idle": "2022-06-01T15:19:21.252000Z",
+ "shell.execute_reply": "2022-06-01T15:19:21.252000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "FMU: IO\n",
+ "InstanceName: [not defined]\n",
+ "Address: Ptr{Nothing} @0x00000000029c12e0\n",
+ "State: fmi2ComponentStateInstantiated\n",
+ "Logging: true\n",
+ "FMU time: -Inf\n",
+ "FMU states: nothing"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiInstantiate!(myFMU; loggingOn=true)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the following code block, start and end time for the simulation is set by the `fmiSetupExperiment()` command."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:21.256000Z",
+ "iopub.status.busy": "2022-06-01T15:19:21.256000Z",
+ "iopub.status.idle": "2022-06-01T15:19:21.603000Z",
+ "shell.execute_reply": "2022-06-01T15:19:21.603000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0x00000000"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiSetupExperiment(myFMU, tStart, tStop)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Parameterize FMU\n",
+ "\n",
+ "To parameterize an FMU, the FMU must be in the initialization mode, which is reached with the `fmiEnterInitializationMode()` command."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:21.607000Z",
+ "iopub.status.busy": "2022-06-01T15:19:21.606000Z",
+ "iopub.status.idle": "2022-06-01T15:19:21.639000Z",
+ "shell.execute_reply": "2022-06-01T15:19:21.639000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0x00000000"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiEnterInitializationMode(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Within this mode it is then possible to change the different parameters. In this example, for each data type (`real`, `integer`, `boolean` and `string)` a corresponding parameter is selected. At the beginning the initial state of these parameters is displayed with `fmiGet()`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:21.643000Z",
+ "iopub.status.busy": "2022-06-01T15:19:21.643000Z",
+ "iopub.status.idle": "2022-06-01T15:19:25.228000Z",
+ "shell.execute_reply": "2022-06-01T15:19:25.228000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "4-element Vector{Any}:\n",
+ " 0.0\n",
+ " 0\n",
+ " 0\n",
+ " \"Hello World!\""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "params = [\"p_real\", \"p_integer\", \"p_boolean\", \"p_string\"]\n",
+ "fmiGet(myFMU, params)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the next step, a function is defined that generates a random value for each parameter. For the parameter `p_string` a random number is inserted into the string. All parameters are combined to a vector and output."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:25.232000Z",
+ "iopub.status.busy": "2022-06-01T15:19:25.232000Z",
+ "iopub.status.idle": "2022-06-01T15:19:25.951000Z",
+ "shell.execute_reply": "2022-06-01T15:19:25.951000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "generateRandomNumbers (generic function with 1 method)"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "function generateRandomNumbers()\n",
+ " rndReal = 100 * rand()\n",
+ " rndInteger = round(Integer, 100 * rand())\n",
+ " rndBoolean = rand() > 0.5\n",
+ " rndString = \"Random number $(100 * rand())!\"\n",
+ "\n",
+ " randValues = [rndReal, rndInteger, rndBoolean, rndString]\n",
+ " println(randValues)\n",
+ " return randValues\n",
+ "end"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The previously defined function is called and the results are displayed in the console."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:25.955000Z",
+ "iopub.status.busy": "2022-06-01T15:19:25.955000Z",
+ "iopub.status.idle": "2022-06-01T15:19:26.678000Z",
+ "shell.execute_reply": "2022-06-01T15:19:26.678000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Any[27.27027116078753, 50, true, \"Random number 83.09640957659121!\"]\n"
+ ]
+ }
+ ],
+ "source": [
+ "paramsVal = generateRandomNumbers();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### First variant\n",
+ "\n",
+ "With this variant it is quickly possible to set all parameters at once. Even different data types can be set with only one command. The command `fmiSet()` selects itself which function is chosen for which data type. After setting the parameters, it is checked whether the corresponding parameters were set correctly. For this the function `fmiGet()` is used as above and afterwards with the macro `@assert` also tested whether the correct values are set."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:26.682000Z",
+ "iopub.status.busy": "2022-06-01T15:19:26.682000Z",
+ "iopub.status.idle": "2022-06-01T15:19:27.242000Z",
+ "shell.execute_reply": "2022-06-01T15:19:27.242000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Any[27.27027116078753, 50, 1, \"Random number 83.09640957659121!\"]"
+ ]
+ }
+ ],
+ "source": [
+ "fmiSet(myFMU, params, paramsVal)\n",
+ "values = fmiGet(myFMU, params)\n",
+ "print(values)\n",
+ "\n",
+ "@assert paramsVal == values"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Second variant\n",
+ "\n",
+ "To make sure that the functions work it is necessary to generate random numbers again. As shown already, we call the defined function `generateRandomNumbers()` and output the values."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:27.246000Z",
+ "iopub.status.busy": "2022-06-01T15:19:27.246000Z",
+ "iopub.status.idle": "2022-06-01T15:19:27.247000Z",
+ "shell.execute_reply": "2022-06-01T15:19:27.247000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Any[54.98169316988619, 76, true, \"Random number 39.54384075977397!\"]\n"
+ ]
+ }
+ ],
+ "source": [
+ "rndReal, rndInteger, rndBoolean, rndString = generateRandomNumbers();"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In the second variant, the value for each data type is set separately by the corresponding command. By this variant one has the maximum control and can be sure that also the correct data type is set. To illustrate the functionality of the parameterization with the separate functions, the corresponding get function is also called separately for each data type:\n",
+ "* `fmiSetReal()` <---> `fmiGetReal()`\n",
+ "* `fmiSetInteger()` <---> `fmiGetInteger()`\n",
+ "* `fmiSetBoolean()` <---> `fmiGetBoolean()`\n",
+ "* `fmiSetString()` <---> `fmiGetString()`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:27.250000Z",
+ "iopub.status.busy": "2022-06-01T15:19:27.250000Z",
+ "iopub.status.idle": "2022-06-01T15:19:28.917000Z",
+ "shell.execute_reply": "2022-06-01T15:19:28.917000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "\"54.98169316988619 == 54.98169316988619\""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "\"76 == 76\""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "\"true == 1\""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "\"Random number 39.54384075977397! == Random number 39.54384075977397!\""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "fmiSetReal(myFMU, \"p_real\", rndReal)\n",
+ "display(\"$rndReal == $(fmiGetReal(myFMU, \"p_real\"))\")\n",
+ "\n",
+ "fmiSetInteger(myFMU, \"p_integer\", rndInteger)\n",
+ "display(\"$rndInteger == $(fmiGetInteger(myFMU, \"p_integer\"))\")\n",
+ "\n",
+ "fmiSetBoolean(myFMU, \"p_boolean\", rndBoolean)\n",
+ "display(\"$rndBoolean == $(fmiGetBoolean(myFMU, \"p_boolean\"))\")\n",
+ "\n",
+ "fmiSetString(myFMU, \"p_string\", rndString)\n",
+ "display(\"$rndString == $(fmiGetString(myFMU, \"p_string\"))\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "After seeing that both variants set the parameters correctly, the initialization mode is terminated with the function `fmiExitInitializationMode()`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:28.920000Z",
+ "iopub.status.busy": "2022-06-01T15:19:28.920000Z",
+ "iopub.status.idle": "2022-06-01T15:19:28.956000Z",
+ "shell.execute_reply": "2022-06-01T15:19:28.955000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0x00000000"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiExitInitializationMode(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From here on, you may want to simulate the FMU. Please note, that with the default `executionConfig`, it is necessary to prevent a new instantiation using the keyword `instantiate=false`. Otherwise, a new instance is allocated for the simulation-call and the parameters set for the previous instance are not transfered.\n",
+ "\n",
+ "Example:\n",
+ "`fmiSimulate(...; instantiate=false, ...)`"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unload FMU\n",
+ "\n",
+ "The FMU will be unloaded and all unpacked data on disc will be removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:28.959000Z",
+ "iopub.status.busy": "2022-06-01T15:19:28.959000Z",
+ "iopub.status.idle": "2022-06-01T15:19:29.101000Z",
+ "shell.execute_reply": "2022-06-01T15:19:29.101000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "fmiUnload(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "Based on this tutorial it can be seen that there are two different variants to set and get parameters.These examples should make it clear to the user how parameters can also be set with different data types."
+ ]
+ }
+ ],
+ "metadata": {
+ "interpreter": {
+ "hash": "037537ff7419c497b9325f7d495147943224d408cf5d5ed915294a5b960167b0"
+ },
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "comment_magics": "false",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "Julia 1.6.6",
+ "language": "julia",
+ "name": "julia-1.6"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ },
+ "nteract": {
+ "version": "0.28.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/example/simulate.ipynb b/example/simulate.ipynb
new file mode 100644
index 00000000..f3ef7b84
--- /dev/null
+++ b/example/simulate.ipynb
@@ -0,0 +1,1012 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Simulate an FMU in different modes\n",
+ "Tutorial by Johannes Stoljar, Tobias Thummerer\n",
+ "\n",
+ "## License\n",
+ "Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n",
+ "\n",
+ "Licensed under the MIT license. See [LICENSE](https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details.\n",
+ "\n",
+ "## Motivation\n",
+ "This Julia Package *FMI.jl* is motivated by the use of simulation models in Julia. Here the FMI specification is implemented. FMI (*Functional Mock-up Interface*) is a free standard ([fmi-standard.org](http://fmi-standard.org/)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user can thus use simulation models in the form of an FMU (*Functional Mock-up Units*). Besides loading the FMU, the user can also set values for parameters and states and simulate the FMU both as co-simulation and model exchange simulation.\n",
+ "\n",
+ "## Introduction to the example\n",
+ "In this example we want to show how fast and easy the simulation for an FMU is. For this purpose, the FMU is simulated in co-simulation mode and in model-exchange mode. After the FMU has been simulated, the simulation results are displayed in a graph. The graphs of the different modes are compared with each other. The used model is a one-dimensional spring pendulum with friction. The object-orientated structure of the *SpringFrictionPendulum1D* can be seen in the following graphic.\n",
+ "\n",
+ "![svg](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/pics/SpringFrictionPendulum1D.svg?raw=true) \n",
+ "\n",
+ "\n",
+ "## Target group\n",
+ "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to use FMUs in Julia.\n",
+ "\n",
+ "\n",
+ "## Other formats\n",
+ "Besides, this [Jupyter Notebook](https://github.com/thummeto/FMI.jl/blob/main/example/simulate.ipynb) there is also a [Julia file](https://github.com/thummeto/FMI.jl/blob/main/example/simulate.jl) with the same name, which contains only the code cells and for the documentation there is a [Markdown file](https://github.com/thummeto/FMI.jl/blob/main/docs/src/examples/simulate.md) corresponding to the notebook. \n",
+ "\n",
+ "\n",
+ "## Getting started\n",
+ "\n",
+ "### Installation prerequisites\n",
+ "| | Description | Command | Alternative | \n",
+ "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n",
+ "| 1. | Enter Package Manager via | ] | |\n",
+ "| 2. | Install FMI via | add FMI | add \" https://github.com/ThummeTo/FMI.jl \" |\n",
+ "| 3. | Install FMIZoo via | add FMIZoo | add \" https://github.com/ThummeTo/FMIZoo.jl \" |\n",
+ "| 4. | Install Plots via | add Plots | |"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Code section\n",
+ "\n",
+ "To run the example, the previously installed packages must be included. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:19:38.296000Z",
+ "iopub.status.busy": "2022-06-01T15:19:37.619000Z",
+ "iopub.status.idle": "2022-06-01T15:20:00.542000Z",
+ "shell.execute_reply": "2022-06-01T15:20:00.469000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [],
+ "source": [
+ "# imports\n",
+ "using FMI\n",
+ "using FMIZoo\n",
+ "using Plots"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulation setup\n",
+ "\n",
+ "Next, the start time and end time of the simulation are set. Finally, a step size is specified to store the results of the simulation at these time steps."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:09.276000Z",
+ "iopub.status.busy": "2022-06-01T15:20:00.544000Z",
+ "iopub.status.idle": "2022-06-01T15:20:10.061000Z",
+ "shell.execute_reply": "2022-06-01T15:20:10.061000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.0:0.01:8.0"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "tStart = 0.0\n",
+ "tStep = 0.01\n",
+ "tStop = 8.0\n",
+ "tSave = tStart:tStep:tStop"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Import FMU\n",
+ "\n",
+ "In the next lines of code the FMU model from *FMIZoo.jl* is loaded and the information about the FMU is shown."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:10.065000Z",
+ "iopub.status.busy": "2022-06-01T15:20:10.065000Z",
+ "iopub.status.idle": "2022-06-01T15:20:14.405000Z",
+ "shell.execute_reply": "2022-06-01T15:20:14.404000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "┌ Info: fmi2Unzip(...): Successfully unzipped 29 files at `/tmp/fmijl_OQnfcn/SpringFrictionPendulum1D`.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:75\n",
+ "┌ Info: fmi2Load(...): FMU resources location is `file:////tmp/fmijl_OQnfcn/SpringFrictionPendulum1D/resources`\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:190\n",
+ "┌ Info: fmi2Load(...): FMU supports both CS and ME, using CS as default if nothing specified.\n",
+ "└ @ FMIImport /home/runner/.julia/packages/FMIImport/ns5Pd/src/FMI2_ext.jl:193\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "#################### Begin information for FMU ####################\n",
+ "\tModel name:\t\t\tSpringFrictionPendulum1D\n",
+ "\tFMI-Version:\t\t\t2.0\n",
+ "\tGUID:\t\t\t\t{df491d8d-0598-4495-913e-5b025e54d7f2}\n",
+ "\tGeneration tool:\t\tDymola Version 2022x (64-bit), 2021-10-08\n",
+ "\tGeneration time:\t\t2022-03-03T15:09:18Z\n",
+ "\tVar. naming conv.:\t\tstructured\n",
+ "\tEvent indicators:\t\t24\n",
+ "\tInputs:\t\t\t\t0\n",
+ "\tOutputs:\t\t\t0\n",
+ "\tStates:\t\t\t\t2\n",
+ "\t\t33554432 [\"mass.s\"]\n",
+ "\t\t33554433 [\"mass.v\", \"mass.v_relfric\"]\n",
+ "\tSupports Co-Simulation:\t\ttrue\n",
+ "\t\tModel identifier:\tSpringFrictionPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "\t\tVar. com. steps:\ttrue\n",
+ "\t\tInput interpol.:\ttrue\n",
+ "\t\tMax order out. der.:\t1\n",
+ "\tSupports Model-Exchange:\ttrue\n",
+ "\t\tModel identifier:\tSpringFrictionPendulum1D\n",
+ "\t\tGet/Set State:\t\ttrue\n",
+ "\t\tSerialize State:\ttrue\n",
+ "\t\tDir. Derivatives:\ttrue\n",
+ "##################### End information for FMU #####################\n"
+ ]
+ }
+ ],
+ "source": [
+ "# we use an FMU from the FMIZoo.jl\n",
+ "pathToFMU = get_model_filename(\"SpringFrictionPendulum1D\", \"Dymola\", \"2022x\")\n",
+ "\n",
+ "myFMU = fmiLoad(pathToFMU)\n",
+ "# fmiLoad(\"path/to/myFMU.fmu\"; unpackPath = \"path/to/unpacked/fmu/\")\n",
+ "\n",
+ "fmiInfo(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Simulate FMU\n",
+ "\n",
+ "In the following, the FMU is simulated in the two different simulation modes.\n",
+ "\n",
+ "#### Simulate as Co-Simulation\n",
+ "\n",
+ "In the next steps the recorded values are defined. The first state is the position of the mass and the second state is the velocity. In the function `fmiSimulateCS()` the FMU is simulated in co-simulation mode (CS) with an adaptive step size but with fixed save points `tSave`. In addition, the start and end time and the recorded variables are specified."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:14.407000Z",
+ "iopub.status.busy": "2022-06-01T15:20:14.407000Z",
+ "iopub.status.idle": "2022-06-01T15:20:19.739000Z",
+ "shell.execute_reply": "2022-06-01T15:20:19.739000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Model name:\n",
+ "\tSpringFrictionPendulum1D\n",
+ "Success:\n",
+ "\ttrue\n",
+ "Values [801]:\n",
+ "\t0.0\t(0.5, 0.0)\n",
+ "\t0.01\t(0.5002235448486548, 0.042692491939260585)\n",
+ "\t0.02\t(0.5008715291319449, 0.08568000508550636)\n",
+ "\t0.03\t(0.5019478597521578, 0.12892136998736314)\n",
+ "\t0.04\t(0.5034570452098334, 0.17232325681284336)\n",
+ "\t0.05\t(0.5053993458877354, 0.2158440857658765)\n",
+ "\t0.06\t(0.5077764240578201, 0.259420181133082)\n",
+ "\t0.07\t(0.5105886522837868, 0.30295578207463486)\n",
+ "\t0.08\t(0.5138351439717114, 0.3464184707972189)\n",
+ "\t...\n",
+ "\t8.0\t(1.071367253976742, -1.000814138594347e-10)\n",
+ "Events [0]:\n"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "vrs = [\"mass.s\", \"mass.v\"]\n",
+ "\n",
+ "dataCS = fmiSimulateCS(myFMU, tStart, tStop; recordValues=vrs, saveat=tSave)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Simulate as Model-Exchange\n",
+ "\n",
+ "In the function `fmiSimulateME()` the FMU is simulated in model-exchange mode (ME) with an adaptive step size but with fixed save points `tSave`. In addition, the start and end time are specified. In contrast to the co-simulation, the values to be stored are not specified here, since the states and events of the FMU are always output as well. The identifiers given above just correspond to the states of the FMU."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:19.742000Z",
+ "iopub.status.busy": "2022-06-01T15:20:19.742000Z",
+ "iopub.status.idle": "2022-06-01T15:20:32.777000Z",
+ "shell.execute_reply": "2022-06-01T15:20:32.777000Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\u001b[34mSimulating ME-FMU ... 100%|██████████████████████████████| Time: 0:00:09\u001b[39m\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "Model name:\n",
+ "\tSpringFrictionPendulum1D\n",
+ "Success:\n",
+ "\ttrue\n",
+ "States [801]:\n",
+ "\t0.0\t[0.5, 0.0]\n",
+ "\t0.01\t[0.5002131418344649, 0.042689450666241]\n",
+ "\t0.02\t[0.5008548874805565, 0.08570846215523381]\n",
+ "\t0.03\t[0.5019281653120716, 0.12898389312495082]\n",
+ "\t0.04\t[0.5034351805057593, 0.17244393475170294]\n",
+ "\t0.05\t[0.5053774287967188, 0.2160182432854046]\n",
+ "\t0.06\t[0.5077556967622916, 0.25963796675862466]\n",
+ "\t0.07\t[0.5105701003205937, 0.3032358690888718]\n",
+ "\t0.08\t[0.5138201049458624, 0.34674641820324037]\n",
+ "\t...\n",
+ "\t8.0\t[1.0668213438183276, -1.0000099359121942e-10]\n",
+ "Events [6]:\n",
+ "\tState-Event #11 @ 0.0s\n",
+ "\tState-Event #11 @ 0.9939s\n",
+ "\tState-Event #19 @ 1.9881s\n",
+ "\tState-Event #11 @ 2.9829s\n",
+ "\tState-Event #19 @ 3.9787s\n",
+ "\tState-Event #11 @ 4.9768s\n"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dataME = fmiSimulateME(myFMU, tStart, tStop; saveat=tSave)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Plotting FMU\n",
+ "\n",
+ "After the simulation is finished the results of the FMU for the co-simulation and model-exchange mode can be plotted. In the plot for the FMU it can be seen that the oscillation continues to decrease due to the effect of the friction. If you simulate long enough, the oscillation comes to a standstill in a certain time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:32.781000Z",
+ "iopub.status.busy": "2022-06-01T15:20:32.780000Z",
+ "iopub.status.idle": "2022-06-01T15:20:51.657000Z",
+ "shell.execute_reply": "2022-06-01T15:20:51.657000Z"
+ },
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiPlot(dataCS)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:51.662000Z",
+ "iopub.status.busy": "2022-06-01T15:20:51.662000Z",
+ "iopub.status.idle": "2022-06-01T15:20:51.876000Z",
+ "shell.execute_reply": "2022-06-01T15:20:51.876000Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/svg+xml": [
+ "\n",
+ "\n"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "fmiPlot(dataME)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "From both graphs it can be seen that the simulation calculates exactly the same results."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Unload FMU\n",
+ "\n",
+ "After plotting the data, the FMU is unloaded and all unpacked data on disc is removed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "execution": {
+ "iopub.execute_input": "2022-06-01T15:20:51.880000Z",
+ "iopub.status.busy": "2022-06-01T15:20:51.880000Z",
+ "iopub.status.idle": "2022-06-01T15:20:51.922000Z",
+ "shell.execute_reply": "2022-06-01T15:20:51.922000Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "fmiUnload(myFMU)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Summary\n",
+ "\n",
+ "Based on this tutorial it can be seen that simulating in the different mode is very easy, and it only takes a few commands to simulate the FMU. "
+ ]
+ }
+ ],
+ "metadata": {
+ "interpreter": {
+ "hash": "037537ff7419c497b9325f7d495147943224d408cf5d5ed915294a5b960167b0"
+ },
+ "jupytext": {
+ "cell_metadata_filter": "-all",
+ "comment_magics": "false",
+ "notebook_metadata_filter": "-all"
+ },
+ "kernelspec": {
+ "display_name": "Julia 1.6.6",
+ "language": "julia",
+ "name": "julia-1.6"
+ },
+ "language_info": {
+ "file_extension": ".jl",
+ "mimetype": "application/julia",
+ "name": "julia",
+ "version": "1.6.6"
+ },
+ "nteract": {
+ "version": "0.28.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/src/FMI.jl b/src/FMI.jl
index 8b4615b1..7b73aa7c 100644
--- a/src/FMI.jl
+++ b/src/FMI.jl
@@ -10,6 +10,8 @@ module FMI
using Requires
using FMIImport
+
+# fmi2 imports
import FMIImport: fmi2CallbackLogger, fmi2CallbackAllocateMemory, fmi2CallbackFreeMemory, fmi2CallbackStepFinished
import FMIImport: fmi2ComponentState, fmi2ComponentStateInstantiated, fmi2ComponentStateInitializationMode, fmi2ComponentStateEventMode, fmi2ComponentStateContinuousTimeMode, fmi2ComponentStateTerminated, fmi2ComponentStateError, fmi2ComponentStateFatal
import FMIImport: fmi2Instantiate, fmi2FreeInstance!, fmi2GetTypesPlatform, fmi2GetVersion
@@ -36,11 +38,54 @@ import FMIImport: fmi2Get, fmi2Get!, fmi2Set
import FMIImport: fmi2GetSolutionTime, fmi2GetSolutionState, fmi2GetSolutionValue
export fmi2GetSolutionTime, fmi2GetSolutionState, fmi2GetSolutionValue
+# fmi3 imports
+import FMIImport: fmi3CallbackLogger, fmi3CallbackIntermediateUpdate, fmi3CallbackClockUpdate
+import FMIImport: fmi3InstanceState, fmi3InstanceStateInstantiated, fmi3InstanceStateInitializationMode, fmi3InstanceStateEventMode, fmi3InstanceStateContinuousTimeMode, fmi3InstanceStateTerminated, fmi3InstanceStateError, fmi3InstanceStateFatal
+import FMIImport: fmi3InstantiateModelExchange!, fmi3InstantiateCoSimulation!, fmi3InstantiateScheduledExecution!, fmi3FreeInstance!, fmi3GetVersion
+import FMIImport: fmi3SetDebugLogging, fmi3EnterInitializationMode, fmi3ExitInitializationMode, fmi3Terminate, fmi3Reset
+import FMIImport: fmi3GetFloat32!, fmi3SetFloat32, fmi3GetFloat64!, fmi3SetFloat64
+import FMIImport: fmi3GetInt8!, fmi3SetInt8, fmi3GetUInt8!, fmi3SetUInt8, fmi3GetInt16!, fmi3SetInt16, fmi3GetUInt16!, fmi3SetUInt16, fmi3GetInt32!, fmi3SetInt32, fmi3GetUInt32!, fmi3SetUInt32, fmi3GetInt64!, fmi3SetInt64, fmi3GetUInt64!, fmi3SetUInt64
+import FMIImport: fmi3GetBoolean!, fmi3SetBoolean, fmi3GetString!, fmi3SetString, fmi3GetBinary!, fmi3SetBinary, fmi3GetClock!, fmi3SetClock
+import FMIImport: fmi3GetFMUState!, fmi3SetFMUState, fmi3FreeFMUState!, fmi3SerializedFMUStateSize!, fmi3SerializeFMUState!, fmi3DeSerializeFMUState!
+import FMIImport: fmi3SetIntervalDecimal, fmi3SetIntervalFraction, fmi3GetIntervalDecimal!, fmi3GetIntervalFraction!, fmi3GetShiftDecimal!, fmi3GetShiftFraction!
+import FMIImport: fmi3ActivateModelPartition
+import FMIImport: fmi3GetNumberOfVariableDependencies!, fmi3GetVariableDependencies!
+import FMIImport: fmi3GetDirectionalDerivative!, fmi3GetAdjointDerivative!, fmi3GetOutputDerivatives!
+import FMIImport: fmi3DoStep!
+
+import FMIImport: fmi3EnterConfigurationMode, fmi3ExitConfigurationMode, fmi3GetNumberOfContinuousStates!, fmi3GetNumberOfEventIndicators!, fmi3GetContinuousStates!, fmi3GetNominalsOfContinuousStates!
+import FMIImport: fmi3EvaluateDiscreteStates, fmi3EnterStepMode
+import FMIImport: fmi3SetTime, fmi3SetContinuousStates, fmi3EnterEventMode, fmi3UpdateDiscreteStates, fmi3EnterContinuousTimeMode, fmi3CompletedIntegratorStep!
+import FMIImport: fmi3GetContinuousStateDerivatives, fmi3GetContinuousStateDerivatives!, fmi3GetEventIndicators, fmi3GetContinuousStates, fmi3GetNominalsOfContinuousStates
+import FMIImport: fmi3StringToValueReference, fmi3ValueReferenceToString, fmi3ModelVariablesForValueReference
+import FMIImport: fmi3GetFloat32, fmi3GetFloat64, fmi3GetInt8, fmi3GetUInt8, fmi3GetInt16, fmi3GetUInt16, fmi3GetInt32, fmi3GetUInt32, fmi3GetInt64, fmi3GetUInt64, fmi3GetBoolean, fmi3GetBinary, fmi3GetClock, fmi3GetString
+import FMIImport: fmi3SetFloat32, fmi3SetFloat64, fmi3SetInt8, fmi3SetUInt8, fmi3SetInt16, fmi3SetUInt16, fmi3SetInt32, fmi3SetUInt32, fmi3SetInt64, fmi3SetUInt64, fmi3SetBoolean, fmi3SetBinary, fmi3SetClock, fmi3SetString
+import FMIImport: fmi3GetFMUState, fmi3SerializedFMUStateSize, fmi3SerializeFMUState, fmi3DeSerializeFMUState
+import FMIImport: fmi3GetDirectionalDerivative, fmi3GetAdjointDerivative
+import FMIImport: fmi3GetStartValue, fmi3SampleDirectionalDerivative, fmi3CompletedIntegratorStep
+import FMIImport: fmi3Unzip, fmi3Load, loadBinary, fmi3Reload, fmi3Unload
+import FMIImport: fmi3SampleDirectionalDerivative!
+import FMIImport: fmi3GetJacobian, fmi3GetJacobian!, fmi3GetFullJacobian, fmi3GetFullJacobian!
+import FMIImport: fmi3LoadModelDescription
+import FMIImport: fmi3GetDefaultStartTime, fmi3GetDefaultStopTime, fmi3GetDefaultTolerance, fmi3GetDefaultStepSize
+import FMIImport: fmi3GetModelName, fmi3GetInstantiationToken, fmi3GetGenerationTool, fmi3GetGenerationDateAndTime, fmi3GetVariableNamingConvention, fmi3GetNumberOfEventIndicators, fmi3GetNumberOfContinuousStates, fmi3IsCoSimulation, fmi3IsModelExchange, fmi3IsScheduledExecution
+import FMIImport: fmi3DependenciesSupported, fmi3GetModelIdentifier, fmi3CanGetSetState, fmi3CanSerializeFMUState, fmi3ProvidesDirectionalDerivatives, fmi3ProvidesAdjointDerivatives
+import FMIImport: fmi3Get, fmi3Get!, fmi3Set
+import FMIImport: fmi3GetSolutionTime, fmi3GetSolutionState, fmi3GetSolutionValue
+# export fmi3GetSolutionTime, fmi3GetSolutionState, fmi3GetSolutionValue
+export fmi3InstantiateCoSimulation!, fmi3InstantiateModelExchange!, fmi3InstantiateScheduledExecution!
+export fmi3EnterInitializationMode, fmi3ExitInitializationMode
+export fmi3GetFloat32, fmi3GetFloat64, fmi3GetInt8, fmi3GetUInt8, fmi3GetInt16, fmi3GetUInt16, fmi3GetInt32, fmi3GetUInt32, fmi3GetInt64, fmi3GetUInt64, fmi3GetBoolean, fmi3GetBinary, fmi3GetClock, fmi3GetString
+export fmi3SetFloat64
+export fmi3UpdateDiscreteStates, fmi3GetContinuousStateDerivatives!
+
import FMIImport: fmi2TypeModelExchange, fmi2TypeCoSimulation, fmi2Type
export fmi2TypeModelExchange, fmi2TypeCoSimulation, fmi2Type
+import FMIImport: fmi3TypeModelExchange, fmi3TypeCoSimulation, fmi3TypeScheduledExecution, fmi3Type
+export fmi2TypeModelExchange, fmi2TypeCoSimulation, fmi3TypeScheduledExecution, fmi2Type
+
using FMIExport
-using FMIExport: fmi2Create, fmi2CreateSimple
using FMIImport.FMICore: fmi2ValueReference, fmi3ValueReference
using FMIImport: fmi2ValueReferenceFormat, fmi3ValueReferenceFormat, fmi2StructMD, fmi3StructMD, fmi2Struct, fmi3Struct
@@ -86,6 +131,25 @@ function fmiLoadSolution(path::AbstractString; keyword="solution")
end
export fmiSaveSolution, fmiLoadSolution
+# from FMI3_plot.jl
+# function fmiPlot(solution::FMU3Solution; kwargs...)
+# @warn "fmiPlot(...) needs `Plots` package. Please install `Plots` and do `using Plots` or `import Plots`."
+# end
+# function fmiPlot!(fig, solution::FMU3Solution; kwargs...)
+# @warn "fmiPlot!(...) needs `Plots` package. Please install `Plots` and do `using Plots` or `import Plots`."
+# end
+# export fmiPlot, fmiPlot!
+
+# from FMI3_JLD2.jl
+# function fmiSaveSolution(solution::FMU3Solution, filepath::AbstractString; keyword="solution")
+# @warn "fmiSave(...) needs `JLD2` package. Please install `JLD2` and do `using JLD2` or `import JLD2`."
+# end
+# function fmiLoadSolution(path::AbstractString; keyword="solution")
+# @warn "fmiLoad(...) needs `JLD2` package. Please install `JLD2` and do `using JLD2` or `import JLD2`."
+# end
+
+export fmiSaveSolution, fmiLoadSolution
+
# Requires init
function __init__()
@require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" begin
@@ -96,7 +160,8 @@ function __init__()
@require JLD2="033835bb-8acc-5ee8-8aae-3f567f8a3819" begin
import .JLD2
include("FMI2_JLD2.jl")
- end
+ include("FMI3_JLD2.jl")
+ end
end
### EXPORTING LISTS START ###
@@ -174,15 +239,15 @@ Returns the tag 'modelName' from the model description.
# Arguments
- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
- `Union{fmi2StructMD, fmi3StructMD} = Union{FMU2, FMU2Component, fmi2ModelDescription, FMU3, FMU3Component, fmi3ModelDescription}`
+ `Union{fmi2StructMD, fmi3StructMD} = Union{FMU2, FMU2Component, fmi2ModelDescription, FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.
- `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
- - `str::FMU3Component`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
- `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
-See also [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`FMU3`](@ref), [`FMU3Component`](@ref), [`fmi3ModelDescription`](@ref).
+See also [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiGetModelName(str::fmi2StructMD)
fmi2GetModelName(str)
@@ -191,7 +256,8 @@ function fmiGetModelName(str::fmi3StructMD)
fmi3GetModelName(str)
end
-"""
+# TODO call differently in fmi3: getInstantationToken
+"""
fmiGetGUID(str::fmi2StructMD)
@@ -209,18 +275,23 @@ function fmiGetGUID(str::fmi2StructMD)
fmi2GetGUID(str)
end
-"""
+# TODO how wo work with docstring
+"""
- fmiGetGenerationTool(str::fmi2StructMD)
+ fmiGetGenerationTool(str::Union{fmi2StructMD, fmi3StructMD})
Returns the tag 'generationtool' from the model description.
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
- - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
+`fmi2StructMD= Union{FMU2, FMU2Component, fmi2ModelDescription}`
+`fmi3StructMD= Union{FMU3, FMU3Instance, fmi3ModelDescription}`
+- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `str.generationtool`: The function `fmi2GetGenerationTool` returns the tag 'generationtool' from the struct, representing a FMU (`str`).
@@ -231,19 +302,25 @@ See also [`fmi2GetGenerationTool`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref)
function fmiGetGenerationTool(str::fmi2StructMD)
fmi2GetGenerationTool(str)
end
-
+function fmiGetGenerationTool(str::fmi3StructMD)
+ fmi3GetGenerationTool(str)
+end
"""
- fmiGetGenerationDateAndTime(str::fmi2StructMD)
+ fmiGetGenerationDateAndTime(str::Union{fmi2StructMD, fmi3StructMD})
Returns the tag 'generationdateandtime' from the model description.
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+ More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `str.generationDateAndTime`: The function `fmi2GetGenerationDateAndTime` returns the tag 'generationDateAndTime' from the struct, representing a FMU (`str`).
@@ -254,226 +331,337 @@ See also [`fmi2GetGenerationDateAndTime`](@ref), [`fmi2StructMD`](@ref), [`FMU2`
function fmiGetGenerationDateAndTime(str::fmi2StructMD)
fmi2GetGenerationDateAndTime(str)
end
+function fmiGetGenerationDateAndTime(str::fmi3StructMD)
+ fmi3GetGenerationDateAndTime(str)
+end
"""
- fmiGetVariableNamingConvention(str::fmi2StructMD)
+ fmiGetVariableNamingConvention(str::Union{fmi2StructMD, fmi3StructMD})
Returns the tag 'varaiblenamingconvention' from the model description.
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+ More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `str.variableNamingConvention`: The function `fmi2GetVariableNamingConvention` returns the tag 'variableNamingConvention' from the struct, representing a FMU (`str`).
+See also [`fmi2GetVariableNamingConvention`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3GetVariableNamingConvention`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
-See also [`fmi2GetVariableNamingConvention`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
"""
function fmiGetVariableNamingConvention(str::fmi2StructMD)
fmi2GetVariableNamingConvention(str)
end
+function fmiGetVariableNamingConvention(str::fmi3StructMD)
+ fmi3GetVariableNamingConvention(str)
+end
"""
- fmiGetNumberOfEventIndicators(str::fmi2StructMD)
+ fmiGetNumberOfEventIndicators(str::str::Union{fmi2StructMD, fmi3StructMD})
Returns the tag 'numberOfEventIndicators' from the model description.
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `str.numberOfEventIndicators`: The function `fmi2GetNumberOfEventIndicators` returns the tag 'numberOfEventIndicators' from the struct, representing a FMU (`str`).
-See also [`fmi2GetNumberOfEventIndicators`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
+See also [`fmi2GetNumberOfEventIndicators`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3GetNumberOfEventIndicators`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiGetNumberOfEventIndicators(str::fmi2StructMD)
fmi2GetNumberOfEventIndicators(str)
end
+function fmiGetNumberOfEventIndicators(str::fmi3StructMD)
+ fmi3GetNumberOfEventIndicators(str)
+end
"""
- fmiGetModelIdentifier(fmu::FMU2)
+ fmiGetModelIdentifier(fmu::Union{FMU2, FMU3})
Returns the tag 'modelIdentifier' from CS or ME section.
# Arguments
- `fmu::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
-
+ - `fmu::FMU3`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 3.0 Standard](https://fmi-standard.org/).
# Returns
-- `fmu.modelDescription.coSimulation.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'coSimulation.modelIdentifier' from the model description of the FMU2-struct (`fmu.modelDescription`), if the FMU supports co simulation.
-- `fmu.modelDescription.modelExchange.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'modelExchange.modelIdentifier' from the model description of the FMU2-struct (`fmu.modelDescription`), if the FMU supports model exchange
+- `fmu.modelDescription.coSimulation.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'coSimulation.modelIdentifier' from the model description of the FMU2 or FMU3-struct (`fmu.modelDescription`), if the FMU supports co simulation.
+- `fmu.modelDescription.modelExchange.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'modelExchange.modelIdentifier' from the model description of the FMU2 or FMU3-struct (`fmu.modelDescription`), if the FMU supports model exchange
+- `fmu.modelDescription.modelExchange.modelIdentifier`: The function `fmiGetModelIdentifier` returns the tag 'scheduledExecution.modelIdentifier' from the model description of the FMU3-struct (`fmu.modelDescription`), if the FMU supports scheduled execution
-Also see [`fmi2GetModelIdentifier`](@ref), [`FMU2`](@ref).
+Also see [`fmi2GetModelIdentifier`](@ref), [`FMU2`](@ref), [`fmi3GetModelIdentifier`](@ref), [`FMU3`](@ref).
"""
function fmiGetModelIdentifier(fmu::FMU2)
fmi2GetModelIdentifier(fmu.modelDescription; type=fmu.type)
end
-
+function fmiGetModelIdentifier(fmu::FMU3)
+ fmi3GetModelIdentifier(fmu.modelDescription; type=fmu.type)
+end
"""
- fmiCanGetSetState(str::fmi2StructMD)
+ fmiCanGetSetState(str::Union{fmi2StructMD, fmi3StructMD})
Returns true, if the FMU supports the getting/setting of states
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `::Bool`: The function `fmi2CanGetSetState` returns True, if the FMU supports the getting/setting of states.
-See also [`fmi2CanGetSetState`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
+See also [`fmi2CanGetSetState`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3CanGetSetState`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiCanGetSetState(str::fmi2StructMD)
fmi2CanGetSetState(str)
end
+function fmiCanGetSetState(str::fmi3StructMD)
+ fmi3CanGetSetState(str)
+end
"""
- fmiCanSerializeFMUstate(str::fmi2StructMD)
+ fmiCanSerializeFMUstate(str::Union{fmi2StructMD, fmi3StructMD})
Returns true, if the FMU state can be serialized
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
- - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
+- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct wich provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `::Bool`: The function `fmi2CanSerializeFMUstate` returns True, if the FMU state can be serialized.
-See also [`fmi2CanSerializeFMUstate`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
+See also [`fmi2CanSerializeFMUstate`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3CanSerializeFMUstate`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiCanSerializeFMUstate(str::fmi2StructMD)
fmi2CanSerializeFMUstate(str)
end
+function fmiCanSerializeFMUstate(str::fmi3StructMD)
+ fmi3CanSerializeFMUState(str)
+end
+# TODO fmi3Call fmiProvidesDirectionalDerivatives
"""
- fmiProvidesDirectionalDerivative(str::fmi2StructMD)
+ fmiProvidesDirectionalDerivative(str::Union{fmi2StructMD, fmi3StructMD})
Returns true, if the FMU provides directional derivatives
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `::Bool`: The function `fmi2ProvidesDirectionalDerivative` returns True, if the FMU provides directional derivatives.
-See also [`fmi2ProvidesDirectionalDerivative`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
+See also [`fmi2ProvidesDirectionalDerivative`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3ProvidesDirectionalDerivative`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiProvidesDirectionalDerivative(str::fmi2StructMD)
fmi2ProvidesDirectionalDerivative(str)
end
+function fmiProvidesDirectionalDerivative(str::fmi3StructMD)
+ fmi3ProvidesDirectionalDerivatives(str)
+end
+
+"""
+
+ fmiProvidesAdjointDerivative(str::fmi3StructMD)
+
+Returns true, if the FMU provides adjoint derivatives
+
+# Arguments
+- `str::fmi3StructMD`: Representative for an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Component, fmi3ModelDescription}`
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
+
+# Returns
+- `::Bool`: The function `fmi3ProvidesAdjointDerivatives` returns True, if the FMU provides adjoint derivatives.
+
+See also [`fmi3ProvidesAdjointDerivatves`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
+"""
+function fmiProvidesAdjointDerivative(str::fmi3StructMD)
+ fmi3ProvidesAdjointDerivatives(str)
+end
"""
- fmiIsCoSimulation(str::fmi2StructMD)
+ fmiIsCoSimulation(str::Union{fmi2StructMD, fmi3StructMD})
Returns true, if the FMU supports co simulation
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
-More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
- - `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
+More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
+- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- `::Bool`: The function `fmi2IsCoSimulation` returns True, if the FMU supports co simulation
-See also [`fmi2IsCoSimulation`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
+See also [`fmi2IsCoSimulation`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3IsCoSimulation`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiIsCoSimulation(str::fmi2StructMD)
fmi2IsCoSimulation(str)
end
+function fmiIsCoSimulation(str::fmi3StructMD)
+ fmi3IsCoSimulation(str)
+end
"""
- fmiIsModelExchange(str::fmi2StructMD)
+ fmiIsModelExchange(str::Union{fmi2StructMD, fmi3StructMD})
Returns true, if the FMU supports model exchange
# Arguments
-- `str::fmi2StructMD`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
-More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
+More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::fmi2ModelDescription`: Struct witch provides the static information of ModelVariables.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
# Returns
- - `::Bool`: The function `fmi2IsCoSimulation` returns True, if the FMU supports model exchange.
+ - `::Bool`: The function `fmi2IsModelExchange` returns True, if the FMU supports model exchange.
-See also [`fmi2IsModelExchange`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref).
+See also [`fmi2IsModelExchange`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3IsModelExchange`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
"""
function fmiIsModelExchange(str::fmi2StructMD)
fmi2IsModelExchange(str)
end
+function fmiIsModelExchange(str::fmi3StructMD)
+ fmi3IsModelExchange(str)
+end
+
+"""
+
+ fmiIsScheduledExecution(str::fmi3StructMD)
+
+Returns true, if the FMU supports scheduled execution
+
+# Arguments
+- `str::fmi3StructMD`: Representative for an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::fmi3ModelDescription`: Struct witch provides the static information of ModelVariables.
+
+# Returns
+ - `::Bool`: The function `fmi3IsScheduledExecution` returns True, if the FMU supports scheduled execution.
+
+See also [`fmi3IsScheduledExecution`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@ref).
+"""
+function fmiIsScheduledExecution(str::fmi2StructMD)
+ fmi3IsScheduledExecution(str)
+end
# Multiple Dispatch variants for FMUs with version 2.0.X
+# TODO check version in MD
"""
fmiLoad(pathToFMU::String; unpackPath=nothing, type=nothing)
-Load FMUs independent of the FMI version, currently supporting version 2.0.X.
+Load FMUs independent of the FMI version, currently supporting version 2.0.X and 3.0.
# Arguments
- `pathToFMU::String`: String that contains the paths of ziped and unziped FMU folders.
# Keywords
- `unpackPath=nothing`: Via optional argument ```unpackPath```, a path to unpack the FMU can be specified (default: system temporary directory).
-- `type::Union{CS, ME} = nothing`: Via ```type```, a FMU type can be selected. If none of the unified type set is used, the default value `type = nothing` will be used.
+- `type::Union{CS, ME, SE} = nothing`: Via ```type```, a FMU type can be selected. If none of the unified type set is used, the default value `type = nothing` will be used.
# Returns
- Returns the instance of the FMU struct.
-See also [`fmi2Load`](@ref).
+See also [`fmi2Load`](@ref), [`fmi3Load`](@ref).
"""
-function fmiLoad(args...; kwargs...)
- fmi2Load(args...; kwargs...)
+function fmiLoad(pathToFMU::AbstractString, args...; kwargs...)
+ version = fmiCheckVersion(pathToFMU)
+ if version == "2.0"
+ fmi2Load(pathToFMU, args...; kwargs...)
+ elseif version == "3.0"
+ fmi3Load(pathToFMU, args...; kwargs...)
+ else
+ @warn "fmiLoad(...): Unknown FMU version"
+ end
end
"""
- fmiReload(fmu::FMU2)
+ fmiReload(fmu::Union{FMU2, FMU3})
Reloads the FMU-binary. This is useful, if the FMU does not support a clean reset implementation.
# Arguments
- `fmu::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `fmu::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
-
-See also [`fmi2Reload`](@ref).
+See also [`fmi2Reload`](@ref), [`fmi3Reload`](@ref).
"""
function fmiReload(fmu::FMU2, args...; kwargs...)
fmi2Reload(fmu, args...; kwargs...)
end
+function fmiReload(fmu::FMU3, args...; kwargs...)
+ fmi3Reload(fmu, args...; kwargs...)
+end
"""
- fmiSimulate(str::fmi2Struct, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+ fmiSimulate(str::Union{fmi2Struct, fmi3Struct}, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
tolerance::Union{Real, Nothing} = nothing,
dt::Union{Real, Nothing} = nothing,
solver = nothing,
@@ -492,12 +680,15 @@ Starts a simulation of the FMU instance for the matching FMU type, if both types
# Arguments
-- - `str::fmi2Struct`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3Struct = Union{FMU3, FMU3Instance}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
-- `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
-- `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
+ - `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.
# Keywords
- `tolerance::Union{Real, Nothing} = nothing`: Real number to set the tolerance for any OED-solver
@@ -526,10 +717,13 @@ See also [`fmi2Simulate`](@ref), [`fmi2SimulateME`](@ref), [`fmi2SimulateCS`](@r
function fmiSimulate(str::fmi2Struct, args...; kwargs...)
fmi2Simulate(str, args...; kwargs...)
end
+function fmiSimulate(str::fmi3Struct, args...; kwargs...)
+ fmi3Simulate(str, args...; kwargs...)
+end
"""
- fmiSimulateCS(str::fmi2Struct, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+ fmiSimulateCS(str::Union{fmi2Struct,fmi3Struct}, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
tolerance::Union{Real, Nothing} = nothing,
dt::Union{Real, Nothing} = nothing,
solver = nothing,
@@ -548,10 +742,13 @@ Starts a simulation of the Co-Simulation FMU instance.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3Struct = Union{FMU3, FMU3Instance}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
- `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
- `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.
@@ -574,16 +771,19 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
- If keyword `recordValues` is not set, a boolean `success` is returned (simulation success).
- If keyword `recordValues` is set, a tuple of type (true, DiffEqCallbacks.SavedValues) or (false, nothing).
-See also [`fmi2SimulateCS`](@ref), [`fmi2Simulate`](@ref), [`fmi2SimulateME`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
+See also [`fmi2SimulateCS`](@ref), [`fmi2Simulate`](@ref), [`fmi2SimulateME`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi3SimulateCS`](@ref), [`fmi3Simulate`](@ref), [`fmi3SimulateME`](@ref), [`fmi3Struct`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref).
"""
function fmiSimulateCS(str::fmi2Struct, args...; kwargs...)
fmi2SimulateCS(str, args...; kwargs...)
+end
+function fmiSimulateCS(str::fmi3Struct, args...; kwargs...)
+ fmi3SimulateCS(str, args...; kwargs...)
end
"""
- fmiSimulateME(str::fmi2Struct, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+ fmiSimulateME(str::Union{fmi2Struct,fmi3Struct}, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
tolerance::Union{Real, Nothing} = nothing,
dt::Union{Real, Nothing} = nothing,
solver = nothing,
@@ -602,10 +802,13 @@ Simulates a FMU instance for the given simulation time interval.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2StructMD, fmi3StructMD}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3Struct = Union{FMU3, FMU3Instance}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
- `t_start::Union{Real, Nothing} = nothing`: Set the start time to a value of type Real or the default value from the model description is used.
- `t_stop::Union{Real, Nothing} = nothing`: Set the end time to a value of type Real or the default value from the model description is used.
@@ -634,42 +837,55 @@ See also [`fmi2SimulateME`](@ref) [`fmi2SimulateCS`](@ref), [`fmi2Simulate`](@re
function fmiSimulateME(str::fmi2Struct, args...; kwargs...)
fmi2SimulateME(str, args...; kwargs...)
end
+function fmiSimulateME(str::fmi3Struct, args...; kwargs...)
+ fmi3SimulateME(str, args...; kwargs...)
+end
"""
- fmiUnload(fmu::FMU2)
+ fmiUnload(fmu::Union{FMU2, FMU3})
Unloads the FMU and all its instances and frees the allocated memory.
# Arguments
- `fmu::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `fmu::FMU3`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 3.0 Standard](https://fmi-standard.org/).
-See also [`fmi2Unload`](@ref).
+See also [`fmi2Unload`](@ref), [`fmi3Unload`](@ref).
"""
function fmiUnload(fmu::FMU2)
fmi2Unload(fmu)
end
+function fmiUnload(fmu::FMU3)
+ fmi3Unload(fmu)
+end
"""
- fmiGetNumberOfStates(str::fmi2Struct)
+ fmiGetNumberOfStates(str::Union{fmi2Struct, fmi3Struct})
Returns the number of states of the FMU.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+- `str::Union{fmi2Struct, fmi3Struct}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/).
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
# Returns
- Returns the length of the `md.valueReferences::Array{fmi2ValueReference}` corresponding to the number of states of the FMU.
-See also [`fmi2GetNumberOfStates`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
+See also [`fmi2GetNumberOfStates`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi3GetNumberOfStates`](@ref), [`fmi3Struct`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref).
"""
function fmiGetNumberOfStates(str::fmi2Struct)
fmi2GetNumberOfStates(str)
end
+function fmiGetNumberOfStates(str::fmi3Struct)
+ fmi3GetNumberOfStates(str)
+end
"""
@@ -699,29 +915,36 @@ end
"""
- fmiGetVersion(str::fmi2Struct)
+ fmiGetVersion(str::Union{fmi2Struct, fmi3Struct})
Returns the version of the FMU, version independent.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the FMI 2.0.2 Standard.
+- `str::Union{fmi2Struct, fmi3Struct}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
# Returns
-- Returns a string from the address of a C-style (NUL-terminated) string. The string represents the version of the “fmi2Functions.h” header file which was used to compile the functions of the FMU. The function returns “fmiVersion” which is defined in this header file. The standard header file as documented in this specification has version “2.0”
+- Returns a string from the address of a C-style (NUL-terminated) string. The string represents the version of the “fmiXFunctions.h” header file which was used to compile the functions of the FMU. The function returns “fmiVersion” which is defined in this header file. The standard header file as documented in this specification has version “2.0” or "3.0"
# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.22]: 2.1.4 Inquire Platform and Version Number of Header Files
- FMISpec2.0.2[p.16]: 2.1.2 Platform Dependent Definitions
+ - FMISpec3.0[p. ]: 2.2.5. Inquire Version Number of Header Files
-See also [`fmi2GetVersion`](@ref), [`unsafe_string`](https://docs.julialang.org/en/v1/base/strings/#Base.unsafe_string), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
+See also [`fmi2GetVersion`](@ref), [`unsafe_string`](https://docs.julialang.org/en/v1/base/strings/#Base.unsafe_string), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi3GetVersion`](@ref), [`fmi3Struct`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref).
"""
function fmiGetVersion(str::fmi2Struct)
fmi2GetVersion(str)
end
+function fmiGetVersion(str::fmi3Struct)
+ fmi3GetVersion(str)
+end
"""
fmiInfo(str::fmi2Struct)
@@ -729,20 +952,27 @@ end
Prints FMU-specific information into the REPL.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the FMI 2.0.2 Standard.
+- `str::Union{fmi2Struct, fmi3Struct}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3StructMD = Union{FMU3, FMU3Instance, fmi3ModelDescription}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
# Source
- - FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
+- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
+- FMISpec3.0 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
-See also [`fmi2Info`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
+See also [`fmi2Info`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi3Info`](@ref), [`fmi3Struct`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref).
"""
function fmiInfo(str::fmi2Struct)
fmi2Info(str)
end
-
+function fmiInfo(str::fmi3Struct)
+ fmi3Info(str)
+end
+# TODO how to handle different calls for CS, ME, SE in FMI3
"""
fmiInstantiate!(fmu::FMU2; pushComponents::Bool = true, visible::Bool = false, loggingOn::Bool = false, externalCallbacks::Bool = false,
@@ -783,47 +1013,54 @@ end
"""
- fmiFreeInstance!(str::fmi2Struct)
+ fmiFreeInstance!(str::Union{fmi2Struct, fmi3Struct})
Frees the allocated memory of the last instance of the FMU.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the FMI 2.0.2 Standard.
+- `str::Union{fmi2Struct, fmi3Struct}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3Struct = Union{FMU3, FMU3Instance}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
# Returns
-- `status::fmi2Status`: Return `status` is an enumeration of type `fmi2Status` and indicates the success of the function call.
-More detailed:
- - `fmi2OK`: all well
- - `fmi2Warning`: things are not quite right, but the computation can continue
- - `fmi2Discard`: if the slave computed successfully only a subinterval of the communication step
- - `fmi2Error`: the communication step could not be carried out at all
- - `fmi2Fatal`: if an error occurred which corrupted the FMU irreparably
- - `fmi2Pending`: this status is returned if the slave executes the function asynchronously
+- `status::fmi2Status`: returned by all functions to indicate the success of the function call
+- `status::fmi3Status`: returned by all functions to indicate the success of the function call
# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an FMU
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions
+- FMISpec3.0 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
+- FMISpec3.0[p. ]: 2.3.1. Super State: FMU State Settable
+- FMISpec3.0[p. ]: 2.2.4. Status Returned by Functions
-See also [fmi2FreeInstance](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
+See also [fmi2FreeInstance](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [fmi3FreeInstance](@ref), [`fmi3Struct`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref).
"""
function fmiFreeInstance!(str::fmi2Struct)
fmi2FreeInstance!(str)
end
+function fmiFreeInstance!(str::fmi3Struct)
+ fmi3FreeInstance!(str)
+end
"""
- fmiSetDebugLogging(str::fmi2Struct)
+
+ fmiSetDebugLogging(str::Union{fmi2Struct, fmi3Struct})
Control the use of the logging callback function, version independent.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the FMI 2.0.2 Standard.
+- `str::Union{fmi2Struct, fmi3Struct}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3Struct = Union{FMU3, FMU3Instance}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
# Returns
- `status::fmi2Status`: Return `status` is an enumeration of type `fmi2Status` and indicates the success of the function call.
@@ -841,11 +1078,16 @@ More detailed:
- FMISpec2.0.2[p.22]: 2.1.2 Platform Dependent Definitions (fmi2TypesPlatform.h)
- FMISpec2.0.2[p.22]: 2.1.3 Status Returned by Functions
- FMISpec2.0.2[p.22]: 2.1.5 Creation, Destruction and Logging of FMU Instances
+- FMISpec3.0 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
+- FMISpec3.0[p. ]: 2.3.1. Super State: FMU State Settable
See also [`fmi2SetDebugLogging`](@ref).
"""
function fmiSetDebugLogging(str::fmi2Struct)
fmi2SetDebugLogging(str)
end
+function fmiSetDebugLogging(str::fmi3Struct)
+ fmi3SetDebugLogging(str)
+end
"""
@@ -885,7 +1127,7 @@ See also [fmi2SetupExperiment](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FM
function fmiSetupExperiment(str::fmi2Struct, args...; kwargs...)
fmi2SetupExperiment(str, args...; kwargs...)
end
-
+# TODO different function call in fmi3 more arguments
"""
fmiEnterInitializationMode(str::fmi2Struct)
@@ -922,37 +1164,40 @@ end
"""
- fmiExitInitializationMode(str::fmi2Struct)
+ fmiExitInitializationMode(str::Union{fmi2Struct, fmi3Struct})
Informs the FMU to exit initialization mode, version independent.
# Arguments
-- `str::fmi2Struct`: Representative for an FMU in the FMI 2.0.2 Standard.
+- `str::Union{fmi2Struct, fmi3Struct}`: Representative for an FMU in the [FMI 2.0.2 Standard](https://fmi-standard.org/) or [FMI 3.0 Standard](https://fmi-standard.org/). Other notation:
More detailed: `fmi2Struct = Union{FMU2, FMU2Component}`
+More detailed: `fmi3Struct = Union{FMU3, FMU3Instance}`
- `str::FMU2`: Mutable struct representing a FMU and all it instantiated instances in the FMI 2.0.2 Standard.
- `str::FMU2Component`: Mutable struct represents an instantiated instance of an FMU in the FMI 2.0.2 Standard.
+ - `str::FMU3`: Mutable struct representing an FMU in the [FMI 3.0 Standard](https://fmi-standard.org/).
+ - `str::FMU3Instance`: Mutable struct represents a pointer to an FMU specific data structure that contains the information needed. Also in [FMI 3.0 Standard](https://fmi-standard.org/).
# Returns
-- Returns a warning if `str.state` is not called in `fmi2ComponentStateInitializationMode`.
-- `status::fmi2Status`: Return `status` is an enumeration of type `fmi2Status` and indicates the success of the function call.
-More detailed:
- - `fmi2OK`: all well
- - `fmi2Warning`: things are not quite right, but the computation can continue
- - `fmi2Discard`: if the slave computed successfully only a subinterval of the communication step
- - `fmi2Error`: the communication step could not be carried out at all
- - `fmi2Fatal`: if an error occurred which corrupted the FMU irreparably
- - `fmi2Pending`: this status is returned if the slave executes the function asynchronously
+- Returns a warning if `str.state` is not called in `fmi2ComponentStateInitializationMode` or `fmi3InstanceInitializationMode`.
+- `status::fmi2Status`: returned by all functions to indicate the success of the function call
+- `status::fmi3Status`: returned by all functions to indicate the success of the function call
# Source
- FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
- FMISpec2.0.2[p.23]: 2.1.6 Initialization, Termination, and Resetting an FMU
- FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions
+- FMISpec3.0 Link: [https://fmi-standard.org/](https://fmi-standard.org/)
+- FMISpec3.0[p. ]: 2.3.3. State: Initialization Mode
+- FMISpec3.0[p. ]: 2.2.4. Status Returned by Functions
- See also [`fmi2ExitInitializationMode`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref).
+ See also [fmi2ExitInitializationMode](@ref), [fmi3ExitInitializationMode](@ref).
"""
function fmiExitInitializationMode(str::fmi2Struct)
fmi2ExitInitializationMode(str)
end
+function fmiExitInitializationMode(str::fmi3Struct)
+ fmi3ExitInitializationMode(str)
+end
"""
@@ -989,7 +1234,9 @@ More detailed:
function fmiTerminate(str::fmi2Struct)
fmi2Terminate(str)
end
-
+function fmiTerminate(str::fmi3Struct)
+ fmi3Terminate(str)
+end
"""
fmiReset(str::fmi2Struct)
@@ -1025,6 +1272,9 @@ More detailed:
function fmiReset(str::fmi2Struct)
fmi2Reset(str)
end
+function fmiReset(str::fmi3Struct)
+ fmi3Reset(str)
+end
"""
@@ -1052,7 +1302,9 @@ More detailed: `fmi2ValueReferenceFormat = Union{Nothing, String, Array{String,1
function fmiGet(str::fmi2Struct, args...; kwargs...)
fmi2Get(str, args...; kwargs...)
end
-
+function fmiGet(str::fmi3Struct, args...; kwargs...)
+ fmi3Get(str, args...; kwargs...)
+end
"""
fmiGet!(str::fmi2Struct, comp::FMU2Component, vrs::fmi2ValueReferenceFormat, dstArray::AbstractArray)
@@ -1082,7 +1334,9 @@ More detailed:
function fmiGet!(str::fmi2Struct, args...; kwargs...)
fmi2Get!(str, args...; kwargs...)
end
-
+function fmiGet!(str::fmi3Struct, args...; kwargs...)
+ fmi3Get!(str, args...; kwargs...)
+end
"""
fmiSet(str::fmi2Struct, comp::FMU2Component, vrs::fmi2ValueReferenceFormat, srcArray::AbstractArray; filter=nothing)
@@ -1115,6 +1369,9 @@ More detailed:
function fmiSet(str::fmi2Struct, args...; kwargs...)
fmi2Set(str, args...; kwargs...)
end
+function fmiSet(str::fmi3Struct, args...; kwargs...)
+ fmi3Set(str, args...; kwargs...)
+end
"""
@@ -1144,6 +1401,10 @@ More detailed: `fmi2ValueReferenceFormat = Union{Nothing, String, Array{String,1
function fmiGetReal(str::fmi2Struct, args...; kwargs...)
fmi2GetReal(str, args...; kwargs...)
end
+function fmiGetReal(str::fmi3Struct, args...; kwargs...)
+ fmi3GetReal(str, args...; kwargs...)
+end
+# TODO different call in fmi3 fmi3GetOuputDerivatives
"""
@@ -1176,7 +1437,7 @@ See also [`fmi2SetRealInputDerivatives!`](@ref).
function fmiGetRealOutputDerivatives(str::fmi2Struct, args...; kwargs...)
fmi2GetRealOutputDerivatives(str, args...; kwargs...)
end
-
+# TODO different call in fmi3
"""
fmiGetReal!(str::fmi2Struct, c::FMU2Component, vr::fmi2ValueReferenceFormat, values::Array{fmi2Real})
@@ -1215,7 +1476,7 @@ More detailed:
function fmiGetReal!(str::fmi2Struct, args...; kwargs...)
fmi2GetReal!(str, args...; kwargs...)
end
-
+# TODO different call in fmi3
"""
fmiSetReal(str::fmi2Struct, c::FMU2Component, vr::fmi2ValueReferenceFormat, values::Union{Array{<:Real}, <:Real})
@@ -1256,7 +1517,7 @@ See also [`fmi2SetReal`](@ref), [`fmi2GetReal`](@ref),[`fmi2ValueReferenceFormat
function fmiSetReal(str::fmi2Struct, args...; kwargs...)
fmi2SetReal(str, args...; kwargs...)
end
-
+# TODO different call in fmi3
"""
#Todo: Add types according spec
@@ -1300,6 +1561,7 @@ function fmiSetRealInputDerivatives(str::fmi2Struct, args...; kwargs...)
fmi2SetRealInputDerivatives(str, args...; kwargs...)
end
+# TODO different call in fmi3
"""
@@ -1330,7 +1592,7 @@ See also [`fmi2GetInteger`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Stru
function fmiGetInteger(str::fmi2Struct,args...; kwargs...)
fmi2GetInteger(str, args...; kwargs...)
end
-
+# TODO different call in fmi3
"""
function fmiGetInteger!(str::fmi2Struct, c::FMU2Component, vr::fmi2ValueReferenceFormat, values::Array{fmi2Integer})
@@ -1374,8 +1636,10 @@ See also [`fmi2GetInteger!`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Str
function fmiGetInteger!(str::fmi2Struct, args...; kwargs...)
fmi2GetInteger!(str, args...; kwargs...)
end
+# TODO different call in fmi3
"""
+Set the values of an array of integer variables
fmiSetInteger(str::fmi2Struct, c::FMU2Component, vr::fmi2ValueReferenceFormat, values::Union{Array{<:Integer}, <:Integer})
@@ -1430,6 +1694,10 @@ See also [`fmi2GetBoolean`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Stru
function fmiGetBoolean(str::fmi2Struct, args...; kwargs...)
fmi2GetBoolean(str, args...; kwargs...)
end
+function fmiGetBoolean(str::fmi3Struct, args...; kwargs...)
+ fmi3GetBoolean(str, args...; kwargs...)
+end
+
"""
fmiGetBoolean!(str::fmi2Struct, c::FMU2Component, vr::fmi2ValueReferenceFormat, values::Array{fmi2Boolean})
@@ -1469,6 +1737,9 @@ See also [`fmi2GetBoolean!`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Str
function fmiGetBoolean!(str::fmi2Struct, args...; kwargs...)
fmi2GetBoolean!(str, args...; kwargs...)
end
+function fmiGetBoolean!(str::fmi3Struct, args...; kwargs...)
+ fmi3GetBoolean!(str, args...; kwargs...)
+end
"""
@@ -1509,6 +1780,9 @@ See also [`fmi2GetBoolean`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Stru
function fmiSetBoolean(str::fmi2Struct, args...; kwargs...)
fmi2SetBoolean(str, args...; kwargs...)
end
+function fmiSetBoolean(str::fmi3Struct, args...; kwargs...)
+ fmi3SetBoolean(str, args...; kwargs...)
+end
"""
@@ -1538,6 +1812,9 @@ See also [`fmi2GetBoolean!`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Str
function fmiGetString(str::fmi2Struct, args...; kwargs...)
fmi2GetString(str, args...; kwargs...)
end
+function fmiGetString(str::fmi3Struct, args...; kwargs...)
+ fmi3GetString(str, args...; kwargs...)
+end
"""
@@ -1581,6 +1858,9 @@ See also [`fmi2GetString!`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Stru
function fmiGetString!(str::fmi2Struct, args...; kwargs...)
fmi2GetString!(str, args...; kwargs...)
end
+function fmiGetString!(str::fmi3Struct, args...; kwargs...)
+ fmi3GetString!(str, args...; kwargs...)
+end
"""
@@ -1629,7 +1909,10 @@ See also [`fmi2SetString`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Struc
function fmiSetString(str::fmi2Struct, args...; kwargs...)
fmi2SetString(str, args...; kwargs...)
end
-
+function fmiSetString(str::fmi3Struct, args...; kwargs...)
+ fmi3SetString(str, args...; kwargs...)
+end
+# TODO different call in fmi3
"""
#ToDo
@@ -1656,6 +1939,10 @@ See also [`fmi2GetFMUstate`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2
function fmiGetFMUstate(str::fmi2Struct)
fmi2GetFMUstate(str)
end
+function fmiGetFMUstate(str::fmi3Struct)
+ fmi3GetFMUState(str)
+end
+# TODO different call in fmi3
"""
@@ -1692,6 +1979,10 @@ See also [`fmi2GetFMUstate`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2
function fmiSetFMUstate(str::fmi2Struct, args...; kwargs...)
fmi2SetFMUstate(str, args...; kwargs...)
end
+function fmiSetFMUstate(str::fmi3Struct, args...; kwargs...)
+ fmi3SetFMUState(str, args...; kwargs...)
+end
+# TODO different call in fmi3
"""
@@ -1731,6 +2022,10 @@ See also [`fmi2FreeFMUstate!`](@ref),[`fmi2FMUstate`](@ref), [`fmi2Struct`](@ref
function fmiFreeFMUstate!(str::fmi2Struct, args...; kwargs...)
fmi2FreeFMUstate!(str, args...; kwargs...)
end
+function fmiFreeFMUstate!(str::fmi3Struct, args...; kwargs...)
+ fmi3FreeFMUState!(str, args...; kwargs...)
+end
+# TODO different call in fmi3
"""
@@ -1761,6 +2056,10 @@ See also [`fmi2SerializedFMUstateSize`](@ref),[`fmi2FMUstate`](@ref), [`fmi2Stru
function fmiSerializedFMUstateSize(str::fmi2Struct, args...; kwargs...)
fmi2SerializedFMUstateSize(str, args...; kwargs...)
end
+function fmiSerializedFMUstateSize(str::fmi3Struct, args...; kwargs...)
+ fmi3SerializedFMUStateSize(str, args...; kwargs...)
+end
+# TODO different call in fmi3
"""
@@ -1789,6 +2088,10 @@ See also [`fmi2SerializeFMUstate`](@ref),[`fmi2FMUstate`](@ref), [`fmi2Struct`](
function fmiSerializeFMUstate(str::fmi2Struct, args...; kwargs...)
fmi2SerializeFMUstate(str, args...; kwargs...)
end
+function fmiSerializeFMUstate(str::fmi3Struct, args...; kwargs...)
+ fmi3SerializeFMUState(str, args...; kwargs...)
+end
+# TODO different call in fmi3
"""
TODO
@@ -1817,6 +2120,9 @@ See also [`fmi2DeSerializeFMUstate`](@ref),[`fmi2FMUstate`](@ref), [`fmi2Struct`
function fmiDeSerializeFMUstate(str::fmi2Struct, args...; kwargs...)
fmi2DeSerializeFMUstate(str, args...; kwargs...)
end
+function fmiDeSerializeFMUstate(str::fmi3Struct, args...; kwargs...)
+ fmi3DeSerializeFMUState(str, args...; kwargs...)
+end
"""
@@ -1872,6 +2178,9 @@ See also [`fmi2GetDirectionalDerivative`](@ref),[`fmi2FMUstate`](@ref), [`fmi2St
function fmiGetDirectionalDerivative(str::fmi2Struct, args...; kwargs...)
fmi2GetDirectionalDerivative(str, args...; kwargs...)
end
+function fmiGetDirectionalDerivative(str::fmi3Struct, args...; kwargs...)
+ fmi3GetDirectionalDerivative(str, args...; kwargs...)
+end
"""
TODO -> Arguments
@@ -1940,6 +2249,23 @@ See also [`fmi2GetDirectionalDerivative`](@ref).
function fmiGetDirectionalDerivative!(str::fmi2Struct, args...; kwargs...)
fmi2GetDirectionalDerivative!(str, args...; kwargs...)
end
+function fmiGetDirectionalDerivative!(str::fmi3Struct, args...; kwargs...)
+ fmi3GetDirectionalDerivative!(str, args...; kwargs...)
+end
+
+"""
+Returns the values of the adjoint derivatives.
+"""
+function fmiGetAdjointDerivative(str::fmi3Struct, args...; kwargs...)
+ fmi3GetAdjointDerivative(str, args...; kwargs...)
+end
+
+"""
+Returns the values of the adjoint derivatives (in-place).
+"""
+function fmiGetAdjointDerivative!(str::fmi3Struct, args...; kwargs...)
+ fmi3GetAdjointDerivative!(str, args...; kwargs...)
+end
"""
@@ -1984,6 +2310,9 @@ See also [`fmi2DoStep`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Compo
function fmiDoStep(str::fmi2Struct, args...; kwargs...)
fmi2DoStep(str, args...; kwargs...)
end
+function fmiDoStep(str::fmi3Struct, args...; kwargs...)
+ fmi3DoStep(str, args...; kwargs...)
+end
"""
#TODO
@@ -2023,6 +2352,9 @@ See also [`fmi2SampleDirectionalDerivative`](@ref), [`fmi2Struct`](@ref), [`FMU2
function fmiSampleDirectionalDerivative(str::fmi2Struct, args...; kwargs...)
fmi2SampleDirectionalDerivative(str, args...; kwargs...)
end
+function fmiSampleDirectionalDerivative(str::fmi3Struct, args...; kwargs...)
+ fmi3SampleDirectionalDerivative(str, args...; kwargs...)
+end
"""
#TODO
@@ -2031,6 +2363,9 @@ Samples the values of the directional derivatives (in-place).
function fmiSampleDirectionalDerivative!(str::fmi2Struct, args...; kwargs...)
fmi2SampleDirectionalDerivative!(str, args...; kwargs...)
end
+function fmiSampleDirectionalDerivative!(str::fmi3Struct, args...; kwargs...)
+ fmi3SampleDirectionalDerivative!(str, args...; kwargs...)
+end
"""
@@ -2065,8 +2400,11 @@ More detailed:
- FMISpec2.0.2[p.83]: 3.2.1 Providing Independent Variables and Re-initialization of Caching
See also [`fmi2SetTime`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ValueReference`](@ref).
"""
-function fmiSetTime(c::fmi2Struct, args...; kwargs...)
- fmi2SetTime(c, args...; kwargs...)
+function fmiSetTime(str::fmi2Struct, args...; kwargs...)
+ fmi2SetTime(str, args...; kwargs...)
+end
+function fmiSetTime(str::fmi3Struct, args...; kwargs...)
+ fmi3SetTime(str, args...; kwargs...)
end
"""
@@ -2108,6 +2446,9 @@ See also [`fmi2SetContinuousStates`](@ref).
function fmiSetContinuousStates(str::fmi2Struct, args...; kwargs...)
fmi2SetContinuousStates(str, args...; kwargs...)
end
+function fmiSetContinuousStates(str::fmi3Struct, args...; kwargs...)
+ fmi3SetContinuousStates(str, args...; kwargs...)
+end
"""
@@ -2138,10 +2479,13 @@ More detailed:
- FMISpec2.0.2[p.83]: 3.2.2 Evaluation of Model Equations
See also [`fmi2EnterEventMode`](@ref).
"""
-function fmi2EnterEventMode(str::fmi2Struct)
+function fmiEnterEventMode(str::fmi2Struct)
fmi2EnterEventMode(str)
end
-
+function fmiEnterEventMode(str::fmi3Struct)
+ fmi3EnterEventMode(str)
+end
+# TODO different function call in fmi3
"""
fmiNewDiscreteStates(str::fmi2Struct)
@@ -2208,6 +2552,9 @@ See also [`fmi2EnterContinuousTimeMode`](@ref).
function fmiEnterContinuousTimeMode(str::fmi2Struct)
fmi2EnterContinuousTimeMode(str)
end
+function fmiEnterContinuousTimeMode(str::fmi3Struct)
+ fmi3EnterContinuousTimeMode(str)
+end
"""
@@ -2243,7 +2590,10 @@ See also [`fmi2CompletedIntegratorStep`](@ref), [`fmi2SetFMUState`](@ref).
function fmiCompletedIntegratorStep(str::fmi2Struct, args...; kwargs...)
fmi2CompletedIntegratorStep(str, args...; kwargs...)
end
-
+function fmiCompletedIntegratorStep(str::fmi3Struct, args...; kwargs...)
+ fmi3CompletedIntegratorStep(str, args...; kwargs...)
+end
+# TODO different function call
"""
fmiGetDerivatives(str::fmi2Struct)
@@ -2294,6 +2644,9 @@ See also [`fmi2GetEventIndicators`](@ref).
function fmiGetEventIndicators(str::fmi2Struct)
fmi2GetEventIndicators(str)
end
+function fmiGetEventIndicators(str::fmi3Struct)
+ fmi3GetEventIndicators(str)
+end
"""
@@ -2318,6 +2671,9 @@ See also [`fmi2GetEventIndicators`](@ref).
function fmiGetContinuousStates(s::fmi2Struct)
fmi2GetContinuousStates(s)
end
+function fmiGetContinuousStates(s::fmi3Struct)
+ fmi3GetContinuousStates(s)
+end
"""
@@ -2342,6 +2698,9 @@ See also [`fmi2GetNominalsOfContinuousStates`](@ref).
function fmiGetNominalsOfContinuousStates(s::fmi2Struct)
fmi2GetNominalsOfContinuousStates(s)
end
+function fmiGetNominalsOfContinuousStates(s::fmi3Struct)
+ fmi3GetNominalsOfContinuousStates(s)
+end
"""
@@ -2362,6 +2721,9 @@ More detailed: `fmi2ValueReferenceFormat = Union{Nothing, String, Array{String,1
function fmiGetStartValue(s::fmi2Struct, vr::fmi2ValueReferenceFormat)
fmi2GetStartValue(s, vr)
end
+function fmiGetStartValue(s::fmi3Struct, vr::fmi3ValueReferenceFormat)
+ fmi3GetStartValue(s, vr)
+end
##### function setters
@@ -2491,7 +2853,7 @@ end
##### Multiple Dispatch fallback for FMUs with unsupported versions #####
-unsupportedFMUs = Union{FMU1,FMU3}
+unsupportedFMUs = FMU1
function fmiDoStep(fmu::unsupportedFMUs, args...; kwargs...)
error(unsupportedFMU::errorType)
end
@@ -2711,5 +3073,12 @@ end
function fmiIsModelExchange(fmu::unsupportedFMUs)
error(unsupportedFMU::errorType)
end
+function fmiGetAdjointDerivative(str::fmi2Struct, args...; kwargs...)
+ error(unsupportedFMU::errorType)
+end
+function fmiGetAdjointDerivative!(str::fmi2Struct, args...; kwargs...)
+ error(unsupportedFMU::errorType)
+end
+
end # module FMI
diff --git a/src/FMI2_comp_wraps.jl b/src/FMI2_comp_wraps.jl
index 06d3af2f..3eacee12 100644
--- a/src/FMI2_comp_wraps.jl
+++ b/src/FMI2_comp_wraps.jl
@@ -8,33 +8,6 @@
# - wrappers to call fmi2ComponentFunctions from FMUs (additional functions, last instantiated component is used) [exported]
# FMI-spec
-"""
- fmi2Simulate(fmu::FMU2, args...; kwargs...)
-
-Wrapper for fmi2Simulate() in FMI/FMI2_sim.jl
-"""
-function fmi2Simulate(fmu::FMU2, args...; kwargs...)
- return fmi2Simulate(fmu, nothing, args...; kwargs...)
-end
-
-"""
- fmi2SimulateCS(fmu::FMU2, args...; kwargs...)
-
-Wrapper for fmi2SimulateCS() in FMI/FMI2_sim.jl
-"""
-function fmi2SimulateCS(fmu::FMU2, args...; kwargs...)
- return fmi2SimulateCS(fmu, nothing, args...; kwargs...)
-end
-
-"""
- fmi2SimulateME(fmu::FMU2, args...; kwargs...)
-
-Wrapper for fmi2SimulateME() in FMI/FMI2_sim.jl
-"""
-function fmi2SimulateME(fmu::FMU2, args...; kwargs...)
- return fmi2SimulateME(fmu, nothing, args...; kwargs...)
-end
-
"""
fmi2FreeInstance!(fmu::FMU2)
@@ -566,6 +539,33 @@ function fmi2GetNominalsOfContinuousStates(fmu::FMU2)
end
# additionals
+"""
+ fmi2Simulate(fmu::FMU2, args...; kwargs...)
+
+Wrapper for fmi2Simulate() in FMI/FMI2_sim.jl
+"""
+function fmi2Simulate(fmu::FMU2, args...; kwargs...)
+ return fmi2Simulate(fmu, nothing, args...; kwargs...)
+end
+
+"""
+ fmi2SimulateCS(fmu::FMU2, args...; kwargs...)
+
+Wrapper for fmi2SimulateCS() in FMI/FMI2_sim.jl
+"""
+function fmi2SimulateCS(fmu::FMU2, args...; kwargs...)
+ return fmi2SimulateCS(fmu, nothing, args...; kwargs...)
+end
+
+"""
+ fmi2SimulateME(fmu::FMU2, args...; kwargs...)
+
+Wrapper for fmi2SimulateME() in FMI/FMI2_sim.jl
+"""
+function fmi2SimulateME(fmu::FMU2, args...; kwargs...)
+ return fmi2SimulateME(fmu, nothing, args...; kwargs...)
+end
+
"""
fmi2GetStartValue(fmu::FMU2, args...; kwargs...)
diff --git a/src/FMI2_sim.jl b/src/FMI2_sim.jl
index 692fdbb2..bacc6f28 100644
--- a/src/FMI2_sim.jl
+++ b/src/FMI2_sim.jl
@@ -820,7 +820,7 @@ function fmi2SimulateME(fmu::FMU2, c::Union{FMU2Component, Nothing}=nothing, t_s
showProgress::Bool = true,
kwargs...)
- @assert fmi2IsModelExchange(fmu) "fmi2SimulateME(...): This function supports Model Excahnge FMUs only."
+ @assert fmi2IsModelExchange(fmu) "fmi2SimulateME(...): This function supports Model Exchange FMUs only."
#@assert fmu.type == fmi2TypeModelExchange "fmi2SimulateME(...): This FMU supports Model Exchange, but was instantiated in CS mode. Use `fmiLoad(...; type=:ME)`."
# input function handling
diff --git a/src/FMI3_JLD2.jl b/src/FMI3_JLD2.jl
new file mode 100644
index 00000000..c93ba612
--- /dev/null
+++ b/src/FMI3_JLD2.jl
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+using FMIImport: FMU3Solution
+
+"""
+Saves a FMU3Solution for later use.
+"""
+function fmiSaveSolution(solution::FMU3Solution, filepath::AbstractString; keyword="solution")
+ return JLD2.save(filepath, Dict(keyword=>solution))
+end
+
+"""
+Loads a FMU3Solution. Returns a previously saved `FMU3Solution`.
+"""
+function fmiLoadSolution(filepath::AbstractString; keyword="solution")
+ return JLD2.load(filepath, keyword)
+end
\ No newline at end of file
diff --git a/src/FMI3_additional.jl b/src/FMI3_additional.jl
index 9d6747d5..e535b53d 100644
--- a/src/FMI3_additional.jl
+++ b/src/FMI3_additional.jl
@@ -1,1319 +1,246 @@
+# STATUS: no todos
+# ABM: done
+
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#
+# What is included in the file `FMI3_additional.jl` (FMU add functions)?
+# - high-level functions, that are useful, but not part of the FMI-spec [exported]
+
+using Base.Filesystem: mktempdir
+
using FMIImport: FMU3, fmi3ModelDescription
using FMIImport: fmi3Float32, fmi3Float64, fmi3Int8, fmi3Int16, fmi3Int32, fmi3Int64, fmi3Boolean, fmi3String, fmi3Binary, fmi3UInt8, fmi3UInt16, fmi3UInt32, fmi3UInt64, fmi3Byte
using FMIImport: fmi3Clock, fmi3FMUState
-using FMIImport: fmi3CallbackLogger, fmi3CallbackIntermediateUpdate, fmi3CallbackClockUpdate
-
-# wrapper functions on the model description
-function fmi3GetModelName(fmu::FMU3)
- fmi3GetModelName(fmu.modelDescription)
-end
-function fmi3GetInstantiationToken(fmu::FMU3)
- fmi3GetInstantiationToken(fmu.modelDescription)
-end
-function fmi3GetGenerationTool(fmu::FMU3)
- fmi3GetGenerationTool(fmu.modelDescription)
-end
-function fmi3GetGenerationDateAndTime(fmu::FMU3)
- fmi3GetGenerationDateAndTime(fmu.modelDescription)
-end
-function fmi3GetVariableNamingConvention(fmu::FMU3)
- fmi3GetVariableNamingConvention(fmu.modelDescription)
-end
+using FMIImport: fmi3True, fmi3False
+using FMIImport: fmi3DependencyKindDependent, fmi3DependencyKindFixed
+using FMIImport: fmi3CallbackLogger, fmi3CallbackIntermediateUpdate, fmi3CallbackClockUpdate, fmi3Instance
+import FMIImport: fmi3VariableNamingConventionFlat, fmi3VariableNamingConventionStructured
-function fmi3CanGetSetState(fmu::FMU3)
- fmi3CanGetSetState(fmu.modelDescription)
-end
-function fmi3CanSerializeFMUstate(fmu::FMU3)
- fmi3CanSerializeFMUstate(fmu.modelDescription)
-end
-function fmi3ProvidesDirectionalDerivatives(fmu::FMU3)
- fmi3ProvidesDirectionalDerivatives(fmu.modelDescription)
-end
-function fmi3ProvidesAdjointDerivatives(fmu::FMU3)
- fmi3ProvidesAdjointDerivatives(fmu.modelDescription)
-end
-function fmi3IsCoSimulation(fmu::FMU3)
- fmi3IsCoSimulation(fmu.modelDescription)
-end
-function fmi3IsModelExchange(fmu::FMU3)
- fmi3IsModelExchange(fmu.modelDescription)
-end
-function fmi3IsScheduledExecution(fmu::FMU3)
- fmi3IsScheduledExecution(fmu.modelDescription)
-end
+using ZipFile, EzXML
-"""
-Returns an array of ValueReferences coresponding to the variable names.
-"""
-function fmi3String2ValueReference(md::fmi3ModelDescription, names::Array{String})
- vr = Array{fmi3ValueReference}(undef,0)
- for name in names
- reference = fmi3String2ValueReference(md, name)
- if reference === nothing
- @warn "Value reference for variable '$name' not found, skipping."
- else
- push!(vr, reference)
- end
- end
- vr
-end
+"""
+Returns how a variable depends on another variable based on the model description.
"""
-Returns the ValueReference coresponding to the variable name.
-"""
-function fmi3String2ValueReference(md::fmi3ModelDescription, name::String)
- reference = nothing
- if haskey(md.stringValueReferences, name)
- reference = md.stringValueReferences[name]
- else
- @warn "No variable named '$name' found."
- end
- reference
-end
-
-function fmi3String2ValueReference(fmu::FMU3, name::Union{String, Array{String}})
- fmi3String2ValueReference(fmu.modelDescription, name)
+function fmi3VariableDependsOnVariable(fmu::FMU3, vr1::fmi3ValueReference, vr2::fmi3ValueReference)
+ i1 = fmu.modelDescription.valueReferenceIndicies[vr1]
+ i2 = fmu.modelDescription.valueReferenceIndicies[vr2]
+ return fmi3GetDependencies(fmu)[i1, i2]
end
"""
-Returns an array of variable names matching a fmi3ValueReference.
-"""
-function fmi3ValueReference2String(md::fmi3ModelDescription, reference::fmi3ValueReference)
- [k for (k,v) in md.stringValueReferences if v == reference]
-end
-function fmi3ValueReference2String(md::fmi3ModelDescription, reference::Int64)
- fmi3ValueReference2String(md, fmi3ValueReference(reference))
-end
-
-function fmi3ValueReference2String(fmu::FMU3, reference::Union{fmi3ValueReference, Int64})
- fmi3ValueReference2String(fmu.modelDescription, reference)
-end
+Returns the FMU's dependency-matrix for fast look-ups on dependencies between value references.
+Entries are from type fmi3DependencyKind.
"""
-Source: FMISpec3.0, Version D5ef1c1:: 2.3.1. Super State: FMU State Setable
+function fmi3GetDependencies(fmu::FMU3)
+ if !isdefined(fmu, :dependencies)
+ dim = length(fmu.modelDescription.valueReferences)
+ @info "fmi3GetDependencies: Started building dependency matrix $(dim) x $(dim) ..."
-Create a new instance of the given fmu, adds a logger if logginOn == true.
+ if fmi3DependenciesSupported(fmu.modelDescription)
+ fmu.dependencies = fill(nothing, dim, dim)
-Returns the instance of a new FMU component.
+ for i in 1:dim
+ modelVariable = fmi3ModelVariablesForValueReference(fmu.modelDescription, fmu.modelDescription.valueReferences[i])[1]
+
+ if modelVariable.dependencies !== nothing
+ indicies = collect(fmu.modelDescription.valueReferenceIndicies[fmu.modelDescription.modelVariables[dependency].valueReference] for dependency in modelVariable.dependencies)
+ dependenciesKind = modelVariable.dependenciesKind
+
+ k = 1
+ for j in 1:dim
+ if j in indicies
+ if dependenciesKind[k] == "fixed"
+ fmu.dependencies[i,j] = fmi3DependencyKindFixed
+ elseif dependenciesKind[k] == "dependent"
+ fmu.dependencies[i,j] = fmi3DependencyKindDependent
+ else
+ @warn "Unknown dependency kind for index ($i, $j) = `$(dependenciesKind[k])`."
+ end
+ k += 1
+ end
+ end
+ end
+ end
+ else
+ fmu.dependencies = fill(nothing, dim, dim)
+ end
-For more information call ?fmi3InstantiateModelExchange
-"""
-function fmi3InstantiateModelExchange!(fmu::FMU3; visible::Bool = false, loggingOn::Bool = false)
+ @info "fmi3GetDependencies: Building dependency matrix $(dim) x $(dim) finished."
+ end
- ptrLogger = @cfunction(fmi3CallbackLogger, Cvoid, (Ptr{Cvoid}, Ptr{Cchar}, Cuint, Ptr{Cchar}))
+ fmu.dependencies
+end
- compAddr = fmi3InstantiateModelExchange(fmu.cInstantiateModelExchange, fmu.instanceName, fmu.modelDescription.instantiationToken, fmu.fmuResourceLocation, fmi3Boolean(visible), fmi3Boolean(loggingOn), fmu.instanceEnvironment, ptrLogger)
+function fmi3PrintDependencies(fmu::FMU2)
+ dep = fmi3GetDependencies(fmu)
+ ni, nj = size(dep)
- if compAddr == Ptr{Cvoid}(C_NULL)
- @error "fmi3InstantiateModelExchange!(...): Instantiation failed!"
- return nothing
+ for i in 1:ni
+ str = ""
+ for j in 1:nj
+ str = "$(str) $(Integer(dep[i,j]))"
+ end
+ println(str)
end
- previous_z = zeros(fmi3Float64, fmi3GetEventIndicators(fmu.modelDescription))
- rootsFound = zeros(fmi3Int32, fmi3GetEventIndicators(fmu.modelDescription))
- stateEvent = fmi3False
- timeEvent = fmi3False
- stepEvent = fmi3False
- component = fmi3Component(compAddr, fmu, previous_z, rootsFound, stateEvent, timeEvent, stepEvent)
- push!(fmu.components, component)
- component
end
"""
-Source: FMISpec3.0, Version D5ef1c1:: 2.3.1. Super State: FMU State Setable
-
-Create a new instance of the given fmu, adds a logger if logginOn == true.
-
-Returns the instance of a new FMU component.
-
-For more information call ?fmi3InstantiateCoSimulation
+Prints FMU related information.
"""
-function fmi3InstantiateCoSimulation!(fmu::FMU3; visible::Bool = false, loggingOn::Bool = false, eventModeUsed::Bool = false, ptrIntermediateUpdate=nothing)
+function fmi3Info(fmu::FMU3)
+ println("#################### Begin information for FMU ####################")
- ptrLogger = @cfunction(fmi3CallbackLogger, Cvoid, (Ptr{Cvoid}, Ptr{Cchar}, Cuint, Ptr{Cchar}))
- if ptrIntermediateUpdate === nothing
- ptrIntermediateUpdate = @cfunction(fmi3CallbackIntermediateUpdate, Cvoid, (Ptr{Cvoid}, fmi3Float64, fmi3Boolean, fmi3Boolean, fmi3Boolean, fmi3Boolean, Ptr{fmi3Boolean}, Ptr{fmi3Float64}))
- end
- if fmu.modelDescription.CShasEventMode
- mode = eventModeUsed
- else
- mode = false
+ println("\tModel name:\t\t\t$(fmi3GetModelName(fmu))")
+ println("\tFMI-Version:\t\t\t$(fmi3GetVersion(fmu))")
+ println("\tInstantiation Token:\t\t\t\t$(fmi3GetInstantiationToken(fmu))")
+ println("\tGeneration tool:\t\t$(fmi3GetGenerationTool(fmu))")
+ println("\tGeneration time:\t\t$(fmi3GetGenerationDateAndTime(fmu))")
+ print("\tVar. naming conv.:\t\t")
+ if fmi3GetVariableNamingConvention(fmu) == fmi3VariableNamingConventionFlat
+ println("flat")
+ elseif fmi3GetVariableNamingConvention(fmu) == fmi3VariableNamingConventionStructured
+ println("structured")
+ else
+ println("[unknown]")
end
+ println("\tEvent indicators:\t\t$(fmi3GetNumberOfEventIndicators(fmu))")
- compAddr = fmi3InstantiateCoSimulation(fmu.cInstantiateCoSimulation, fmu.instanceName, fmu.modelDescription.instantiationToken, fmu.fmuResourceLocation, fmi3Boolean(visible), fmi3Boolean(loggingOn),
- fmi3Boolean(mode), fmi3Boolean(fmu.modelDescription.CScanReturnEarlyAfterIntermediateUpdate), fmu.modelDescription.intermediateUpdateValueReferences, Csize_t(length(fmu.modelDescription.intermediateUpdateValueReferences)), fmu.instanceEnvironment, ptrLogger, ptrIntermediateUpdate)
-
- if compAddr == Ptr{Cvoid}(C_NULL)
- @error "fmi3InstantiateCoSimulation!(...): Instantiation failed!"
- return nothing
+ println("\tInputs:\t\t\t\t$(length(fmu.modelDescription.inputValueReferences))")
+ for vr in fmu.modelDescription.inputValueReferences
+ println("\t\t$(vr) $(fmi3ValueReferenceToString(fmu, vr))")
end
- component = fmi3Component(compAddr, fmu)
- push!(fmu.components, component)
- component
-end
-
-# TODO not tested
-"""
-Source: FMISpec3.0, Version D5ef1c1:: 2.3.1. Super State: FMU State Setable
-
-Create a new instance of the given fmu, adds a logger if logginOn == true.
-
-Returns the instance of a new FMU component.
-
-For more information call ?fmi3InstantiateScheduledExecution
-"""
-function fmi3InstantiateScheduledExecution!(fmu::FMU3, ptrlockPreemption::Ptr{Cvoid}, ptrunlockPreemption::Ptr{Cvoid}; visible::Bool = false, loggingOn::Bool = false)
-
- ptrLogger = @cfunction(fmi3CallbackLogger, Cvoid, (Ptr{Cvoid}, Ptr{Cchar}, Cuint, Ptr{Cchar}))
- ptrClockUpdate = @cfunction(fmi3CallbackClockUpdate, Cvoid, (Ptr{Cvoid}, ))
-
- compAddr = fmi3InstantiateScheduledExecution(fmu.cInstantiateScheduledExecution, fmu.instanceName, fmu.modelDescription.instantiationToken, fmu.fmuResourceLocation, fmi3Boolean(visible), fmi3Boolean(loggingOn), fmu.instanceEnvironment, ptrLogger, ptrClockUpdate, ptrlockPreemption, ptrunlockPreemption)
-
- if compAddr == Ptr{Cvoid}(C_NULL)
- @error "fmi3InstantiateScheduledExecution!(...): Instantiation failed!"
- return nothing
+ println("\tOutputs:\t\t\t$(length(fmu.modelDescription.outputValueReferences))")
+ for vr in fmu.modelDescription.outputValueReferences
+ println("\t\t$(vr) $(fmi3ValueReferenceToString(fmu, vr))")
end
- component = fmi3Component(compAddr, fmu)
- push!(fmu.components, component)
- component
-end
-
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.4. Inquire Version Number of Header Files
-
-Returns the version of the FMI Standard used in this FMU.
-
-For more information call ?fmi3GetVersion
-"""
-function fmi3GetVersion(fmu::FMU3)
- fmi3GetVersion(fmu.cGetVersion)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.1. Super State: FMU State Setable
-
-Sets debug logging for the FMU.
-
-For more information call ?fmi3SetDebugLogging
-"""
-function fmi3SetDebugLogging(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetDebugLogging(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.2. State: Instantiated
-
-FMU enters Initialization mode.
-
-For more information call ?fmi3EnterInitializationMode
-"""
-function fmi3EnterInitializationMode(fmu::FMU3, startTime::Real = 0.0, stopTime::Real = startTime; tolerance::Real = 0.0)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3EnterInitializationMode(fmu.components[end], startTime, stopTime; tolerance = tolerance)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.3. State: Initialization Mode
-
-FMU exits Initialization mode.
-
-For more information call ?fmi3ExitInitializationMode
-"""
-function fmi3ExitInitializationMode(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3ExitInitializationMode(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.4. Super State: Initialized
-
-Informs FMU that simulation run is terminated.
-
-For more information call ?fmi3Terminate
-"""
-function fmi3Terminate(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3Terminate(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.1. Super State: FMU State Setable
-
-Resets FMU.
-
-For more information call ?fmi3Reset
-"""
-function fmi3Reset(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3Reset(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Float32 variables.
-
-For more information call ?fmi3GetFloat32
-"""
-function fmi3GetFloat32(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetFloat32(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Float32 variables.
-
-For more information call ?fmi3GetFloat32!
-"""
-function fmi3GetFloat32!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Float32}, fmi3Float32})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetFloat32!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Float32 variables.
-
-For more information call ?fmi3SetFloat32
-"""
-function fmi3SetFloat32(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Float32}, fmi3Float32})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetFloat32(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Float64 variables.
-
-For more information call ?fmi3GetFloat64
-"""
-function fmi3GetFloat64(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetFloat64(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Float64 variables.
-
-For more information call ?fmi3GetFloat64!
-"""
-function fmi3GetFloat64!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Float64}, fmi3Float64})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetFloat64!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Float64 variables.
-
-For more information call ?fmi3SetFloat64
-"""
-function fmi3SetFloat64(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Float64}, fmi3Float64})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetFloat64(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Int8 variables.
-
-For more information call ?fmi3GetInt8
-"""
-function fmi3GetInt8(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetInt8(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Int8 variables.
-
-For more information call ?fmi3GetInt8!
-"""
-function fmi3GetInt8!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int8}, fmi3Int8})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetInt8!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Int8 variables.
-
-For more information call ?fmi3SetInt8
-"""
-function fmi3SetInt8(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int8}, fmi3Int8})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetInt8(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3UInt8 variables.
-
-For more information call ?fmi3GetUInt8
-"""
-function fmi3GetUInt8(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetUInt8(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3UInt8 variables.
-
-For more information call ?fmi3GetUInt8!
-"""
-function fmi3GetUInt8!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt8}, fmi3UInt8})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetUInt8!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3UInt8 variables.
-
-For more information call ?fmi3SetUInt8
-"""
-function fmi3SetUInt8(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt8}, fmi3UInt8})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetUInt8(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Int16 variables.
-
-For more information call ?fmi3GetInt16
-"""
-function fmi3GetInt16(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetInt16(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Int16 variables.
-
-For more information call ?fmi3GetInt16!
-"""
-function fmi3GetInt16!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int16}, fmi3Int16})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetInt16!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Int16 variables.
-
-For more information call ?fmi3SetInt16
-"""
-function fmi3SetInt16(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int16}, fmi3Int16})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetInt16(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3UInt16 variables.
-
-For more information call ?fmi3GetUInt16
-"""
-function fmi3GetUInt16(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetUInt16(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3UInt16 variables.
-
-For more information call ?fmi3GetUInt16!
-"""
-function fmi3GetUInt16!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt16}, fmi3UInt16})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetUInt16!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3UInt16 variables.
-
-For more information call ?fmi3SetUInt16
-"""
-function fmi3SetUInt16(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt16}, fmi3UInt16})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetUInt16(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Int32 variables.
-
-For more information call ?fmi3GetInt32
-"""
-function fmi3GetInt32(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetInt32(fmu.components[end], vr)
-end
+ println("\tStates:\t\t\t\t$(length(fmu.modelDescription.stateValueReferences))")
+ for vr in fmu.modelDescription.stateValueReferences
+ println("\t\t$(vr) $(fmi3ValueReferenceToString(fmu, vr))")
+ end
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
+ println("\tSupports Co-Simulation:\t\t$(fmi3IsCoSimulation(fmu))")
+ if fmi3IsCoSimulation(fmu)
+ println("\t\tModel identifier:\t$(fmu.modelDescription.coSimulation.modelIdentifier)")
+ println("\t\tGet/Set State:\t\t$(fmu.modelDescription.coSimulation.canGetAndSetFMUstate)")
+ println("\t\tSerialize State:\t$(fmu.modelDescription.coSimulation.canSerializeFMUstate)")
+ println("\t\tDir. Derivatives:\t$(fmu.modelDescription.coSimulation.providesDirectionalDerivatives)")
+ println("\t\tAdj. Derivatives:\t$(fmu.modelDescription.coSimulation.providesAdjointDerivatives)")
+ println("\t\tEvent Mode:\t$(fmu.modelDescription.coSimulation.hasEventMode)")
+
+ println("\t\tVar. com. steps:\t$(fmu.modelDescription.coSimulation.canHandleVariableCommunicationStepSize)")
+ println("\t\tInput interpol.:\t$(fmu.modelDescription.coSimulation.canInterpolateInputs)")
+ println("\t\tMax order out. der.:\t$(fmu.modelDescription.coSimulation.maxOutputDerivativeOrder)")
+ end
-Get the values of an array of fmi3Int32 variables.
+ println("\tSupports Model-Exchange:\t$(fmi3IsModelExchange(fmu))")
+ if fmi3IsModelExchange(fmu)
+ println("\t\tModel identifier:\t$(fmu.modelDescription.modelExchange.modelIdentifier)")
+ println("\t\tGet/Set State:\t\t$(fmu.modelDescription.modelExchange.canGetAndSetFMUstate)")
+ println("\t\tSerialize State:\t$(fmu.modelDescription.modelExchange.canSerializeFMUstate)")
+ println("\t\tDir. Derivatives:\t$(fmu.modelDescription.modelExchange.providesDirectionalDerivatives)")
+ println("\t\tAdj. Derivatives:\t$(fmu.modelDescription.modelExchange.providesAdjointDerivatives)")
+ end
-For more information call ?fmi3GetInt32!
-"""
-function fmi3GetInt32!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int32}, fmi3Int32})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ println("\tSupports Scheduled-Execution:\t$(fmi3IsScheduledExecution(fmu))")
+ if fmi3IsScheduledExecution(fmu)
+ println("\t\tModel identifier:\t$(fmu.modelDescription.scheduledExecution.modelIdentifier)")
+ println("\t\tGet/Set State:\t\t$(fmu.modelDescription.scheduledExecution.canGetAndSetFMUstate)")
+ println("\t\tSerialize State:\t$(fmu.modelDescription.scheduledExecution.canSerializeFMUstate)")
+ println("\t\tNeeds Execution Tool:\t$(fmu.modelDescription.scheduledExecution.needsExecutionTool)")
+ println("\t\tInstantiated Once Per Process:\t$(fmu.modelDescription.scheduledExecution.canBeInstantiatedOnlyOncePerProcess)")
+ println("\t\tPer Element Dependencies:\t$(fmu.modelDescription.scheduledExecution.providesPerElementDependencies)")
+
+ println("\t\tDir. Derivatives:\t$(fmu.modelDescription.scheduledExecution.providesDirectionalDerivatives)")
+ println("\t\tAdj. Derivatives:\t$(fmu.modelDescription.scheduledExecution.providesAdjointDerivatives)")
+ end
- fmi3GetInt32!(fmu.components[end], vr, values)
+ println("##################### End information for FMU #####################")
end
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
+function fmiCheckVersion(pathToFMU::String; unpackPath=nothing)
+ # Unzip MD
-Set the values of an array of fmi3Int32 variables.
+ # Download FMU if necessary
+ if startswith(pathToFMU, "http")
+ @info "Downloading FMU for Version extraction from `$(pathToFMU)`."
+ pathToFMU = download(pathToFMU)
+ end
-For more information call ?fmi3SetInt32
-"""
-function fmi3SetInt32(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int32}, fmi3Int32})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ pathToFMU = normpath(pathToFMU)
- fmi3SetInt32(fmu.components[end], vr, values)
-end
+ fileNameExt = basename(pathToFMU)
+ (fileName, fileExt) = splitext(fileNameExt)
+
+ if unpackPath === nothing
+ # cleanup=true leads to issues with automatic testing on linux server.
+ unpackPath = mktempdir(; prefix="fmijl_", cleanup=false)
+ end
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
+ zipPath = joinpath(unpackPath, fileName * ".zip")
+ unzippedPath = joinpath(unpackPath, fileName)
-Get the values of an array of fmi3UInt32 variables.
+ # only copy ZIP if not already there
+ if !isfile(zipPath)
+ cp(pathToFMU, zipPath; force=true)
+ end
-For more information call ?fmi3GetUInt32
-"""
-function fmi3GetUInt32(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ @assert isfile(zipPath) ["fmi3Unzip(...): ZIP-Archive couldn't be copied to `$zipPath`."]
- fmi3GetUInt32(fmu.components[end], vr)
-end
+ zipAbsPath = isabspath(zipPath) ? zipPath : joinpath(pwd(), zipPath)
+ unzippedAbsPath = isabspath(unzippedPath) ? unzippedPath : joinpath(pwd(), unzippedPath)
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
+ @assert isfile(zipAbsPath) ["fmi3Unzip(...): Can't deploy ZIP-Archive at `$(zipAbsPath)`."]
-Get the values of an array of fmi3UInt32 variables.
+ # only unzip if not already done
+ if !isdir(unzippedAbsPath)
+ mkpath(unzippedAbsPath)
-For more information call ?fmi3GetUInt32!
-"""
-function fmi3GetUInt32!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt32}, fmi3UInt32})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ zarchive = ZipFile.Reader(zipAbsPath)
+ for f in zarchive.files
+ if f.name == "modelDescription.xml"
+ fileAbsPath = normpath(joinpath(unzippedAbsPath, f.name))
- fmi3GetUInt32!(fmu.components[end], vr, values)
-end
+ # create directory if not forced by zip file folder
+ mkpath(dirname(fileAbsPath))
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
+ numBytes = write(fileAbsPath, read(f))
+
+ if numBytes == 0
+ @info "fmi3Unzip(...): Written file `$(f.name)`, but file is empty."
+ end
-Set the values of an array of fmi3UInt32 variables.
+ @assert isfile(fileAbsPath) ["fmi3Unzip(...): Can't unzip file `$(f.name)` at `$(fileAbsPath)`."]
+ end
+
+ end
+ close(zarchive)
+ end
-For more information call ?fmi3SetUInt32
-"""
-function fmi3SetUInt32(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt32}, fmi3UInt32})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ @assert isdir(unzippedAbsPath) ["fmi3Unzip(...): ZIP-Archive couldn't be unzipped at `$(unzippedPath)`."]
+ @info "fmiUnzipVersion(...): Successfully unzipped modelDescription.xml at `$unzippedAbsPath`."
- fmi3SetUInt32(fmu.components[end], vr, values)
-end
+ # read version tag
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
+ doc = readxml(normpath(joinpath(unzippedAbsPath, "modelDescription.xml")))
-Get the values of an array of fmi3Int64 variables.
+ root = doc.root
+ version = root["fmiVersion"]
-For more information call ?fmi3GetInt64
-"""
-function fmi3GetInt64(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ # cleanup unzipped modelDescription
+ try
+ rm(unzippedAbsPath; recursive = true, force = true)
+ rm(zipAbsPath; recursive = true, force = true)
+ catch e
+ @warn "Cannot delete unpacked data on disc. Maybe some files are opened in another application."
+ end
- fmi3GetInt64(fmu.components[end], vr)
+ # return version
+ version
end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Int64 variables.
-
-For more information call ?fmi3GetInt64!
-"""
-function fmi3GetInt64!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int64}, fmi3Int64})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetInt64!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Int64 variables.
-
-For more information call ?fmi3SetInt64
-"""
-function fmi3SetInt64(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Int64}, fmi3Int64})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetInt64(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3UInt64 variables.
-
-For more information call ?fmi3GetUInt64
-"""
-function fmi3GetUInt64(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetUInt64(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3UInt64 variables.
-
-For more information call ?fmi3GetUInt64!
-"""
-function fmi3GetUInt64!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt64}, fmi3UInt64})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetUInt64!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3UInt64 variables.
-
-For more information call ?fmi3SetUInt64
-"""
-function fmi3SetUInt64(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3UInt64}, fmi3UInt64})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetUInt64(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Boolean variables.
-
-For more information call ?fmi3GetBoolean
-"""
-function fmi3GetBoolean(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetBoolean(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Boolean variables.
-
-For more information call ?fmi3GetBoolean!
-"""
-function fmi3GetBoolean!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{Bool}, Bool, Array{fmi3Boolean}})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetBoolean!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Boolean variables.
-
-For more information call ?fmi3SetBoolean
-"""
-function fmi3SetBoolean(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{Bool}, Bool})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetBoolean(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3String variables.
-
-For more information call ?fmi3GetString
-"""
-function fmi3GetString(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetString(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3String variables.
-
-For more information call ?fmi3GetString!
-"""
-function fmi3GetString!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{String}, String})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetString!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3String variables.
-
-For more information call ?fmi3SetString
-"""
-function fmi3SetString(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{String}, String})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetString(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Binary variables.
-
-For more information call ?fmi3GetBinary
-"""
-function fmi3GetBinary(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetBinary(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Binary variables.
-
-For more information call ?fmi3GetBinary!
-"""
-function fmi3GetBinary!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Binary}, fmi3Binary})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetBinary!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Binary variables.
-
-For more information call ?fmi3SetBinary
-"""
-function fmi3SetBinary(fmu::FMU3, vr::fmi3ValueReferenceFormat, valueSizes::Union{Array{Csize_t}, Csize_t}, values::Union{Array{fmi3Binary}, fmi3Binary})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetBinary(fmu.components[end], vr, valueSizes, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Clock variables.
-
-For more information call ?fmi3GetClock
-"""
-function fmi3GetClock(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetClock(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Get the values of an array of fmi3Clock variables.
-
-For more information call ?fmi3GetClock!
-"""
-function fmi3GetClock!(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Clock}, fmi3Clock})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetClock!(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.2. Getting and Setting Variable Values
-
-Set the values of an array of fmi3Clock variables.
-
-For more information call ?fmi3SetClock
-"""
-function fmi3SetClock(fmu::FMU3, vr::fmi3ValueReferenceFormat, values::Union{Array{fmi3Clock}, fmi3Clock})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetClock(fmu.components[end], vr, values)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.4. Getting and Setting the Complete FMU State
-
-Get the pointer to the current FMU state.
-
-For more information call ?fmi3GetFMUState
-"""
-function fmi3GetFMUState(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetFMUState(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.4. Getting and Setting the Complete FMU State
-
-Set the FMU to the given fmi3FMUstate.
-
-For more information call ?fmi3SetFMUState
-"""
-function fmi3SetFMUState(fmu::FMU3, state::fmi3FMUState)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SetFMUState(fmu.components[end], state)
-end
-
-"""
-function fmi3FreeFMUState(c::fmi3Component, FMUstate::Ref{fmi3FMUState})
-
-Free the allocated memory for the FMU state.
-
-For more information call ?fmi3FreeFMUState
-"""
-function fmi3FreeFMUState(fmu::FMU3, state::fmi3FMUState)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- stateRef = Ref(state)
- fmi3FreeFMUState(fmu.components[end], stateRef)
- state = stateRef[]
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.4. Getting and Setting the Complete FMU State
-
-Returns the size of a byte vector the FMU can be stored in.
-
-For more information call ?fmi3SerzializedFMUStateSize
-"""
-function fmi3SerializedFMUStateSize(fmu::FMU3, state::fmi3FMUState)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SerializedFMUStateSize(fmu.components[end], state)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.4. Getting and Setting the Complete FMU State
-
-Serialize the data in the FMU state pointer.
-
-For more information call ?fmi3SerializeFMUState
-"""
-function fmi3SerializeFMUState(fmu::FMU3, state::fmi3FMUState)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SerializeFMUState(fmu.components[end], state)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.6.4. Getting and Setting the Complete FMU State
-
-Deserialize the data in the serializedState fmi3Byte field.
-
-For more information call ?fmi3DeSerializeFMUState
-"""
-function fmi3DeSerializeFMUState(fmu::FMU3, serializedState::Array{fmi3Byte})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3DeSerializeFMUState(fmu.components[end], serializedState)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.11. Getting Partial Derivatives
-
-Retrieves directional derivatives.
-
-For more information call ?fmi3GetDirectionalDerivative
-"""
-function fmi3GetDirectionalDerivative(fmu::FMU3,
- unknowns::fmi3ValueReference,
- knowns::fmi3ValueReference,
- seed::fmi3Float64 = 1.0)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetDirectionalDerivative(fmu.components[end], unknowns, knowns, seed)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.11. Getting Partial Derivatives
-
-Retrieves directional derivatives.
-
-For more information call ?fmi3GetDirectionalDerivative
-"""
-function fmi3GetDirectionalDerivative(fmu::FMU3,
- unknowns::Array{fmi3ValueReference},
- knowns::Array{fmi3ValueReference},
- seed::Array{fmi3Float64} = Array{fmi3Float64}([]))
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetDirectionalDerivative(fmu.components[end], unknowns, knowns, seed)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.11. Getting Partial Derivatives
-
-Retrieves directional derivatives in-place.
-
-For more information call ?fmi3GetDirectionalDerivative
-"""
-function fmi3GetDirectionalDerivative!(fmu::FMU3,
- unknowns::Array{fmi3ValueReference},
- knowns::Array{fmi3ValueReference},
- sensitivity::Array{fmi3Float64},
- seed::Array{fmi3Float64} = Array{fmi3Float64}([]))
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetDirectionalDerivative!(fmu.components[end], unknowns, knowns, sensitivity, seed)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.11. Getting Partial Derivatives
-
-Retrieves adjoint derivatives.
-
-For more information call ?fmi3GetAdjointDerivative
-"""
-function fmi3GetAdjointDerivative(fmu::FMU3,
- unknowns::fmi3ValueReference,
- knowns::fmi3ValueReference,
- seed::fmi3Float64 = 1.0)
-
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetAdjointDerivative(fmu.components[end], unknowns, knowns, seed)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.11. Getting Partial Derivatives
-
-Retrieves adjoint derivatives.
-
-For more information call ?fmi3GetAdjointDerivative
-"""
-function fmi3GetAdjointDerivative(fmu::FMU3,
- unknowns::Array{fmi3ValueReference},
- knowns::Array{fmi3ValueReference},
- seed::Array{fmi3Float64} = Array{fmi3Float64}([]))
-
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetAdjointDerivative(fmu.components[end], unknowns, knowns, seed)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.11. Getting Partial Derivatives
-
-Retrieves adjoint derivatives.
-
-For more information call ?fmi3GetAdjointDerivative
-"""
-function fmi3GetAdjointDerivative!(fmu::FMU3,
- unknowns::Array{fmi3ValueReference},
- knowns::Array{fmi3ValueReference},
- sensitivity::Array{fmi3Float64},
- seed::Array{fmi3Float64} = Array{fmi3Float64}([]))
-
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetAdjointDerivative!(fmu.components[end], unknowns, knowns, sensitivity, seed)
-end
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.12. Getting Derivatives of Continuous Outputs
-
-Retrieves the n-th derivative of output values.
-
-vr defines the value references of the variables
-the array order specifies the corresponding order of derivation of the variables
-
-For more information call ?fmi3GetOutputDerivatives
-"""
-function fmi3GetOutputDerivatives(fmu::FMU3, vr::fmi3ValueReferenceFormat, order::Array{Integer})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetOutputDerivatives(fmu.components[end], vr, order)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.12. Getting Derivatives of Continuous Outputs
-
-Retrieves the n-th derivative of output values.
-
-vr defines the value references of the variables
-the array order specifies the corresponding order of derivation of the variables
-
-For more information call ?fmi3GetOutputDerivatives
-"""
-function fmi3GetOutputDerivatives(fmu::FMU3, vr::fmi3ValueReference, order::Integer)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetOutputDerivatives(fmu.components[end], vr, order)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.2. State: Instantiated
-
-If the importer needs to change structural parameters, it must move the FMU into Configuration Mode using fmi3EnterConfigurationMode.
-For more information call ?fmi3EnterConfigurationMode
-"""
-function fmi3EnterConfigurationMode(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3EnterConfigurationMode(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.2. State: Instantiated
-
-This function returns the number of continuous states.
-This function can only be called in Model Exchange.
-For more information call ?fmi3GetNumberOfContinuousStates
-"""
-function fmi3GetNumberOfContinuousStates(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetNumberOfContinuousStates(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.2. State: Instantiated
-
-This function returns the number of event indicators.
-This function can only be called in Model Exchange.
-For more information call ?fmi3GetNumberOfEventIndicators
-"""
-function fmi3GetNumberOfEventIndicators(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetNumberOfEventIndicators(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.10. Dependencies of Variables
-
-The number of dependencies of a given variable, which may change if structural parameters are changed, can be retrieved by calling the following function:
-For more information call ?fmi3GetNumberOfVariableDependencies
-"""
-function fmi3GetNumberOfVariableDependencies(fmu::FMU3, vr::Union{fmi3ValueReference, String})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetNumberOfVariableDependencies(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.3. State: Initialization Mode
-
-Return the states at the current time instant.
-For more information call ?fmi3GetContinuousStates
-"""
-function fmi3GetContinuousStates(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetContinuousStates(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.2.10. Dependencies of Variables
-
-The dependencies (of type dependenciesKind) can be retrieved by calling the function fmi3GetVariableDependencies.
-For more information call ?fmi3GetVariableDependencies
-"""
-function fmi3GetVariableDependencies(fmu::FMU3, vr::Union{fmi3ValueReference, String})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetVariableDependencies(fmu.components[end], vr)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.3. State: Initialization Mode
-
-Return the nominal values of the continuous states.
-
-For more information call ?fmi3GetNominalsOfContinuousStates
-"""
-function fmi3GetNominalsOfContinuousStates(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetNominalsOfContinuousStates(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.3. State: Initialization Mode
-
-This function is called to trigger the evaluation of fdisc to compute the current values of discrete states from previous values.
-The FMU signals the support of fmi3EvaluateDiscreteStates via the capability flag providesEvaluateDiscreteStates.
-
-For more information call ?fmi3EvaluateDiscreteStates
-"""
-function fmi3EvaluateDiscreteStates(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3EvaluateDiscreteStates(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.5. State: Event Mode
-
-This function is called to signal a converged solution at the current super-dense time instant. fmi3UpdateDiscreteStates must be called at least once per super-dense time instant.
-
-For more information call ?fmi3UpdateDiscreteStates
-"""
-function fmi3UpdateDiscreteStates(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3UpdateDiscreteStates(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.5. State: Event Mode
-
-The model enters Continuous-Time Mode.
-
-For more information call ?fmi3EnterContinuousTimeMode
-"""
-function fmi3EnterContinuousTimeMode(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3EnterContinuousTimeMode(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.5. State: Event Mode
-
-This function must be called to change from Event Mode into Step Mode in Co-Simulation.
-
-For more information call ?fmi3EnterStepMode
-"""
-function fmi3EnterStepMode(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3EnterStepMode(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 2.3.6. State: Configuration Mode
-
-Exits the Configuration Mode and returns to state Instantiated.
-
-For more information call ?fmi3ExitConfigurationMode
-"""
-function fmi3ExitConfigurationMode(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3ExitConfigurationMode(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.2.1. State: Continuous-Time Mode
-
-Set independent variable time and reinitialize chaching of variables that depend on time.
-
-For more information call ?fmi3SetTime
-"""
-function fmi3SetTime(fmu::FMU3, time::Real)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmu.t = time
- fmi3SetTime(fmu.components[end], fmi3Float64(time))
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.2.1. State: Continuous-Time Mode
-
-Set a new (continuous) state vector and reinitialize chaching of variables that depend on states.
-
-For more information call ?fmi3SetContinuousStates
-"""
-function fmi3SetContinuousStates(fmu::FMU3, x::Union{Array{Float32}, Array{Float64}})
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- nx = Csize_t(length(x))
- fmu.x = x
- fmi3SetContinuousStates(fmu.components[end], Array{fmi3Float64}(x), nx)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.2.1. State: Continuous-Time Mode
-
-Compute state derivatives at the current time instant and for the current states.
-
-For more information call ?fmi3GetContinuousStateDerivatives
-"""
-function fmi3GetContinuousStateDerivatives(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetContinuousStateDerivatives(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.2.1. State: Continuous-Time Mode
-
-Returns the event indicators of the FMU.
-
-For more information call ?fmi3GetEventIndicators
-"""
-function fmi3GetEventIndicators(fmu::FMU3)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetEventIndicators(fmu.components[end])
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.2.1. State: Continuous-Time Mode
-
-This function must be called by the environment after every completed step
-If enterEventMode == fmi3True, the event mode must be entered
-If terminateSimulation == fmi3True, the simulation shall be terminated
-
-For more information call ?fmi3CompletedIntegratorStep
-"""
-function fmi3CompletedIntegratorStep(fmu::FMU3,
- noSetFMUStatePriorToCurrentPoint::fmi3Boolean)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3CompletedIntegratorStep(fmu.components[end], noSetFMUStatePriorToCurrentPoint)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.2.1. State: Continuous-Time Mode
-
-The model enters Event Mode.
-
-For more information call ?fmi3EnterEventMode
-"""
-function fmi3EnterEventMode(fmu::FMU3, stepEvent::Bool, stateEvent::Bool, rootsFound::Array{fmi3Int32}, nEventIndicators::Integer, timeEvent::Bool)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3EnterEventMode(fmu.components[end], stepEvent, stateEvent, rootsFound, nEventIndicators, timeEvent)
-end
-
-"""
-Source: FMISpec3.0, Version D5ef1c1: 4.2.1. State: Step Mode
-
-The computation of a time step is started.
-
-For more information call ?fmi3DoStep
-"""
-function fmi3DoStep(fmu::FMU3, currentCommunicationPoint::Real, communicationStepSize::Real, noSetFMUStatePriorToCurrentPoint::Bool, eventEncountered::fmi3Boolean, terminateSimulation::fmi3Boolean, earlyReturn::fmi3Boolean, lastSuccessfulTime::fmi3Float64)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- refeventEncountered = Ref(eventEncountered)
- refterminateSimulation = Ref(terminateSimulation)
- refearlyReturn = Ref(earlyReturn)
- reflastSuccessfulTime = Ref(lastSuccessfulTime)
- fmi3DoStep(fmu.components[end], fmi3Float64(currentCommunicationPoint), fmi3Float64(communicationStepSize), fmi3Boolean(noSetFMUStatePriorToCurrentPoint), refeventEncountered, refterminateSimulation, refearlyReturn, reflastSuccessfulTime)
- eventEncountered = refeventEncountered[]
- terminateSimulation = refterminateSimulation[]
- earlyReturn = refearlyReturn[]
- lastSuccessfulTime = reflastSuccessfulTime[]
-end
-
-"""
-Starts a simulation of the fmu instance for the matching fmu type. If both types are available, CS is preferred over ME.
-"""
-function fmi3Simulate(fmu::FMU3, t_start::Real = 0.0, t_stop::Real = 1.0;
- recordValues::fmi3ValueReferenceFormat = nothing, saveat=[], setup=true)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3Simulate(fmu.components[end], t_start, t_stop;
- recordValues=recordValues, saveat=saveat, setup=setup)
-end
-"""
-Starts a simulation of a FMU in CS-mode.
-"""
-function fmi3SimulateCS(fmu::FMU3, t_start::Real, t_stop::Real;
- recordValues::fmi3ValueReferenceFormat = nothing, saveat=[], setup=true)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SimulateCS(fmu.components[end], t_start, t_stop;
- recordValues=recordValues, saveat=saveat, setup=setup)
-end
-
-"""
-Starts a simulation of a FMU in ME-mode.
-"""
-function fmi3SimulateME(fmu::FMU3, t_start::Real, t_stop::Real; kwargs...)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3SimulateME(fmu.components[end], t_start, t_stop; kwargs...)
-end
-
-"""
-Returns the start/default value for a given value reference.
-
-TODO: Add this command in the documentation.
-"""
-function fmi3GetStartValue(fmu::FMU3, vr::fmi3ValueReferenceFormat)
- @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
-
- fmi3GetStartValue(fmu.components[end], vr)
-end
\ No newline at end of file
diff --git a/src/FMI3_comp_wraps.jl b/src/FMI3_comp_wraps.jl
index 1ed16699..4b769075 100644
--- a/src/FMI3_comp_wraps.jl
+++ b/src/FMI3_comp_wraps.jl
@@ -1,4 +1,899 @@
+# STATUS: no todos
+# ABM: done
+
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
-#
\ No newline at end of file
+#
+
+# What is included in the file `FMI3_comp_wraps.jl` (FMU instance wrappers)?
+# - wrappers to call fmi3InstanceFunctions from FMUs (FMI-functions, last instantiated instance is used) [exported]
+# - wrappers to call fmi3InstanceFunctions from FMUs (additional functions, last instantiated instance is used) [exported]
+
+# TODO why is this here?
+# using FMIImport: FMU3, fmi3ModelDescription
+# using FMIImport: fmi3Float32, fmi3Float64, fmi3Int8, fmi3Int16, fmi3Int32, fmi3Int64, fmi3Boolean, fmi3String, fmi3Binary, fmi3UInt8, fmi3UInt16, fmi3UInt32, fmi3UInt64, fmi3Byte
+# using FMIImport: fmi3Clock, fmi3FMUState
+# using FMIImport: fmi3CallbackLogger, fmi3CallbackIntermediateUpdate, fmi3CallbackClockUpdate
+
+# fmi-spec
+"""
+ fmi3FreeInstance!(fmu::FMU3)
+
+Wrapper for fmi3FreeInstance!() in FMIImport/FMI3_c.jl
+"""
+function fmi3FreeInstance!(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3FreeInstance!(fmu.instances[end]) # this command also removes the instance from the array
+end
+
+"""
+ fmi3SetDebugLogging(fmu::FMU3)
+
+Wrapper for fmi3SetDebugLogging() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetDebugLogging(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetDebugLogging(fmu.instances[end])
+end
+
+"""
+ fmi3EnterInitializationMode(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3EnterInitializationMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3EnterInitializationMode(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3EnterInitializationMode(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3ExitInitializationMode(fmu::FMU2)
+
+Wrapper for fmi3ExitInitializationMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3ExitInitializationMode(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3ExitInitializationMode(fmu.instances[end])
+end
+
+"""
+ fmi3Terminate(fmu::FMU3)
+
+Wrapper for fmi3Terminate() in FMIImport/FMI3_c.jl
+"""
+function fmi3Terminate(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3Terminate(fmu.instances[end])
+end
+
+"""
+ fmi3Reset(fmu::FMU3)
+
+Wrapper for fmi3Reset() in FMIImport/FMI3_c.jl
+"""
+function fmi3Reset(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3Reset(fmu.instances[end])
+end
+
+"""
+ fmi3GetFloat32(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetFloat32() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetFloat32(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetFloat32(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetFloat32!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetFloat32!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetFloat32!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetFloat32!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+fmi3SetFloat32(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetFloat32() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetFloat32(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetFloat32(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetFloat64(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetFloat64() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetFloat64(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetFloat64(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetFloat64!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetFloat64!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetFloat64!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetFloat64!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetFloat64(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetFloat64() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetFloat64(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetFloat64(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt8(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt8() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt8(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt8(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt8!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt8!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt8!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt8!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetInt8(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetInt8() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetInt8(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetInt8(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt8(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt8() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt8(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt8(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt8!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt8!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt8!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt8!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetUInt8(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetUInt8() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetUInt8(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetUInt8(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt16(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt16() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt16(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt16(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt16!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt16!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt16!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt16!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetInt16(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetInt16() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetInt16(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetInt16(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt16(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt16() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt16(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt16(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt16!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt16!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt16!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt16!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetUInt16(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetUInt16() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetUInt16(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetUInt16(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt32(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt32() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt32(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt32(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt32!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt32!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt32!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt32!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetInt32(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetInt32() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetInt32(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetInt32(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt32(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt32() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt32(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt32(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt32!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt32!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt32!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt32!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetUInt32(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetUInt32() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetUInt32(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetUInt32(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt64(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt64() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt64(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt64(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetInt64!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetInt64!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetInt64!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetInt64!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetInt64(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetInt64() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetInt64(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetInt64(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt64(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt64() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt64(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt64(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetUInt64!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetUInt64!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetUInt64!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetUInt64!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetUInt64(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetUInt64() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetUInt64(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetUInt64(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetBoolean(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetBoolean() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetBoolean(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetBoolean(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetBoolean!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetBoolean!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetBoolean!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetBoolean!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetBoolean(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetBoolean!() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetBoolean(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetBoolean(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetString(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetString() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetString(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetString(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetString!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetString!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetString!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetString!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetString(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetString() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetString(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetString(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetBinary(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetBinary() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetBinary(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetBinary(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetBinary!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetBinary!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetBinary!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetBinary!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetBinary(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetBinary() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetBinary(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetBinary(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetClock(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetClock() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetClock(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetClock(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetClock!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetClock!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetClock!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetClock!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetClock(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetClock() in FMIImport/FMI3_int.jl
+"""
+function fmi3SetClock(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetClock(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmiGet(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmiGet() in FMIImport/FMI3_ext.jl
+"""
+function fmiGet(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3Get(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmiGet!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmiGet!() in FMIImport/FMI3_ext.jl
+"""
+function fmiGet!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3Get!(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmiSet(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmiSet() in FMIImport/FMI3_ext.jl
+"""
+function fmiSet(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3Set(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetFMUstate(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetFMUstate() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetFMUState(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetFMUState(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetFMUstate(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetFMUstate() in FMIImport/FMI3_c.jl
+"""
+function fmi3SetFMUState(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetFMUState(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3FreeFMUState!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3FreeFMUState!() in FMIImport/FMI3_int.jl
+"""
+function fmi3FreeFMUState!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3FreeFMUState!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SerializedFMUStateSize(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SerializedFMUStateSize() in FMIImport/FMI3_int.jl
+"""
+function fmi3SerializedFMUStateSize(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SerializedFMUStateSize(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SerializeFMUState(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SerializeFMUState() in FMIImport/FMI3_int.jl
+"""
+function fmi3SerializeFMUState(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SerializeFMUState(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3DeSerializeFMUState(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3DeSerializeFMUState() in FMIImport/FMI3_int.jl
+"""
+function fmi3DeSerializeFMUState(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3DeSerializeFMUState(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetDirectionalDerivative(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetDirectionalDerivative() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetDirectionalDerivative(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetDirectionalDerivative(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetDirectionalDerivative!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetDirectionalDerivative!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetDirectionalDerivative!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetDirectionalDerivative!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetAdjointDerivative(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetAdjointDerivative() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetAdjointDerivative(fmu::FMU3, args...; kwargs...)
+
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetAdjointDerivative(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetAdjointDerivative!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetAdjointDerivative!() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetAdjointDerivative!(fmu::FMU3, args...; kwargs...)
+
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetAdjointDerivative!(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SampleDirectionalDerivative!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SampleDirectionalDerivative!() in FMIImport/FMI3_ext.jl
+"""
+function fmi3SampleDirectionalDerivative!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SampleDirectionalDerivative!(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmi3SampleDirectionalDerivative(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SampleDirectionalDerivative() in FMIImport/FMI3_ext.jl
+"""
+function fmi3SampleDirectionalDerivative(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SampleDirectionalDerivative(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetJacobian!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetJacobian!() in FMIImport/FMI3_ext.jl
+"""
+function fmi3GetJacobian!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetJacobian!(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetJacobian(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetJacobian() in FMIImport/FMI3_ext.jl
+"""
+function fmi3GetJacobian(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetJacobian(fmu.components[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetOutputDerivatives(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetOutputDerivatives() in FMIImport/FMI3_int.jl
+"""
+function fmi3GetOutputDerivatives(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetOutputDerivatives(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3EnterConfigurationMode(fmu::FMU3)
+
+Wrapper for fmi3EnterConfigurationMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3EnterConfigurationMode(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3EnterConfigurationMode(fmu.instances[end])
+end
+
+"""
+ fmi3GetNumberOfContinuousStates(fmu::FMU3)
+
+Wrapper for fmi3GetNumberOfContinuousStates() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetNumberOfContinuousStates(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetNumberOfContinuousStates(fmu.instances[end])
+end
+
+"""
+ fmi3GetNumberOfVariableDependencies(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetNumberOfVariableDependencies() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetNumberOfVariableDependencies(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetNumberOfVariableDependencies(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetVariableDependencies(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetVariableDependencies() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetVariableDependencies(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetVariableDependencies(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3GetContinuousStates(fmu::FMU3)
+
+Wrapper for fmi3GetContinuousStates() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetContinuousStates(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetContinuousStates(fmu.instances[end])
+end
+
+"""
+ fmi3GetNominalsOfContinuousStates(fmu::FMU3)
+
+Wrapper for fmi3GetNominalsOfContinuousStates() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetNominalsOfContinuousStates(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetNominalsOfContinuousStates(fmu.instances[end])
+end
+
+"""
+fmi3EvaluateDiscreteStates(fmu::FMU3)
+
+Wrapper for fmi3EvaluateDiscreteStates() in FMIImport/FMI3_c.jl
+"""
+function fmi3EvaluateDiscreteStates(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3EvaluateDiscreteStates(fmu.instances[end])
+end
+
+"""
+ fmi3UpdateDiscreteStates(fmu::FMU3)
+
+Wrapper for fmi3UpdateDiscreteStates() in FMIImport/FMI3_c.jl
+"""
+function fmi3UpdateDiscreteStates(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3UpdateDiscreteStates(fmu.instances[end])
+end
+
+"""
+ fmi3EnterContinuousTimeMode(fmu::FMU3)
+
+Wrapper for fmi3EnterContinuousTimeMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3EnterContinuousTimeMode(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3EnterContinuousTimeMode(fmu.instances[end])
+end
+
+"""
+ fmi3EnterStepMode(fmu::FMU3)
+
+Wrapper for fmi3EnterStepMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3EnterStepMode(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3EnterStepMode(fmu.instances[end])
+end
+
+"""
+ fmi3ExitConfigurationMode(fmu::FMU3)
+
+Wrapper for fmi3ExitConfigurationMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3ExitConfigurationMode(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3ExitConfigurationMode(fmu.instances[end])
+end
+
+"""
+ fmi3SetTime(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetTime() in FMIImport/FMI3_c.jl
+"""
+function fmi3SetTime(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetTime(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3SetContinuousStates(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SetContinuousStates() in FMIImport/FMI3_c.jl
+"""
+function fmi3SetContinuousStates(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3SetContinuousStates(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+fmi3GetContinuousStateDerivatives(fmu::FMU3)
+
+Wrapper for fmi3GetContinuousStateDerivatives() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetContinuousStateDerivatives(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetContinuousStateDerivatives(fmu.instances[end])
+end
+
+"""
+ fmi3GetEventIndicators(fmu::FMU3)
+
+Wrapper for fmi3GetEventIndicators() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetEventIndicators(fmu::FMU3)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetEventIndicators(fmu.instances[end])
+end
+
+"""
+fmi3CompletedIntegratorStep(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3CompletedIntegratorStep() in FMIImport/FMI3_c.jl
+"""
+function fmi3CompletedIntegratorStep(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3CompletedIntegratorStep(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3EnterEventMode(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3EnterEventMode() in FMIImport/FMI3_c.jl
+"""
+function fmi3EnterEventMode(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3EnterEventMode(fmu.instances[end], args...; kwargs...)
+end
+
+"""
+ fmi3DoStep!(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3DoStep!() in FMIImport/FMI3_c.jl
+"""
+function fmi3DoStep!(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3DoStep!(fmu.instances[end], args...; kwargs...)
+end
+
+#additional
+"""
+ fmi3Simulate(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3Simulate() in FMI/FMI3_sim.jl
+"""
+function fmi3Simulate(fmu::FMU3, args...; kwargs...)
+ fmi3Simulate(fmu, nothing, args...; kwargs...)
+end
+
+"""
+ fmi3SimulateCS(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SimulateCS() in FMI/FMI3_sim.jl
+"""
+function fmi3SimulateCS(fmu::FMU3, args...; kwargs...)
+ return fmi3SimulateCS(fmu, nothing, args...; kwargs...)
+end
+
+"""
+ fmi3SimulateME(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3SimulateME() in FMI/FMI3_sim.jl
+"""
+function fmi3SimulateME(fmu::FMU3, args...; kwargs...)
+ return fmi3SimulateME(fmu, nothing, args...; kwargs...)
+end
+
+
+"""
+ fmi3GetStartValue(fmu::FMU3, args...; kwargs...)
+
+Wrapper for fmi3GetStartValue() in FMIImport/FMI3_c.jl
+"""
+function fmi3GetStartValue(fmu::FMU3, args...; kwargs...)
+ @assert length(fmu.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"]
+ fmi3GetStartValue(fmu.instances[end], args...; kwargs...)
+end
\ No newline at end of file
diff --git a/src/FMI3_plot.jl b/src/FMI3_plot.jl
index b56dd818..9105b137 100644
--- a/src/FMI3_plot.jl
+++ b/src/FMI3_plot.jl
@@ -1,9 +1,13 @@
+# ABM: done
+
+
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#
-using SciMLBase: ODESolution
+using FMIImport: FMU3Solution
+import ForwardDiff
"""
Plots data from a ME-FMU.
@@ -11,70 +15,169 @@ Plots data from a ME-FMU.
Optional `t_in_solution` controls if the first state in the solution is interpreted as t(ime).
Optional keyword argument `maxLabelLength` controls the maximum length for legend labels (too long labels are cut from front).
"""
-function fmiPlot(fmu::FMU3, solution::ODESolution; maxLabelLength=64)
+function fmiPlot(solution::FMU3Solution; kwargs...)
+ fig = Plots.plot(; xlabel="t [s]")
+ fmiPlot!(fig, solution; kwargs...)
+ return fig
+end
+function fmiPlot!(fig, solution::FMU3Solution;
+ states::Union{Bool, Nothing}=nothing,
+ values::Union{Bool, Nothing}=nothing,
+ stateEvents::Union{Bool, Nothing}=nothing,
+ timeEvents::Union{Bool, Nothing}=nothing,
+ stateIndices=nothing,
+ valueIndices=nothing,
+ maxLabelLength=64,
+ plotkwargs...)
+
+ numStateEvents = 0
+ numTimeEvents = 0
+ for e in solution.events
+ if e.indicator > 0
+ numStateEvents += 1
+ else
+ numTimeEvents += 1
+ end
+ end
- t = solution.t
-
- numStates = length(solution.u[1])
-
- fig = Plots.plot(xlabel="t [s]")
- for s in 1:numStates
- vr = fmu.modelDescription.stateValueReferences[s]
- vrName = fmi3ValueReference2String(fmu, vr)[1]
+ if states === nothing
+ states = (solution.states !== nothing)
+ end
- values = collect(data[s] for data in solution.u)
+ if values === nothing
+ values = (solution.values !== nothing)
+ end
- # prevent legend labels from getting too long
- label = "$vrName ($vr)"
- labelLength = length(label)
- if labelLength > maxLabelLength
- label = "..." * label[labelLength-maxLabelLength:end]
+ if stateEvents === nothing
+ stateEvents = false
+ for e in solution.events
+ if e.indicator > 0
+ stateEvents = true
+ break
+ end
+ end
+
+ if numStateEvents > 100
+ @info "fmiPlot(...): Number of state events ($(numStateEvents)) exceeding 100, disabling automatic plotting of state events (can be forced with keyword `stateEvents=true`)."
+ stateEvents = false
end
-
- Plots.plot!(fig, t, values, label=label)
end
- fig
-end
-
-"""
-Extended the original plot-command by plotting FMUs.
-"""
-function Plots.plot(fmu::FMU3, solution::ODESolution)
- fmiPlot(fmu, solution)
-end
-
-"""
-Plots data from a CS-FMU.
-"""
-function fmiPlot(fmu::FMU3, recordValues::fmi3ValueReferenceFormat, savedValues::DiffEqCallbacks.SavedValues; maxLabelLength=64)
- ts = savedValues.t
+ if timeEvents === nothing
+ timeEvents = false
+ for e in solution.events
+ if e.indicator == 0
+ timeEvents = true
+ break
+ end
+ end
+
+ if numTimeEvents > 100
+ @info "fmiPlot(...): Number of time events ($(numTimeEvents)) exceeding 100, disabling automatic plotting of time events (can be forced with keyword `timeEvents=true`)."
+ timeEvents = false
+ end
+ end
- recordValues = prepareValueReference(fmu, recordValues)
+ if stateIndices === nothing
+ stateIndices = 1:length(solution.fmu.modelDescription.stateValueReferences)
+ end
- numVars = length(recordValues)
+ if valueIndices === nothing
+ if solution.values !== nothing
+ valueIndices = 1:length(solution.values.saveval[1])
+ end
+ end
- fig = Plots.plot(xlabel="t [s]")
- for i in 1:numVars
- vr = recordValues[i]
- vrName = fmi3ValueReference2String(fmu, vr)[1]
- values = collect(data[i] for data in savedValues.saveval)
+ plot_min = Inf
+ plot_max = -Inf
+
+ # plot states
+ if states
+ t = collect(ForwardDiff.value(e) for e in solution.states.t)
+ numValues = length(solution.states.u[1])
+
+ for v in 1:numValues
+ if v ∈ stateIndices
+ vr = solution.fmu.modelDescription.stateValueReferences[v]
+ vrNames = fmi3ValueReferenceToString(solution.fmu, vr)
+ vrName = vrNames[1]
+
+ vals = collect(ForwardDiff.value(data[v]) for data in solution.states.u)
+
+ plot_min = min(plot_min, vals...)
+ plot_max = max(plot_max, vals...)
+
+ # prevent legend labels from getting too long
+ label = "$vrName ($vr)"
+ labelLength = length(label)
+ if labelLength > maxLabelLength
+ label = "..." * label[labelLength-maxLabelLength:end]
+ end
+
+ Plots.plot!(fig, t, vals; label=label, plotkwargs...)
+ end
+ end
+ end
+
+ # plot recorded values
+ if values
+ t = collect(ForwardDiff.value(e) for e in solution.values.t)
+ numValues = length(solution.values.saveval[1])
+
+ for v in 1:numValues
+ if v ∈ valueIndices
+ vr = solution.valueReferences[v]
+ vrNames = fmi3ValueReferenceToString(solution.fmu, vr)
+ vrName = vrNames[1]
+
+ vals = collect(ForwardDiff.value(data[v]) for data in solution.values.saveval)
+
+ plot_min = min(plot_min, vals...)
+ plot_max = max(plot_max, vals...)
+
+ # prevent legend labels from getting too long
+ label = "$vrName ($vr)"
+ labelLength = length(label)
+ if labelLength > maxLabelLength
+ label = "..." * label[labelLength-maxLabelLength:end]
+ end
+
+ Plots.plot!(fig, t, vals; label=label, plotkwargs...)
+ end
+ end
+ end
- # prevent legend labels from getting too long
- label = "$vrName ($vr)"
- labelLength = length(label)
- if labelLength > maxLabelLength
- label = "..." * label[labelLength-maxLabelLength:end]
+ if stateEvents
+ first = true
+ for e in solution.events
+ if e.indicator > 0
+ Plots.plot!(fig, [e.t, e.t], [plot_min, plot_max]; label=(first ? "State event(s)" : nothing), style=:dash, color=:blue)
+ first = false
+ end
end
+ end
- Plots.plot!(fig, ts, values, label=label)
+ if timeEvents
+ first = true
+ for e in solution.events
+ if e.indicator == 0
+ Plots.plot!(fig, [e.t, e.t], [plot_min, plot_max]; label=(first ? "Time event(s)" : nothing), style=:dash, color=:red)
+ first = false
+ end
+ end
end
- fig
+
+ return fig
end
"""
Extended the original plot-command by plotting FMUs.
+
+For further information seek `?fmiPlot`.
"""
-function Plots.plot(fmu::FMU3, recordValues::fmi3ValueReferenceFormat, savedValues::DiffEqCallbacks.SavedValues)
- fmiPlot(fmu, recordValues, savedValues)
+function Plots.plot(solution::FMU3Solution, args...; kwargs...)
+ fmiPlot(solution, args...; kwargs...)
end
+function Plots.plot!(fig, solution::FMU3Solution, args...; kwargs...)
+ fmiPlot!(fig, solution, args...; kwargs...)
+end
\ No newline at end of file
diff --git a/src/FMI3_sim.jl b/src/FMI3_sim.jl
index ca4a35d4..d6087de8 100644
--- a/src/FMI3_sim.jl
+++ b/src/FMI3_sim.jl
@@ -1,40 +1,67 @@
+# STATUS: simulation of ME is not working, needs fixing,
+# other TODOs include adding simulation for SE
+
+
#
# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
# Licensed under the MIT license. See LICENSE file in the project root for details.
#
using DifferentialEquations, DiffEqCallbacks
+import SciMLBase: RightRootFind
+
+using FMIImport: fmi3EnterInitializationMode, fmi3ExitInitializationMode, fmi3UpdateDiscreteStates, fmi3GetContinuousStates, fmi3GetNominalsOfContinuousStates, fmi3SetContinuousStates, fmi3GetContinuousStateDerivatives!
+using FMIImport.FMICore: fmi3StatusOK, fmi3TypeCoSimulation, fmi3TypeModelExchange
+using FMIImport.FMICore: fmi3InstanceState, fmi3InstanceStateInstantiated, fmi3InstanceStateInitializationMode, fmi3InstanceStateEventMode, fmi3InstanceStateContinuousTimeMode, fmi3InstanceStateTerminated, fmi3InstanceStateError, fmi3InstanceStateFatal
+using FMIImport: FMU3Solution, FMU3Event
-using FMIImport: fmi3Instance
+using ChainRulesCore
+import ForwardDiff
+
+import ProgressMeter
############ Model-Exchange ############
# Read next time event from FMU and provide it to the integrator
-function time_choice(c::fmi3Instance, integrator)
- @debug "time_choice(_, _): Time event @ t=$(integrator.t)"
-
+function time_choice(c::FMU3Instance, integrator, tStart, tStop)
+ #@info "TC"
+
discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime = fmi3UpdateDiscreteStates(c)
- fmi3EnterContinuousTimeMode(c)
- if Bool(nextEventTimeDefined)
- c.timeEvent = integrator.t >= nextEventTime
- nextEventTime
+
+ if nextEventTimeDefined == fmi3True
+
+ if nextEventTime >= tStart && nextEventTime <= tStop
+ return nextEventTime
+ else
+ # the time event is outside the simulation range!
+ @debug "Next time event @$(c.eventInfo.nextEventTime)s is outside simulation time range ($(tStart), $(tStop)), skipping."
+ return nothing
+ end
else
- Inf
+ return nothing
end
end
# Handles events and returns the values and nominals of the changed continuous states.
-function handleEvents(c::fmi3Instance, enterEventMode::Bool, exitInContinuousMode::Bool)
+function handleEvents(c::FMU3Instance)
+ # @assert c.state == fmi3InstanceStateEventMode "handleEvents(...): Must be in event mode!"
+
+ # trigger the loop
+ discreteStatesNeedUpdate = fmi3True
nominalsChanged = fmi3False
valuesChanged = fmi3False
- if enterEventMode
+ nextEventTimeDefined = fmi3False
+ nextEventTime = 0.0
+
+ while discreteStatesNeedUpdate == fmi3True
+ # TODO set inputs
+ discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime = fmi3UpdateDiscreteStates(c)
+
fmi3EnterEventMode(c, c.stepEvent, c.stateEvent, c.rootsFound, Csize_t(c.fmu.modelDescription.numberOfEventIndicators), c.timeEvent)
# TODO inputEvent handling
-
discreteStatesNeedUpdate = fmi3True
while discreteStatesNeedUpdate == fmi3True
-
# update discrete states
discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime = fmi3UpdateDiscreteStates(c)
@@ -52,279 +79,1027 @@ function handleEvents(c::fmi3Instance, enterEventMode::Bool, exitInContinuousMod
end
end
-
- if exitInContinuousMode
- fmi3EnterContinuousTimeMode(c)
- end
+ fmi3EnterContinuousTimeMode(c)
@debug "handleEvents(_, $(enterEventMode), $(exitInContinuousMode)): rootsFound: $(c.rootsFound) valuesChanged: $(valuesChanged) continuousStates: $(fmi3GetContinuousStates(c))",
return valuesChanged, nominalsChanged
end
# Returns the event indicators for an FMU.
-function condition(c::fmi3Instance, out, x, t, integrator, inputFunction, inputValues::Array{fmi3ValueReference}) # Event when event_f(u,t) == 0
+function condition(c::FMU3Instance, out::AbstractArray{<:Real}, x, t, integrator, inputFunction, inputValues::AbstractArray{fmi3ValueReference})
if inputFunction !== nothing
- fmi3SetFloat64(c, inputValues, inputFunction(integrator.t))
+ fmi3SetFloat64(c, inputValues, inputFunction(c, x, t))
end
- c.stateEvent = fmi3False
- fmi3SetTime(c, t)
+ @assert c.state == fmi3InstanceStateContinuousTimeMode "condition(...): Must be called in mode continuous time."
+
fmi3SetContinuousStates(c, x)
- indicators = fmi3GetEventIndicators(c)
- if length(indicators) > 0
- for i in 1:length(indicators)
- if c.z_prev[i] < 0 && indicators[i] >= 0
- c.rootsFound[i] = 1
- elseif c.z_prev[i] > 0 && indicators[i] <= 0
- c.rootsFound[i] = -1
- else
- c.rootsFound[i] = 0
- end
- c.stateEvent |= (c.rootsFound[i] != 0)
- # c.z_prev[i] = indicators[i]
- end
+ fmi3SetTime(c, t)
+ if inputFunction !== nothing
+ fmi3SetFloat64(c, inputValues, inputFunction(c, x, t))
end
- @debug "condition(_, _, $(x), $(t), _, _, _): eventIndicators $indicators rootsFound $(c.rootsFound) stateEvent $(c.stateEvent)"
- copy!(out, indicators)
+
+ # TODO check implementation of fmi3GetEventIndicators! mit abstract array
+ fmi3GetEventIndicators!(c, out, UInt(length(out)))
+
+ # if length(indicators) > 0
+ # for i in 1:length(indicators)
+ # if c.z_prev[i] < 0 && indicators[i] >= 0
+ # c.rootsFound[i] = 1
+ # elseif c.z_prev[i] > 0 && indicators[i] <= 0
+ # c.rootsFound[i] = -1
+ # else
+ # c.rootsFound[i] = 0
+ # end
+ # c.stateEvent |= (c.rootsFound[i] != 0)
+ # # c.z_prev[i] = indicators[i]
+ # end
+ # end
+
+ return nothing
end
# Handles the upcoming events.
-function affectFMU!(c::fmi3Instance, integrator, idx, inputFunction, inputValues::Array{fmi3ValueReference}, force=false)
- # Event found - handle it
-
- @debug "affectFMU!(_, _, $(idx), _, _): x:$(integrator.u) [before handle events]"
+function affectFMU!(c::FMU3Instance, integrator, idx, inputFunction, inputValues::Array{fmi3ValueReference}, solution::FMU3Solution)
+
+ @assert c.state == fmi3ComponentStateContinuousTimeMode "affectFMU!(...): Must be in continuous time mode!"
+ # there are fx-evaluations before the event is handled, reset the FMU state to the current integrator step
fmi3SetContinuousStates(c, integrator.u)
+ fmi3SetTime(c, integrator.t)
+ if inputFunction !== nothing
+ fmi3SetFloat64(c, inputValues, inputFunction(c, integrator.u, integrator.t))
+ end
- continuousStatesChanged, nominalsChanged = handleEvents(c, true, Bool(sign(idx)))
+ fmi3EnterEventMode(c, c.stepEvent, c.stateEvent, c.rootsFound, Csize_t(c.fmu.modelDescription.numberOfEventIndicators), c.timeEvent)
+
+ # Event found - handle it
+ handleEvents(c)
- @debug "affectFMU!(_, _, $(idx), _, _): continuousStatesChanged=$(continuousStatesChanged) x_int:$(integrator.u) x_fmu:$(fmi3GetContinuousStates(c)) [after handle events]"
+ left_x = nothing
+ right_x = nothing
- if inputFunction !== nothing
- fmi3SetFloat64(c, inputValues, inputFunction(integrator.t))
- end
+ if c.eventInfo.valuesOfContinuousStatesChanged == fmi3True
+ left_x = integrator.u
+ right_x = fmi3GetContinuousStates(c)
+ @debug "affectFMU!(...): Handled event at t=$(integrator.t), new state is $(new_u)"
+ integrator.u = right_x
- if continuousStatesChanged == fmi3True
- integrator.u = fmi3GetContinuousStates(c)
- @debug "affectFMU!(_, _, $(idx), _, _): Set new state $(integrator.u)"
+ u_modified!(integrator, true)
+ #set_proposed_dt!(integrator, 1e-10)
+ else
+ u_modified!(integrator, false)
+ @debug "affectFMU!(...): Handled event at t=$(integrator.t), no new state."
end
- if nominalsChanged == fmi3True
+ if c.eventInfo.nominalsOfContinuousStatesChanged == fmi3True
x_nom = fmi3GetNominalsOfContinuousStates(c)
end
- # timeEventCb = PresetTimeCallback(2.0, (integrator) -> affect!(c, integrator, 0))
+
+ ignore_derivatives() do
+ if idx != -1 # -1 no event, 0, time event, >=1 state event with indicator
+ e = FMU3Event(integrator.t, UInt64(idx), left_x, right_x)
+ push!(solution.events, e)
+ end
+ end
+
+ #fmi3EnterContinuousTimeMode(c)
end
# Does one step in the simulation.
-function stepCompleted(c::fmi3Instance, x, t, integrator, inputFunction, inputValues::Array{fmi3ValueReference})
+function stepCompleted(c::FMU3Instance, x, t, integrator, inputFunction, inputValues::AbstractArray{fmi3ValueReference}, progressMeter, tStart, tStop, solution::FMU3Solution)
+
+ @assert c.state == fmi3InstanceStateContinuousTimeMode "stepCompleted(...): Must be in continuous time mode."
+ #@info "Step completed"
+ if progressMeter !== nothing
+ stat = 1000.0*(t-tStart)/(tStop-tStart)
+ if !isnan(stat)
+ stat = floor(Integer, stat)
+ ProgressMeter.update!(progressMeter, stat)
+ end
+ end
- fmi3SetContinuousStates(c, x)
+ # if length(indicators) > 0
+ # c.stateEvent = fmi3False
- indicators = fmi3GetEventIndicators(c)
- if length(indicators) > 0
- c.stateEvent = fmi3False
+ # for i in 1:length(indicators)
+ # if c.z_prev[i] < 0 && indicators[i] >= 0
+ # c.rootsFound[i] = 1
+ # elseif c.z_prev[i] > 0 && indicators[i] <= 0
+ # c.rootsFound[i] = -1
+ # else
+ # c.rootsFound[i] = 0
+ # end
+ # c.stateEvent |= (c.rootsFound[i] != 0)
+ # c.z_prev[i] = indicators[i]
+ # end
+ # end
+ (status, enterEventMode, terminateSimulation) = fmi3CompletedIntegratorStep(c, fmi3True)
- for i in 1:length(indicators)
- if c.z_prev[i] < 0 && indicators[i] >= 0
- c.rootsFound[i] = 1
- elseif c.z_prev[i] > 0 && indicators[i] <= 0
- c.rootsFound[i] = -1
- else
- c.rootsFound[i] = 0
- end
- c.stateEvent |= (c.rootsFound[i] != 0)
- c.z_prev[i] = indicators[i]
+ if terminateSimulation == fmi3True
+ @error "stepCompleted(...): FMU requested termination!"
+ end
+
+ if enterEventMode == fmi3True
+ affectFMU!(c, integrator, -1, inputFunction, inputValues, solution)
+ else
+ if inputFunction !== nothing
+ fmi3SetFloat64(c, inputValues, inputFunction(c, x, t))
end
end
+end
- (status, c.stepEvent, terminateSimulation) = fmi3CompletedIntegratorStep(c, fmi3True)
- @debug "stepCompleted(_, $(x), $(t), _, _, _): stepEvent $(c.stepEvent)"
- @assert terminateSimulation == fmi3False "completed Integratorstep failed!"
+# save FMU values
+function saveValues(c::FMU3Instance, recordValues, x, t, integrator, inputFunction, inputValues)
+
+ @assert c.state == fmi3ComponentStateContinuousTimeMode "saveValues(...): Must be in continuous time mode."
+
+ #x_old = fmi3GetContinuousStates(c)
+ #t_old = c.t
+ fmi3SetContinuousStates(c, x)
+ fmi3SetTime(c, t)
+ if inputFunction !== nothing
+ fmi3SetFloat64(c, inputValues, inputFunction(c, x, t))
+ end
+
+ #fmi3SetContinuousStates(c, x_old)
+ #fmi3SetTime(c, t_old)
+
+ return (fmiGetFloat64(c, recordValues)...,)
end
# Returns the state derivatives of the FMU.
-function fx(c::fmi3Instance, x, p, t)
+function fx(c::FMU3Instance,
+ dx::AbstractArray{<:Real},
+ x::AbstractArray{<:Real},
+ p::AbstractArray,
+ t::Real)
+
+ # if isa(t, ForwardDiff.Dual)
+ # t = ForwardDiff.value(t)
+ # end
@debug "fx($(x), _, $(t))"
fmi3SetTime(c, t)
fmi3SetContinuousStates(c, x)
dx = fmi3GetContinuousStateDerivatives(c)
+
+ # if all(isa.(dx, ForwardDiff.Dual))
+ # dx_tmp = collect(ForwardDiff.value(e) for e in dx)
+ # fmi3GetContinuousStateDerivatives!(c, dx_tmp)
+ # T, V, N = fd_eltypes(dx)
+ # dx[:] = collect(ForwardDiff.Dual{T, V, N}(dx_tmp[i], ForwardDiff.partials(dx[i]) ) for i in 1:length(dx))
+ # else
+ # fmi3GetContinuousStateDerivatives!(c, dx)
+ # end
+
+ # y, dx = FMIImport.eval!(c, dx, nothing, nothing, x, nothing, nothing, t)
+
+ return dx
end
-# save FMU values
-function saveValues(c::fmi3Instance, recordValues, u, t, integrator)
- fmi3SetTime(c, t)
- x = integrator.sol(t)
- fmi3SetContinuousStates(c, x)
+# same function as in FMI2_sim.jl
+function fd_eltypes(e::ForwardDiff.Dual{T, V, N}) where {T, V, N}
+ return (T, V, N)
+end
+function fd_eltypes(e::AbstractArray{<:ForwardDiff.Dual{T, V, N}}) where {T, V, N}
+ return (T, V, N)
+end
+
+# same function as in FMI2_sim.jl
+function _fx_fd(comp, dx, x, p, t)
+
+ ȧrgs = []
+ args = []
+
+ push!(ȧrgs, NoTangent())
+ push!(args, fx)
+
+ push!(ȧrgs, NoTangent())
+ push!(args, comp)
+
+ T = nothing
+ V = nothing
+
+ dx_set = length(dx) > 0 && all(isa.(dx, ForwardDiff.Dual))
+ x_set = length(x) > 0 && all(isa.(x, ForwardDiff.Dual))
+ p_set = length(p) > 0 && all(isa.(p, ForwardDiff.Dual))
+ t_set = isa(t, ForwardDiff.Dual)
+
+ if dx_set
+ T, V, N = fd_eltypes(dx)
+ push!(ȧrgs, collect(ForwardDiff.partials(e) for e in dx))
+ push!(args, collect(ForwardDiff.value(e) for e in dx))
+ #@info "dx_set num=$(length(dx)) partials=$(length(ForwardDiff.partials(dx[1])))"
+ else
+ push!(ȧrgs, NoTangent())
+ push!(args, dx)
+ end
+
+ if x_set
+ T, V, N = fd_eltypes(x)
+ push!(ȧrgs, collect(ForwardDiff.partials(e) for e in x))
+ push!(args, collect(ForwardDiff.value(e) for e in x))
+ #@info "x_set num=$(length(x)) partials=$(length(ForwardDiff.partials(x[1])))"
+ else
+ push!(ȧrgs, NoTangent())
+ push!(args, x)
+ end
+
+ if p_set
+ T, V, N = fd_eltypes(p)
+ push!(ȧrgs, collect(ForwardDiff.partials(e) for e in p))
+ push!(args, collect(ForwardDiff.value(e) for e in p))
+ else
+ push!(ȧrgs, NoTangent())
+ push!(args, p)
+ end
+
+ if t_set
+ T, V, N = fd_eltypes(t)
+ push!(ȧrgs, ForwardDiff.partials(t))
+ push!(args, ForwardDiff.value(t))
+ else
+ push!(ȧrgs, NoTangent())
+ push!(args, t)
+ end
+
+ ȧrgs = (ȧrgs...,)
+ args = (args...,)
+
+ y, _, sdx, sx, sp, st = ChainRulesCore.frule(ȧrgs, args...)
- (fmi3GetFloat64(c, recordValues)...,)
+ ys = []
+
+ #[collect( ForwardDiff.Dual{Tx, Vx, Nx}(y[i], ForwardDiff.partials(x_partials[i], t_partials[i])) for i in 1:length(y) )...]
+ for i in 1:length(y)
+ is = NoTangent()
+
+ if dx_set
+ is = sdx[i]#.values
+ end
+ if x_set
+ is = sx[i]#.values
+ end
+
+ if p_set
+ is = sp[i]#.values
+ end
+ if t_set
+ is = st[i]#.values
+ end
+
+ #display("dx: $dx")
+ #display("sdx: $sdx")
+
+ #partials = (isdx, isx, isp, ist)
+
+ #display(partials)
+
+
+ #V = Float64
+ #N = length(partials)
+ #display("$T $V $N")
+
+ #display(is)
+
+ @assert is != ZeroTangent() && is != NoTangent() "is: $(is)"
+
+ push!(ys, ForwardDiff.Dual{T, V, N}(y[i], is ) ) # ForwardDiff.Partials{N, V}(partials)
+ end
+
+ ys
end
-"""
-Source: FMISpec3.0, Version D5ef1c1: 3.3. Code Example
+# same functionhead as in FMI2_sim.jl
+# frule for fx
+function ChainRulesCore.frule((Δself, Δcomp, Δdx, Δx, Δp, Δt),
+ ::typeof(fx),
+ comp, #::FMU3Instance,
+ dx,
+ x,#::AbstractArray{<:Real},
+ p,
+ t)
+
+ y = fx(comp, dx, x, p, t)
+ function fx_pullforward(Δdx, Δx, Δt)
+
+ # if t >= 0.0
+ # fmi3SetTime(comp, t)
+ # end
+
+ # if all(isa.(x, ForwardDiff.Dual))
+ # xf = collect(ForwardDiff.value(e) for e in x)
+ # fmi3SetContinuousStates(comp, xf)
+ # else
+ # fmi3SetContinuousStates(comp, x)
+ # end
+
+ c̄omp = ZeroTangent()
+ d̄x = ZeroTangent()
+ x̄ = ZeroTangent()
+ p̄ = ZeroTangent()
+ t̄ = ZeroTangent()
+
+ if Δdx != NoTangent()
+ d̄x = Δdx
+ end
+
+ if Δx != NoTangent()
+ if comp.A === nothing || size(comp.A) != (length(comp.fmu.modelDescription.derivativeValueReferences), length(comp.fmu.modelDescription.stateValueReferences))
+ comp.A = zeros(length(comp.fmu.modelDescription.derivativeValueReferences), length(comp.fmu.modelDescription.stateValueReferences))
+ end
+ comp.jacobianUpdate!(comp.A, comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+ x̄ = comp.A * Δx
+ end
+
+ if Δt != NoTangent()
+ dt = 1e-6
+ dx1 = fmi3GetContinuousStateDerivatives(comp)
+ fmi3SetTime(comp, t + dt)
+ dx2 = fmi3GetContinuousStateDerivatives(comp)
+ ∂t = (dx2-dx1)/dt
+ t̄ = ∂t * Δt
+ end
+
+ return (c̄omp, d̄x, x̄, p̄, t̄)
+ end
+ return (y, fx_pullforward(Δdx, Δx, Δt)...)
+end
+
+# rrule for fx
+function ChainRulesCore.rrule(::typeof(fx),
+ comp::FMU3Instance,
+ dx,
+ x,
+ p,
+ t)
+
+ y = fx(comp, dx, x, p, t)
+ function fx_pullback(ȳ)
+
+ if t >= 0.0
+ fmi3SetTime(comp, t)
+ end
+
+ fmi3SetContinuousStates(comp, x)
+
+ if comp.A === nothing || size(comp.A) != (length(comp.fmu.modelDescription.derivativeValueReferences), length(comp.fmu.modelDescription.stateValueReferences))
+ comp.A = zeros(length(comp.fmu.modelDescription.derivativeValueReferences), length(comp.fmu.modelDescription.stateValueReferences))
+ end
+ comp.jacobianUpdate!(comp.A, comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+
+ n_dx_x = @thunk(comp.A' * ȳ)
+
+ f̄ = NoTangent()
+ c̄omp = ZeroTangent()
+ d̄x = ZeroTangent()
+ x̄ = n_dx_x
+ p̄ = ZeroTangent()
+ t̄ = ZeroTangent()
+
+ return f̄, c̄omp, d̄x, x̄, p̄, t̄
+ end
+ return (y, fx_pullback)
+end
+
+# frule for fx
+# function ChainRulesCore.frule((Δself, Δcomp, Δdx, Δx, Δp, Δt),
+# ::typeof(fx),
+# comp, #::FMU3Instance,
+# dx,
+# x,#::AbstractArray{<:Real},
+# p,
+# t)
+
+# y = fx(comp, dx, x, p, t)
+# function fx_pullforward(Δx)
+
+# if t >= 0.0
+# fmi3SetTime(comp, t)
+# end
+
+# if all(isa.(x, ForwardDiff.Dual))
+# xf = collect(ForwardDiff.value(e) for e in x)
+# fmi3SetContinuousStates(comp, xf)
+# else
+# fmi3SetContinuousStates(comp, x)
+# end
+
+# if comp.A == nothing || size(comp.A) != (length(comp.fmu.modelDescription.derivativeValueReferences), length(comp.fmu.modelDescription.stateValueReferences))
+# comp.A = zeros(length(comp.fmu.modelDescription.derivativeValueReferences), length(comp.fmu.modelDescription.stateValueReferences))
+# end
+# comp.jacobianUpdate!(comp.A, comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+
+# n_dx_x = comp.A * Δx
+
+# c̄omp = ZeroTangent()
+# d̄x = ZeroTangent()
+# x̄ = n_dx_x
+# p̄ = ZeroTangent()
+# t̄ = ZeroTangent()
+
+# return (c̄omp, d̄x, x̄, p̄, t̄)
+# end
+# return (y, fx_pullforward(Δx)...)
+# end
+
+import FMIImport: fmi3VariabilityConstant, fmi3InitialApprox, fmi3InitialExact
+function setBeforeInitialization(mv::FMIImport.fmi3ModelVariable)
+ return mv.variability != fmi3VariabilityConstant && mv.initial ∈ (fmi3InitialApprox, fmi3InitialExact)
+end
+
+import FMIImport: fmi3CausalityInput, fmi3CausalityParameter, fmi3VariabilityTunable
+function setInInitialization(mv::FMIImport.fmi3ModelVariable)
+ return mv.causality == fmi3CausalityInput || (mv.causality != fmi3CausalityParameter && mv.variability == fmi3VariabilityTunable) || (mv.variability != fmi3VariabilityConstant && mv.initial == fmi3InitialExact)
+end
+
+function prepareFMU(fmu::FMU3, c::Union{Nothing, FMU3Instance}, type::fmi3Type, instantiate::Union{Nothing, Bool}, terminate::Union{Nothing, Bool}, reset::Union{Nothing, Bool}, setup::Union{Nothing, Bool}, parameters::Union{Dict{<:Any, <:Any}, Nothing}, t_start, t_stop, tolerance;
+ x0::Union{AbstractArray{<:Real}, Nothing}=nothing, inputFunction=nothing, inputValueReferences=nothing)
+
+ if instantiate === nothing
+ instantiate = fmu.executionConfig.instantiate
+ end
+
+ if terminate === nothing
+ terminate = fmu.executionConfig.terminate
+ end
+
+ if reset === nothing
+ reset = fmu.executionConfig.reset
+ end
+
+ if setup === nothing
+ setup = fmu.executionConfig.setup
+ end
+
+ c = nothing
+
+ # instantiate (hard)
+ if instantiate
+ if type == fmi3TypeCoSimulation
+ c = fmi3InstantiateCoSimulation!(fmu)
+ elseif type == fmi3TypeModelExchange
+ c = fmi3InstantiateModelExchange!(fmu)
+ else
+ c = fmi3InstantiateScheduledExecution!(fmu)
+ end
+ else
+ if c === nothing
+ c = fmu.components[end]
+ end
+ end
+
+ # soft terminate (if necessary)
+ if terminate
+ retcode = fmi3Terminate(c; soft=true)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Termination failed with return code $(retcode)."
+ end
+
+ # soft reset (if necessary)
+ if reset
+ retcode = fmi3Reset(c; soft=true)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Reset failed with return code $(retcode)."
+ end
+
+ # setup experiment (hard)
+ # TODO this part is handled by fmi3EnterInitializationMode
+ # if setup
+ # retcode = fmi2SetupExperiment(c, t_start, t_stop; tolerance=tolerance)
+ # @assert retcode == fmi3StatusOK "fmi3Simulate(...): Setting up experiment failed with return code $(retcode)."
+ # end
+
+ # parameters
+ if parameters !== nothing
+ retcodes = fmi3Set(c, collect(keys(parameters)), collect(values(parameters)); filter=setBeforeInitialization)
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial parameters failed with return code $(retcode)."
+ end
+
+ # inputs
+ inputs = nothing
+ if inputFunction !== nothing && inputValueReferences !== nothing
+ # set inputs
+ inputs = Dict{fmi3ValueReference, Any}()
+
+ inputValues = nothing
+ if hasmethod(inputFunction, Tuple{FMU3Instance, fmi3Float64}) # CS
+ inputValues = inputFunction(c, t_start)
+ else # ME
+ inputValues = inputFunction(c, nothing, t_start)
+ end
+
+ for i in 1:length(inputValueReferences)
+ vr = inputValueReferences[i]
+ inputs[vr] = inputValues[i]
+ end
+ end
+
+ # inputs
+ if inputs !== nothing
+ retcodes = fmi3Set(c, collect(keys(inputs)), collect(values(inputs)); filter=setBeforeInitialization)
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial inputs failed with return code $(retcode)."
+ end
+
+ # start state
+ if x0 !== nothing
+ #retcode = fmi3SetContinuousStates(c, x0)
+ #@assert retcode == fmi3StatusOK "fmi3Simulate(...): Setting initial state failed with return code $(retcode)."
+ retcodes = fmi3Set(c, fmu.modelDescription.stateValueReferences, x0; filter=setBeforeInitialization)
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial inputs failed with return code $(retcode)."
+ end
+
+ # enter (hard)
+ if setup
+ retcode = fmi3EnterInitializationMode(c, t_start, t_stop; tolerance = tolerance)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Entering initialization mode failed with return code $(retcode)."
+ end
+
+ # parameters
+ if parameters !== nothing
+ retcodes = fmi3Set(c, collect(keys(parameters)), collect(values(parameters)); filter=setInInitialization)
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial parameters failed with return code $(retcode)."
+ end
+
+ if inputs !== nothing
+ retcodes = fmi3Set(c, collect(keys(inputs)), collect(values(inputs)); filter=setInInitialization)
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial inputs failed with return code $(retcode)."
+ end
+
+ # start state
+ if x0 !== nothing
+ #retcode = fmi3SetContinuousStates(c, x0)
+ #@assert retcode == fmi3StatusOK "fmi3Simulate(...): Setting initial state failed with return code $(retcode)."
+ retcodes = fmi3Set(c, fmu.modelDescription.stateValueReferences, x0; filter=setInInitialization)
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial inputs failed with return code $(retcode)."
+ end
+
+ # exit setup (hard)
+ if setup
+ retcode = fmi3ExitInitializationMode(c)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Exiting initialization mode failed with return code $(retcode)."
+ end
+
+ if type == fmi3TypeModelExchange
+ if x0 === nothing
+ x0 = fmi3GetContinuousStates(c)
+ end
+ end
+
+ return c, x0
+end
+
+function prepareFMU(fmu::Vector{FMU3}, c::Vector{Union{Nothing, FMU3Instance}}, type::Vector{fmi3Type}, instantiate::Union{Nothing, Bool}, freeInstance::Union{Nothing, Bool}, terminate::Union{Nothing, Bool}, reset::Union{Nothing, Bool}, setup::Union{Nothing, Bool}, parameters::Union{Vector{Union{Dict{<:Any, <:Any}, Nothing}}, Nothing}, t_start, t_stop, tolerance;
+ x0::Union{Vector{Union{Array{<:Real}, Nothing}}, Nothing}=nothing, initFct=nothing)
+
+ ignore_derivatives() do
+ for i in 1:length(fmu)
+
+ if instantiate === nothing
+ instantiate = fmu[i].executionConfig.instantiate
+ end
+
+ if freeInstance === nothing
+ freeInstance = fmu[i].executionConfig.freeInstance
+ end
+
+ if terminate === nothing
+ terminate = fmu[i].executionConfig.terminate
+ end
+
+ if reset === nothing
+ reset = fmu[i].executionConfig.reset
+ end
+
+ if setup === nothing
+ setup = fmu[i].executionConfig.setup
+ end
+
+ # instantiate (hard)
+ if instantiate
+ # remove old one if we missed it (callback)
+ if c[i] !== nothing
+ if freeInstance
+ fmi3FreeInstance!(c[i])
+ @debug "[AUTO-RELEASE INST]"
+ end
+ end
+
+ if type[i] == fmi3TypeCoSimulation
+ c[i] = fmi3InstantiateCoSimulation!(fmu[i])
+ elseif type[i] == fmi3TypeModelExchange
+ c[i] = fmi3InstantiateModelExchange!(fmu[i])
+ else
+ c[i] = fmi3InstantiateScheduledExecution!(fmu[i])
+ end
+ @debug "[NEW INST]"
+ else
+ if c[i] === nothing
+ c[i] = fmu[i].components[end]
+ end
+ end
+
+ # soft terminate (if necessary)
+ if terminate
+ retcode = fmi3Terminate(c[i]; soft=true)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Termination failed with return code $(retcode)."
+ end
+
+ # soft reset (if necessary)
+ if reset
+ retcode = fmi3Reset(c[i]; soft=true)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Reset failed with return code $(retcode)."
+ end
+
+ # enter setup (hard)
+ if setup
+ # retcode = fmi2SetupExperiment(c[i], t_start, t_stop; tolerance=tolerance)
+ # @assert retcode == fmi2StatusOK "fmi2Simulate(...): Setting up experiment failed with return code $(retcode)."
+
+ retcode = fmi3EnterInitializationMode(c[i], t_start, t_stop; tolerance=tolerance)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Entering initialization mode failed with return code $(retcode)."
+ end
+
+ if x0 !== nothing
+ if x0[i] !== nothing
+ retcode = fmi3SetContinuousStates(c[i], x0[i])
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Setting initial state failed with return code $(retcode)."
+ end
+ end
+
+ if parameters !== nothing
+ if parameters[i] !== nothing
+ retcodes = fmi3Set(c[i], collect(keys(parameters[i])), collect(values(parameters[i])) )
+ @assert all(retcodes .== fmi3StatusOK) "fmi3Simulate(...): Setting initial parameters failed with return code $(retcode)."
+ end
+ end
+
+ if initFct !== nothing
+ initFct()
+ end
+
+ # exit setup (hard)
+ if setup
+ retcode = fmi3ExitInitializationMode(c[i])
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Exiting initialization mode failed with return code $(retcode)."
+ end
+
+ if type == fmi3TypeModelExchange
+ if x0 === nothing
+ if x0[i] === nothing
+ x0[i] = fmi3GetContinuousStates(c[i])
+ end
+ end
+ end
+ end
+
+ end # ignore_derivatives
+
+ return c, x0
+end
+
+function finishFMU(fmu::FMU3, c::FMU3Instance, terminate::Union{Nothing, Bool}, freeInstance::Union{Nothing, Bool})
+
+ if c === nothing
+ return
+ end
+
+ if terminate === nothing
+ terminate = fmu.executionConfig.terminate
+ end
+
+ if freeInstance === nothing
+ freeInstance = fmu.executionConfig.freeInstance
+ end
+
+ # soft terminate (if necessary)
+ if terminate
+ retcode = fmi3Terminate(c; soft=true)
+ @assert retcode == fmi3StatusOK "fmi3Simulate(...): Termination failed with return code $(retcode)."
+ end
+
+ # freeInstance (hard)
+ if freeInstance
+ fmi3FreeInstance!(c)
+ end
+end
+
+# wrapper
+function fmi3SimulateME(c::FMU3Instance, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing; kwargs...)
+ fmi3SimulateME(c.fmu, c, t_start, t_stop; kwargs...)
+end
+
+# sets up the ODEProblem for simulating a ME-FMU
+function setupODEProblem(c::FMU3Instance, x0::AbstractArray{fmi3Float64}, t_start::fmi3Float64, t_stop::fmi3Float64; p=[], customFx=nothing)
+ if customFx === nothing
+ customFx = (dx, x, p, t) -> fx(c, dx, x, p, t)
+ end
+
+ p = []
+ c.problem = ODEProblem(customFx, x0, (t_start, t_stop), p,)
+
+ return c.problem
+end
+"""
Simulates a FMU instance for the given simulation time interval.
State- and Time-Events are handled correctly.
-Returns a tuple of type (ODESolution, DiffEqCallbacks.SavedValues).
-If keyword `recordValues` is not set, a tuple of type (ODESolution, nothing) is returned for consitency.
+Via the optional keyword arguemnts `inputValues` and `inputFunction`, a custom input function `f(c, u, t)`, `f(c, t)`, `f(u, t)`, `f(c, u)` or `f(t)` with `c` current component, `u` current state and `t` current time can be defined, that should return a array of values for `fmi3SetFloat64(..., inputValues, inputFunction(...))`.
+
+Keywords:
+ - solver: Any Julia-supported ODE-solver (default is Tsit5)
+ - customFx: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t)
+ - recordValues: Array of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues`
+ - saveat: Time points to save values at (interpolated)
+ - setup: Boolean, if FMU should be setup (default=true)
+ - reset: Union{Bool, :auto}, if FMU should be reset before simulation (default reset=:auto)
+ - inputValueReferences: Array of input variables (strings or variableIdentifiers) to set at every simulation step
+ - inputFunction: Function to retrieve the values to set the inputs to
+ - parameters: Dictionary of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization
+ - `callbacks`: custom callbacks to add
+
+Returns:
+ - If keyword `recordValues` is not set, a struct of type `ODESolution`.
+ - If keyword `recordValues` is set, a tuple of type (ODESolution, DiffEqCallbacks.SavedValues).
"""
-function fmi3SimulateME(c::fmi3Instance, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+function fmi3SimulateME(fmu::FMU3, c::Union{FMU3Instance, Nothing}=nothing, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+ tolerance::Union{Real, Nothing} = nothing,
+ dt::Union{Real, Nothing} = nothing,
solver = nothing,
customFx = nothing,
recordValues::fmi3ValueReferenceFormat = nothing,
- saveat = [],
- saveAtEvent::Bool = false,
- setup::Bool = true,
- reset = nothing, # nothing = auto
- inputValues::fmi3ValueReferenceFormat = nothing,
+ saveat = nothing,
+ x0::Union{AbstractArray{<:Real}, Nothing} = nothing,
+ setup::Union{Bool, Nothing} = nothing,
+ reset::Union{Bool, Nothing} = nothing,
+ instantiate::Union{Bool, Nothing} = nothing,
+ freeInstance::Union{Bool, Nothing} = nothing,
+ terminate::Union{Bool, Nothing} = nothing,
+ inputValueReferences::fmi3ValueReferenceFormat = nothing,
inputFunction = nothing,
- rootSearchInterpolationPoints = 100,
+ parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing,
+ dtmax::Union{Real, Nothing} = nothing,
+ callbacks = [],
+ showProgress::Bool = true,
kwargs...)
- if t_start == nothing
- t_start = c.fmu.modelDescription.defaultStartTime
- end
+
+ @assert fmi3IsModelExchange(fmu) "fmi3SimulateME(...): This function supports Model Exchange FMUs only."
+ #@assert fmu.type == fmi3TypeModelExchange "fmi3SimulateME(...): This FMU supports Model Exchange, but was instantiated in CS mode. Use `fmiLoad(...; type=:ME)`."
- if t_stop == nothing
- t_stop = c.fmu.modelDescription.defaultStopTime
+ # input function handling
+ _inputFunction = nothing
+ if inputFunction !== nothing
+ if hasmethod(inputFunction, Tuple{fmi3Float64})
+ _inputFunction = (c, u, t) -> inputFunction(t)
+ elseif hasmethod(inputFunction, Tuple{Union{FMU3Instance, Nothing}, fmi3Float64})
+ _inputFunction = (c, u, t) -> inputFunction(c, t)
+ elseif hasmethod(inputFunction, Tuple{Union{FMU3Instance, Nothing}, AbstractArray{fmi3Float64,1}})
+ _inputFunction = (c, u, t) -> inputFunction(c, u)
+ elseif hasmethod(inputFunction, Tuple{AbstractArray{fmi3Float64,1}, fmi3Float64})
+ _inputFunction = (c, u, t) -> inputFunction(u, t)
+ else
+ _inputFunction = inputFunction
+ end
+ @assert hasmethod(_inputFunction, Tuple{FMU3Instance, Union{AbstractArray{fmi3Float64,1}, Nothing}, fmi3Float64}) "The given input function does not fit the needed input function pattern for ME-FMUs, which are: \n- `inputFunction(t::fmi3Float64)`\n- `inputFunction(comp::FMU3Instance, t::fmi3Float64)`\n- `inputFunction(comp::FMU3Instance, u::Union{AbstractArray{fmi3Float64,1}, Nothing})`\n- `inputFunction(u::Union{AbstractArray{fmi3Float64,1}, Nothing}, t::fmi3Float64)`\n- `inputFunction(comp::FMU3Instance, u::Union{AbstractArray{fmi3Float64,1}, Nothing}, t::fmi3Float64)`"
end
- recordValues = prepareValueReference(c, recordValues)
- inputValues = prepareValueReference(c, inputValues)
- solution = nothing
- callbacks = []
- savedValues = nothing
+ recordValues = prepareValueReference(fmu, recordValues)
+ inputValueReferences = prepareValueReference(fmu, inputValueReferences)
+
+ fmusol = FMU3Solution(fmu)
savingValues = (length(recordValues) > 0)
- hasInputs = (length(inputValues) > 0)
+ hasInputs = (length(inputValueReferences) > 0)
+ hasParameters = (parameters !== nothing)
+ hasStartState = (x0 !== nothing)
- if savingValues
- savedValues = SavedValues(Float64, Tuple{collect(Float64 for i in 1:length(recordValues))...})
+ cbs = []
- savingCB = SavingCallback((u,t,integrator) -> saveValues(c, recordValues, u, t, integrator),
- savedValues,
- saveat=saveat)
- push!(callbacks, savingCB)
+ for cb in callbacks
+ push!(cbs, cb)
end
- # auto correct reset if only setup is given
- if reset === nothing
- reset = setup
+ if t_start === nothing
+ t_start = fmi3GetDefaultStartTime(fmu.modelDescription)
+
+ if t_start === nothing
+ t_start = 0.0
+ @info "No `t_start` choosen, no `t_start` availabel in the FMU, auto-picked `t_start=0.0`."
+ end
end
- @assert !(setup==false && reset==true) "fmi3SimulateME(...): keyword argument `setup=false`, but `reset=true`. This may cause a FMU crash."
-
+
+ if t_stop === nothing
+ t_stop = fmi3GetDefaultStopTime(fmu.modelDescription)
- if reset
- fmi3Reset(c)
+ if t_stop === nothing
+ t_stop = 1.0
+ @warn "No `t_stop` choosen, no `t_stop` availabel in the FMU, auto-picked `t_stop=1.0`."
+ end
end
- if setup
- fmi3EnterInitializationMode(c, t_start, t_stop)
- fmi3ExitInitializationMode(c)
+ if tolerance === nothing
+ tolerance = fmi3GetDefaultTolerance(fmu.modelDescription)
+ # if no tolerance is given, pick auto-setting from DifferentialEquations.jl
end
- eventHandling = c.fmu.modelDescription.numberOfEventIndicators > 0
- timeEventHandling = false
-
- if eventHandling
- discreteStatesNeedUpdate = fmi3True
- while discreteStatesNeedUpdate == fmi3True
- discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime = fmi3UpdateDiscreteStates(c)
- @assert terminateSimulation == fmi3False ["Initial Event handling failed!"]
- timeEventHandling |= (nextEventTimeDefined == fmi3True)
- end
+ if dt === nothing
+ dt = fmi3GetDefaultStepSize(fmu.modelDescription)
+ # if no dt is given, pick auto-setting from DifferentialEquations.jl
end
-
- if customFx === nothing
- customFx = (x, p, t) -> fx(c, x, p, t)
+
+ if dtmax === nothing
+ dtmax = (t_stop-t_start)/100.0
end
- fmi3EnterContinuousTimeMode(c)
+ # argument `tolerance=nothing` here, because ME-FMUs doesn't support tolerance control (no solver included)
+ # tolerance for the solver is set-up later in this function
+ c, x0 = prepareFMU(fmu, c, fmi3TypeModelExchange, instantiate, terminate, reset, setup, parameters, t_start, t_stop, nothing; x0=x0, inputFunction=_inputFunction, inputValueReferences=inputValueReferences)
- if eventHandling
- c.z_prev = fmi3GetEventIndicators(c)
- end
+ # from here on, we are in event mode, if `setup=false` this is the job of the user
+ #@assert c.state == fmi3InstanceStateEventMode "FMU needs to be in event mode after setup."
- # First evaluation of the FMU
- x0 = fmi3GetContinuousStates(c)
- x0_nom = fmi3GetNominalsOfContinuousStates(c)
+ # if x0 === nothing
+ # x0 = fmi3GetContinuousStates(c)
+ # x0_nom = fmi3GetNominalsOfContinuousStates(c)
+ # end
- p = []
- problem = ODEProblem(customFx, x0, (t_start, t_stop), p,)
+ # initial event handling
+ fmi3EnterEventMode(c, c.stepEvent, c.stateEvent, c.rootsFound, Csize_t(c.fmu.modelDescription.numberOfEventIndicators), c.timeEvent)
+ handleEvents(c)
+ #fmi3EnterContinuousTimeMode(c)
+
+ c.fmu.hasStateEvents = (c.fmu.modelDescription.numberOfEventIndicators > 0)
+ # c.fmu.hasTimeEvents = (c.eventInfo.nextEventTimeDefined == fmi2True)
+ c.fmu.hasTimeEvents = fmi3False
+ setupODEProblem(c, x0, t_start, t_stop; customFx=customFx)
+
+ progressMeter = nothing
+ if showProgress
+ progressMeter = ProgressMeter.Progress(1000; desc="Simulating ME-FMU ...", color=:blue, dt=1.0) #, barglyphs=ProgressMeter.BarGlyphs("[=> ]"))
+ ProgressMeter.update!(progressMeter, 0) # show it!
+ end
+
# callback functions
- # use step callback always if we have inputs or need evenet handling
- if hasInputs || eventHandling
- stepCb = FunctionCallingCallback((x, t, integrator) -> stepCompleted(c, x, t, integrator, inputFunction, inputValues);
- func_everystep = true,
- func_start = true)
- push!(callbacks, stepCb)
+ if c.fmu.hasTimeEvents
+ timeEventCb = IterativeCallback((integrator) -> time_choice(c, integrator, t_start, t_stop),
+ (integrator) -> affectFMU!(c, integrator, 0, _inputFunction, inputValueReferences, fmusol), Float64;
+ initial_affect = false, # (c.eventInfo.nextEventTime == t_start)
+ save_positions=(false,false))
+ push!(cbs, timeEventCb)
end
- if eventHandling
+ if c.fmu.hasStateEvents
- eventCb = VectorContinuousCallback((out, x, t, integrator) -> condition(c, out, x, t, integrator, inputFunction, inputValues),
- (integrator, idx) -> affectFMU!(c, integrator, idx, inputFunction, inputValues, true),
+ eventCb = VectorContinuousCallback((out, x, t, integrator) -> condition(c, out, x, t, integrator, _inputFunction, inputValueReferences),
+ (integrator, idx) -> affectFMU!(c, integrator, idx, _inputFunction, inputValueReferences, fmusol),
Int64(c.fmu.modelDescription.numberOfEventIndicators);
rootfind = RightRootFind,
- save_positions=(saveAtEvent,saveAtEvent),
- interp_points=rootSearchInterpolationPoints)#,abstol=1e-16, reltol=1e-12, repeat_nudge=1//100)
- push!(callbacks, eventCb)
-
- if timeEventHandling
- timeEventCb = IterativeCallback((integrator) -> time_choice(c, integrator),
- (integrator) -> affectFMU!(c, integrator, 0, inputFunction, inputValues), Float64;
- initial_affect = true,
- save_positions=(saveAtEvent,saveAtEvent))
- push!(callbacks, timeEventCb)
+ save_positions=(false,false))
+ push!(cbs, eventCb)
+ end
+
+ # use step callback always if we have inputs or need event handling (or just want to see our simulation progress)
+ if hasInputs || c.fmu.hasStateEvents || c.fmu.hasTimeEvents || showProgress
+ stepCb = FunctionCallingCallback((x, t, integrator) -> stepCompleted(c, x, t, integrator, _inputFunction, inputValueReferences, progressMeter, t_start, t_stop, fmusol);
+ func_everystep = true,
+ func_start = true)
+ push!(cbs, stepCb)
+ end
+
+ if savingValues
+ fmusol.values = SavedValues(Float64, Tuple{collect(Float64 for i in 1:length(recordValues))...})
+ fmusol.valueReferences = copy(recordValues)
+
+ if saveat === nothing
+ savingCB = SavingCallback((u,t,integrator) -> saveValues(c, recordValues, u, t, integrator, _inputFunction, inputValueReferences),
+ fmusol.values)
+ else
+ savingCB = SavingCallback((u,t,integrator) -> saveValues(c, recordValues, u, t, integrator, _inputFunction, inputValueReferences),
+ fmusol.values,
+ saveat=saveat)
end
+
+ push!(cbs, savingCB)
+ end
+
+ # if auto_dt == true
+ # @assert solver !== nothing "fmi2SimulateME(...): `auto_dt=true` but no solver specified, this is not allowed."
+ # tmpIntegrator = init(c.problem, solver)
+ # dt = auto_dt_reset!(tmpIntegrator)
+ # end
+
+ solveKwargs = Dict{Symbol, Any}()
+
+ if dt !== nothing
+ solveKwargs[:dt] = dt
+ end
+
+ if tolerance !== nothing
+ solveKwargs[:reltol] = tolerance
+ end
+
+ if saveat !== nothing
+ solveKwargs[:saveat] = saveat
end
if solver === nothing
- solution = solve(problem; callback = CallbackSet(callbacks...), saveat = saveat, kwargs...)
+ fmusol.states = solve(c.problem; callback = CallbackSet(cbs...), dtmax=dtmax, solveKwargs..., kwargs...)
else
- solution = solve(problem, solver; callback = CallbackSet(callbacks...), saveat = saveat, kwargs...)
+ fmusol.states = solve(c.problem, solver; callback = CallbackSet(cbs...), dtmax=dtmax, solveKwargs..., kwargs...)
end
- if savingValues
- return solution, savedValues
- else
- return solution
+ fmusol.success = (fmusol.states.retcode == :Success)
+
+ # cleanup progress meter
+ if showProgress
+ ProgressMeter.finish!(progressMeter)
end
+
+ finishFMU(fmu, c, terminate, freeInstance)
+
+ return fmusol
end
-"""
-Source: FMISpec3.0, Version D5ef1c1: 4.3. Code Examples
+# wrapper
+function fmi3SimulateCS(c::FMU3Instance, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing; kwargs...)
+ fmi3SimulateCS(c.fmu, c, t_start, t_stop; kwargs...)
+end
+
+############ Co-Simulation ############
+
+"""
Starts a simulation of the Co-Simulation FMU instance.
-Returns a tuple of (success::Bool, DiffEqCallbacks.SavedValues) with success = `true` or `false`.
-If keyword `recordValues` is not set, a tuple of type (success::Bool, nothing) is returned for consitency.
+Via the optional keyword arguments `inputValues` and `inputFunction`, a custom input function `f(c, t)` or `f(t)` with time `t` and component `c` can be defined, that should return a array of values for `fmi3SetFloat64(..., inputValues, inputFunction(...))`.
-ToDo: Improve Documentation.
+Keywords:
+ - recordValues: Array of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues`
+ - saveat: Time points to save values at (interpolated)
+ - setup: Boolean, if FMU should be setup (default=true)
+ - reset: Boolean, if FMU should be reset before simulation (default reset=setup)
+ - inputValueReferences: Array of input variables (strings or variableIdentifiers) to set at every simulation step
+ - inputFunction: Function to retrieve the values to set the inputs to
+ - parameters: Dictionary of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization
+Returns:
+ - If keyword `recordValues` is not set, a boolean `success` is returned (simulation success).
+ - If keyword `recordValues` is set, a tuple of type (true, DiffEqCallbacks.SavedValues) or (false, nothing).
"""
-function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
- recordValues::fmi3ValueReferenceFormat = nothing,
- saveat = [],
- setup::Bool = true,
- reset = nothing,
- inputValues::fmi3ValueReferenceFormat = nothing,
- inputFunction = nothing)
+function fmi3SimulateCS(fmu::FMU3, c::Union{FMU3Instance, Nothing}=nothing, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+ tolerance::Union{Real, Nothing} = nothing,
+ dt::Union{Real, Nothing} = nothing,
+ recordValues::fmi3ValueReferenceFormat = nothing,
+ saveat = [],
+ setup::Union{Bool, Nothing} = nothing,
+ reset::Union{Bool, Nothing} = nothing,
+ instantiate::Union{Bool, Nothing} = nothing,
+ freeInstance::Union{Bool, Nothing} = nothing,
+ terminate::Union{Bool, Nothing} = nothing,
+ inputValueReferences::fmi3ValueReferenceFormat = nothing,
+ inputFunction = nothing,
+ showProgress::Bool=true,
+ parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing)
- recordValues = prepareValueReference(c, recordValues)
- inputValues = prepareValueReference(c, inputValues)
- variableSteps = c.fmu.modelDescription.CScanHandleVariableCommunicationStepSize
+ @assert fmi3IsCoSimulation(fmu) "fmi3SimulateCS(...): This function supports Co-Simulation FMUs only."
+
+ # input function handling
+ _inputFunction = nothing
+ if inputFunction != nothing
+ if hasmethod(inputFunction, Tuple{fmi3Float64})
+ _inputFunction = (c, t) -> inputFunction(t)
+ else
+ _inputFunction = inputFunctiont
+ end
+ @assert hasmethod(_inputFunction, Tuple{FMU3Instance, fmi3Float64}) "The given input function does not fit the needed input function pattern for CS-FMUs, which are: \n- `inputFunction(t::fmi3Float64)`\n- `inputFunction(comp::FMU3Instance, t::fmi3Float64)`"
+ end
+
+ fmusol = FMU3Solution(fmu)
- success = false
- savedValues = nothing
+
+ recordValues = prepareValueReference(fmu, recordValues)
+ inputValueReferences = prepareValueReference(fmu, inputValueReferences)
+ hasInputs = (length(inputValueReferences) > 0)
+
+ variableSteps = fmi3IsCoSimulation(fmu) && fmu.modelDescription.coSimulation.canHandleVariableCommunicationStepSize
+
+ t_start = t_start === nothing ? fmi3GetDefaultStartTime(fmu.modelDescription) : t_start
+ t_start = t_start === nothing ? 0.0 : t_start
+ t_stop = t_stop === nothing ? fmi3GetDefaultStopTime(fmu.modelDescription) : t_stop
+ t_stop = t_stop === nothing ? 1.0 : t_stop
+ tolerance = tolerance === nothing ? fmi3GetDefaultTolerance(fmu.modelDescription) : tolerance
+ tolerance = tolerance === nothing ? 0.0 : tolerance
+ dt = dt === nothing ? fmi3GetDefaultStepSize(fmu.modelDescription) : dt
+ dt = dt === nothing ? 1e-3 : dt
+
+ c, _ = prepareFMU(fmu, c, fmi3TypeCoSimulation, instantiate, terminate, reset, setup, parameters, t_start, t_stop, tolerance; inputFunction=_inputFunction, inputValueReferences=inputValueReferences)
# default setup
if length(saveat) == 0
- saveat = LinRange(t_start, t_stop, 100)
+ saveat = t_start:dt:t_stop
end
- dt = (t_stop - t_start) / length(saveat)
# setup if no variable steps
if variableSteps == false
@@ -333,26 +1108,16 @@ function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
end
end
- # auto correct reset if only setup is given
- if reset === nothing
- reset = setup
- end
- @assert !(setup==false && reset==true) "fmi3SimulateME(...): keyword argument `setup=false`, but `reset=true`. This may cause a FMU crash."
-
-
- if reset
- fmi3Reset(c)
- end
-
- if setup
- fmi3EnterInitializationMode(c, t_start, t_stop)
- fmi3ExitInitializationMode(c)
- end
-
t = t_start
record = length(recordValues) > 0
+ progressMeter = nothing
+ if showProgress
+ progressMeter = ProgressMeter.Progress(1000; desc="Simulating CS-FMU ...", color=:blue, dt=1.0) #, barglyphs=ProgressMeter.BarGlyphs("[=> ]"))
+ ProgressMeter.update!(progressMeter, 0) # show it!
+ end
+
#numDigits = length(string(round(Integer, 1/dt)))
noSetFMUStatePriorToCurrentPoint = fmi3False
eventEncountered = fmi3False
@@ -361,13 +1126,14 @@ function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
lastSuccessfulTime = fmi3Float64(0.0)
if record
- savedValues = SavedValues(Float64, Tuple{collect(Float64 for i in 1:length(recordValues))...})
+ fmusol.values = SavedValues(Float64, Tuple{collect(Float64 for i in 1:length(recordValues))...} )
+ fmusol.valueReferences = copy(recordValues)
i = 1
- values = (fmi3GetFloat64(c, recordValues)...,)
- DiffEqCallbacks.copyat_or_push!(savedValues.t, i, t)
- DiffEqCallbacks.copyat_or_push!(savedValues.saveval, i, values, Val{false})
+ svalues = (fmi3GetFloat64(c, recordValues)...,)
+ DiffEqCallbacks.copyat_or_push!(fmusol.values.t, i, t)
+ DiffEqCallbacks.copyat_or_push!(fmusol.values.saveval, i, svalues, Val{false})
while t < t_stop
if variableSteps
@@ -378,11 +1144,11 @@ function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
end
end
- if inputFunction !== nothing
- fmi3SetFloat64(c, inputValues, inputFunction(t))
+ if _inputFunction !== nothing
+ fmi3SetFloat64(fmu, inputValueReferences, _inputFunction(c, t))
end
- fmi3DoStep(c, t, dt, fmi3True, eventEncountered, terminateSimulation, earlyReturn, lastSuccessfulTime)
+ fmi3DoStep!(fmu, t, dt, true, eventEncountered, terminateSimulation, earlyReturn, lastSuccessfulTime)
if eventEncountered == fmi3True
@warn "Event handling"
end
@@ -395,13 +1161,21 @@ function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
t = t + dt #round(t + dt, digits=numDigits)
i += 1
- values = (fmi3GetFloat64(c, recordValues)...,)
- DiffEqCallbacks.copyat_or_push!(savedValues.t, i, t)
- DiffEqCallbacks.copyat_or_push!(savedValues.saveval, i, values, Val{false})
+ svalues = (fmi3GetFloat64(c, recordValues)...,)
+ DiffEqCallbacks.copyat_or_push!(fmusol.values.t, i, t)
+ DiffEqCallbacks.copyat_or_push!(fmusol.values.saveval, i, svalues, Val{false})
+
+ if progressMeter !== nothing
+ ProgressMeter.update!(progressMeter, floor(Integer, 1000.0*(t-t_start)/(t_stop-t_start)) )
+ end
+ end
+
+ if progressMeter !== nothing
+ ProgressMeter.finish!(progressMeter)
end
- success = true
- return success, savedValues
+ fmusol.success = true
+
else
i = 1
while t < t_stop
@@ -413,11 +1187,11 @@ function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
end
end
- if inputFunction !== nothing
- fmi3SetFloat64(c, inputValues, inputFunction(t))
+ if _inputFunction !== nothing
+ fmi3SetFloat64(fmu, inputValues, _inputFunction(c, t))
end
- fmi3DoStep(c, t, dt, fmi3True, eventEncountered, terminateSimulation, earlyReturn, lastSuccessfulTime)
+ fmi3DoStep!(fmu, t, dt, true, eventEncountered, terminateSimulation, earlyReturn, lastSuccessfulTime)
if eventEncountered == fmi3True
@warn "Event handling"
end
@@ -429,40 +1203,75 @@ function fmi3SimulateCS(c::fmi3Instance, t_start::Real, t_stop::Real;
end
t = t + dt #round(t + dt, digits=numDigits)
i += 1
+
+ if progressMeter !== nothing
+ ProgressMeter.update!(progressMeter, floor(Integer, 1000.0*(t-t_start)/(t_stop-t_start)) )
+ end
+ end
+
+ if progressMeter !== nothing
+ ProgressMeter.finish!(progressMeter)
end
- success = true
- return success
+ fmusol.success = true
end
+
+ finishFMU(fmu, c, terminate, freeInstance)
+
+ return fmusol
end
# TODO simulate ScheduledExecution
-function fmi3SimulateSE(c::fmi3Instance, t_start::Real, t_stop::Real;
+function fmi3SimulateSE(fmu::FMU3, c::Union{FMU3Instance, Nothing}=nothing, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing;
+ tolerance::Union{Real, Nothing} = nothing,
+ dt::Union{Real, Nothing} = nothing,
recordValues::fmi3ValueReferenceFormat = nothing,
saveat = [],
- setup::Bool = true,
- reset = nothing,
- inputValues::fmi3ValueReferenceFormat = nothing,
- inputFunction = nothing)
- @assert false "Not implemented"
+ setup::Union{Bool, Nothing} = nothing,
+ reset::Union{Bool, Nothing} = nothing,
+ instantiate::Union{Bool, Nothing} = nothing,
+ freeInstance::Union{Bool, Nothing} = nothing,
+ terminate::Union{Bool, Nothing} = nothing,
+ inputValueReferences::fmi3ValueReferenceFormat = nothing,
+ inputFunction = nothing,
+ showProgress::Bool=true,
+ parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing) @assert false "Not implemented"
end
+##### CS & ME #####
+
+# wrapper
+function fmi3Simulate(c::FMU3Instance, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing; kwargs...)
+ fmi3Simulate(c.fmu, c, t_start, t_stop; kwargs...)
+end
+
"""
-Starts a simulation of the fmu instance for the matching fmu type, if both types are available, CS is preferred.
+Starts a simulation of the FMU instance for the matching FMU type, if both types are available, CS is preferred.
+
+Keywords:
+ - recordValues: Array of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues`
+ - setup: Boolean, if FMU should be setup (default=true)
+ - reset: Boolean, if FMU should be reset before simulation (default reset=setup)
+ - inputValues: Array of input variables (strings or variableIdentifiers) to set at every simulation step
+ - inputFunction: Function to retrieve the values to set the inputs to
+ - saveat: [ME only] Time points to save values at (interpolated)
+ - solver: [ME only] Any Julia-supported ODE-solver (default is Tsit5)
+ - customFx: [ME only, deperecated] Ability to give a custom state derivative function ẋ=f(x,t)
Returns:
- - a tuple of (success::Bool, DiffEqCallbacks.SavedValues) with success = `true` or `false` for CS-FMUs
- - a tuple of (ODESolution, DiffEqCallbacks.SavedValues) for ME-FMUs
- - if keyword `recordValues` is not set, a tuple of type (..., nothing)
-
-ToDo: Improve Documentation.
+ - `success::Bool` for CS-FMUs
+ - `ODESolution` for ME-FMUs
+ - if keyword `recordValues` is set, a tuple of type (success::Bool, DiffEqCallbacks.SavedValues) for CS-FMUs
+ - if keyword `recordValues` is set, a tuple of type (ODESolution, DiffEqCallbacks.SavedValues) for ME-FMUs
"""
-function fmi3Simulate(c::fmi3Instance, t_start::Real = 0.0, t_stop::Real = 1.0;kwargs...)
-
- if fmi3IsCoSimulation(c.fmu)
- return fmi3SimulateCS(c, t_start, t_stop; kwargs...)
- elseif fmi3IsModelExchange(c.fmu)
- return fmi3SimulateME(c, t_start, t_stop; kwargs...)
+function fmi3Simulate(fmu::FMU3, c::Union{FMU3Instance, Nothing}=nothing, t_start::Union{Real, Nothing} = nothing, t_stop::Union{Real, Nothing} = nothing; kwargs...)
+
+ if fmu.type == fmi3TypeCoSimulation
+ return fmi3SimulateCS(fmu, c, t_start, t_stop; kwargs...)
+ elseif fmu.type == fmi3TypeModelExchange
+ return fmi3SimulateME(fmu, c, t_start, t_stop; kwargs...)
+ elseif fmu.type == fmi3TypeScheduledExecution
+ return fmi3SimulateSE(fmu, c, t_start, t_stop; kwargs...)
else
error(unknownFMUType)
end
diff --git a/test/cs_me.jl b/test/FMI2/cs_me.jl
similarity index 100%
rename from test/cs_me.jl
rename to test/FMI2/cs_me.jl
diff --git a/test/dir_ders.jl b/test/FMI2/dir_ders.jl
similarity index 100%
rename from test/dir_ders.jl
rename to test/FMI2/dir_ders.jl
diff --git a/test/getter_setter.jl b/test/FMI2/getter_setter.jl
similarity index 100%
rename from test/getter_setter.jl
rename to test/FMI2/getter_setter.jl
diff --git a/test/load_save.jl b/test/FMI2/load_save.jl
similarity index 100%
rename from test/load_save.jl
rename to test/FMI2/load_save.jl
diff --git a/test/model_description.jl b/test/FMI2/model_description.jl
similarity index 100%
rename from test/model_description.jl
rename to test/FMI2/model_description.jl
diff --git a/test/plots.jl b/test/FMI2/plots.jl
similarity index 100%
rename from test/plots.jl
rename to test/FMI2/plots.jl
diff --git a/test/sens.jl b/test/FMI2/sens.jl
similarity index 100%
rename from test/sens.jl
rename to test/FMI2/sens.jl
diff --git a/test/sim_CS.jl b/test/FMI2/sim_CS.jl
similarity index 100%
rename from test/sim_CS.jl
rename to test/FMI2/sim_CS.jl
diff --git a/test/sim_ME.jl b/test/FMI2/sim_ME.jl
similarity index 100%
rename from test/sim_ME.jl
rename to test/FMI2/sim_ME.jl
diff --git a/test/sim_auto.jl b/test/FMI2/sim_auto.jl
similarity index 100%
rename from test/sim_auto.jl
rename to test/FMI2/sim_auto.jl
diff --git a/test/state.jl b/test/FMI2/state.jl
similarity index 100%
rename from test/state.jl
rename to test/FMI2/state.jl
diff --git a/test/FMI3/cs_me.jl b/test/FMI3/cs_me.jl
new file mode 100644
index 00000000..52dca0c6
--- /dev/null
+++ b/test/FMI3/cs_me.jl
@@ -0,0 +1,57 @@
+###############
+# Prepare FMU #
+###############
+
+t_start = 0.0
+t_stop = 1.0
+
+myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.16", "3.0")
+# @test fmiIsCoSimulation(myFMU)
+# @test fmiIsModelExchange(myFMU)
+# inst = fmiInstantiate!(myFMU; loggingOn=false)
+# @test inst != 0
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = inst
+# end
+# sol = fmiSimulateCS(fmuStruct, t_start, t_stop)
+# @test sol.success
+# sol = fmiSimulateME(fmuStruct, t_start, t_stop)
+# @test sol.success
+# fmiUnload(myFMU)
+
+
+
+# myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]; type=:ME)
+# @test myFMU.type == FMI.fmi3TypeModelExchange
+# inst = fmi3InstantiateModelExchange!(myFMU; loggingOn=false)
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = inst
+# end
+# sol = fmiSimulate(fmuStruct, t_start, t_stop)
+# @test sol.success
+# fmiUnload(myFMU)
+
+
+
+# myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]; type=:CS)
+@test myFMU.type == FMI.fmi3TypeCoSimulation
+inst = fmi3InstantiateCoSimulation!(myFMU; loggingOn=false)
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = inst
+end
+sol = fmiSimulate(fmuStruct, t_start, t_stop)
+@test sol.success
+fmiUnload(myFMU)
\ No newline at end of file
diff --git a/test/FMI3/dir_ders.jl b/test/FMI3/dir_ders.jl
new file mode 100644
index 00000000..6e19ba32
--- /dev/null
+++ b/test/FMI3/dir_ders.jl
@@ -0,0 +1,55 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+using FMI.FMIImport.FMICore: fmi2Real
+
+myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+comp = fmiInstantiate!(myFMU; loggingOn=false)
+@test comp != 0
+
+# choose FMU or FMUComponent
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = comp
+end
+@assert fmuStruct != nothing "Unknwon fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+@test fmiSetupExperiment(fmuStruct) == 0
+@test fmiEnterInitializationMode(fmuStruct) == 0
+@test fmiExitInitializationMode(fmuStruct) == 0
+
+targetValues = [[0.0, -10.0], [1.0, 0.0]]
+dir_ders_buffer = zeros(fmi2Real, 2)
+sample_ders_buffer = zeros(fmi2Real, 2, 1)
+for i in 1:fmiGetNumberOfStates(myFMU)
+
+ if fmiProvidesDirectionalDerivative(myFMU)
+ # multi derivatives calls
+ sample_ders = fmiSampleDirectionalDerivative(fmuStruct, myFMU.modelDescription.derivativeValueReferences, [myFMU.modelDescription.stateValueReferences[i]])
+ fmiSampleDirectionalDerivative!(fmuStruct, myFMU.modelDescription.derivativeValueReferences, [myFMU.modelDescription.stateValueReferences[i]], sample_ders_buffer)
+
+ @test sum(abs.(sample_ders[:,1] - targetValues[i])) < 1e-3
+ @test sum(abs.(sample_ders_buffer[:,1] - targetValues[i])) < 1e-3
+
+ dir_ders = fmiGetDirectionalDerivative(fmuStruct, myFMU.modelDescription.derivativeValueReferences, [myFMU.modelDescription.stateValueReferences[i]])
+ @test fmiGetDirectionalDerivative!(fmuStruct, myFMU.modelDescription.derivativeValueReferences, [myFMU.modelDescription.stateValueReferences[i]], dir_ders_buffer) == 0
+
+ @test sum(abs.(dir_ders - targetValues[i])) < 1e-3
+ @test sum(abs.(dir_ders_buffer - targetValues[i])) < 1e-3
+
+ # single derivative call
+ dir_der = fmiGetDirectionalDerivative(fmuStruct, myFMU.modelDescription.derivativeValueReferences[1], myFMU.modelDescription.stateValueReferences[1])
+ @test dir_der == targetValues[1][1]
+
+ else
+ @warn "Skipping directional derivative testing, FMU from $(ENV["EXPORTINGTOOL"]) doesn't support directional derivatives."
+ end
+
+end
+
+fmiUnload(myFMU)
diff --git a/test/FMI3/getter_setter.jl b/test/FMI3/getter_setter.jl
new file mode 100644
index 00000000..09efb54e
--- /dev/null
+++ b/test/FMI3/getter_setter.jl
@@ -0,0 +1,143 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+###############
+# Prepare FMU #
+###############
+
+myFMU = fmiLoad("IO", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+comp = fmiInstantiate!(myFMU; loggingOn=false)
+@test comp != 0
+
+# choose FMU or FMUComponent
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = comp
+end
+@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+@test fmiSetupExperiment(fmuStruct, 0.0) == 0
+
+@test fmiEnterInitializationMode(fmuStruct) == 0
+
+realValueReferences = ["p_real", "u_real"]
+integerValueReferences = ["p_integer", "u_integer"]
+booleanValueReferences = ["p_boolean", "u_boolean"]
+stringValueReferences = ["p_string", "p_string"]
+
+#########################
+# Testing Single Values #
+#########################
+
+rndReal = 100 * rand()
+rndInteger = round(Integer, 100 * rand())
+rndBoolean = rand() > 0.5
+rndString = Random.randstring(12)
+
+cacheReal = 0.0
+cacheInteger = 0
+cacheBoolean = false
+cacheString = ""
+
+@test fmiSetReal(fmuStruct, realValueReferences[1], rndReal) == 0
+@test fmiGetReal(fmuStruct, realValueReferences[1]) == rndReal
+@test fmiSetReal(fmuStruct, realValueReferences[1], -rndReal) == 0
+@test fmiGetReal(fmuStruct, realValueReferences[1]) == -rndReal
+
+@test fmiSetInteger(fmuStruct, integerValueReferences[1], rndInteger) == 0
+@test fmiGetInteger(fmuStruct, integerValueReferences[1]) == rndInteger
+@test fmiSetInteger(fmuStruct, integerValueReferences[1], -rndInteger) == 0
+@test fmiGetInteger(fmuStruct, integerValueReferences[1]) == -rndInteger
+
+@test fmiSetBoolean(fmuStruct, booleanValueReferences[1], rndBoolean) == 0
+@test fmiGetBoolean(fmuStruct, booleanValueReferences[1]) == rndBoolean
+@test fmiSetBoolean(fmuStruct, booleanValueReferences[1], !rndBoolean) == 0
+@test fmiGetBoolean(fmuStruct, booleanValueReferences[1]) == !rndBoolean
+
+@test fmiSetString(fmuStruct, stringValueReferences[1], rndString) == 0
+@test fmiGetString(fmuStruct, stringValueReferences[1]) == rndString
+
+fmiSet(fmuStruct,
+ [realValueReferences[1], integerValueReferences[1], booleanValueReferences[1], stringValueReferences[1]],
+ [rndReal, rndInteger, rndBoolean, rndString])
+@test fmiGet(fmuStruct,
+ [realValueReferences[1], integerValueReferences[1], booleanValueReferences[1], stringValueReferences[1]]) ==
+ [rndReal, rndInteger, rndBoolean, rndString]
+
+#@test fmiGetStartValue(fmuStruct, "p_enumeration") == "myEnumeration1"
+# TODO implement for fmi3
+# @test fmiGetStartValue(fmuStruct, "p_string") == "Hello World!"
+# @test fmiGetStartValue(fmuStruct, "p_real") == 0.0
+
+##################
+# Testing Arrays #
+##################
+
+rndReal = [100 * rand(), 100 * rand()]
+rndInteger = [round(Integer, 100 * rand()), round(Integer, 100 * rand())]
+rndBoolean = [(rand() > 0.5), (rand() > 0.5)]
+tmp = Random.randstring(8)
+rndString = [tmp, tmp]
+
+cacheReal = [0.0, 0.0]
+cacheInteger = [FMI.fmi2Integer(0), FMI.fmi2Integer(0)]
+cacheBoolean = [FMI.fmi2Boolean(false), FMI.fmi2Boolean(false)]
+cacheString = [pointer(""), pointer("")]
+
+@test fmiSetReal(fmuStruct, realValueReferences, rndReal) == 0
+@test fmiGetReal(fmuStruct, realValueReferences) == rndReal
+fmiGetReal!(fmuStruct, realValueReferences, cacheReal)
+@test cacheReal == rndReal
+@test fmiSetReal(fmuStruct, realValueReferences, -rndReal) == 0
+@test fmiGetReal(fmuStruct, realValueReferences) == -rndReal
+fmiGetReal!(fmuStruct, realValueReferences, cacheReal)
+@test cacheReal == -rndReal
+
+@test fmiSetInteger(fmuStruct, integerValueReferences, rndInteger) == 0
+@test fmiGetInteger(fmuStruct, integerValueReferences) == rndInteger
+fmiGetInteger!(fmuStruct, integerValueReferences, cacheInteger)
+@test cacheInteger == rndInteger
+@test fmiSetInteger(fmuStruct, integerValueReferences, -rndInteger) == 0
+@test fmiGetInteger(fmuStruct, integerValueReferences) == -rndInteger
+fmiGetInteger!(fmuStruct, integerValueReferences, cacheInteger)
+@test cacheInteger == -rndInteger
+
+@test fmiSetBoolean(fmuStruct, booleanValueReferences, rndBoolean) == 0
+@test fmiGetBoolean(fmuStruct, booleanValueReferences) == rndBoolean
+fmiGetBoolean!(fmuStruct, booleanValueReferences, cacheBoolean)
+@test cacheBoolean == rndBoolean
+not_rndBoolean = collect(!b for b in rndBoolean)
+@test fmiSetBoolean(fmuStruct, booleanValueReferences, not_rndBoolean) == 0
+@test fmiGetBoolean(fmuStruct, booleanValueReferences) == not_rndBoolean
+fmiGetBoolean!(fmuStruct, booleanValueReferences, cacheBoolean)
+@test cacheBoolean == not_rndBoolean
+
+@test fmiSetString(fmuStruct, stringValueReferences, rndString) == 0
+@test fmiGetString(fmuStruct, stringValueReferences) == rndString
+fmiGetString!(fmuStruct, stringValueReferences, cacheString)
+@test unsafe_string.(cacheString) == rndString
+
+#@test fmiGetStartValue(fmuStruct, ["p_enumeration", "p_string", "p_real"]) == ["myEnumeration1", "Hello World!", 0.0]
+@test fmiGetStartValue(fmuStruct, ["p_string", "p_real"]) == ["Hello World!", 0.0]
+
+# Testing input/output derivatives
+dirs = fmiGetRealOutputDerivatives(fmuStruct, ["y_real"], ones(FMI.fmi2Integer, 1))
+@test dirs == -Inf # at this point, derivative is undefined
+@test fmiSetRealInputDerivatives(fmuStruct, ["u_real"], ones(FMI.fmi2Integer, 1), zeros(1)) == 0
+
+@test fmiExitInitializationMode(fmuStruct) == 0
+@test fmiDoStep(fmuStruct, 0.1) == 0
+
+dirs = fmiGetRealOutputDerivatives(fmuStruct, ["y_real"], ones(FMI.fmi2Integer, 1))
+@test dirs == 0.0
+
+############
+# Clean up #
+############
+
+fmiUnload(myFMU)
diff --git a/test/FMI3/load_save.jl b/test/FMI3/load_save.jl
new file mode 100644
index 00000000..235bec76
--- /dev/null
+++ b/test/FMI3/load_save.jl
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+using JLD2
+
+# our simulation setup
+t_start = 0.0
+t_stop = 8.0
+
+# load the FMU container
+myFMU = fmiLoad("SpringFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+recordValues = ["mass.s", "mass.v"]
+solutionME = fmiSimulateME(myFMU, t_start, t_stop; recordValues=recordValues)
+solutionCS = fmiSimulateCS(myFMU, t_start, t_stop; recordValues=recordValues)
+
+# ME
+
+fmiSaveSolution(solutionME, "solutionME.jld2")
+anotherSolutionME = fmiLoadSolution("solutionME.jld2")
+
+@test solutionME.success == true
+@test solutionME.success == anotherSolutionME.success
+@test solutionME.states.u == anotherSolutionME.states.u
+@test solutionME.states.t == anotherSolutionME.states.t
+@test solutionME.values.saveval == anotherSolutionME.values.saveval
+@test solutionME.values.t == anotherSolutionME.values.t
+
+# ME-BONUS: events
+@test solutionME.events == anotherSolutionME.events
+
+# CS
+
+fmiSaveSolution(solutionCS, "solutionCS.jld2")
+anotherSolutionCS = fmiLoadSolution("solutionCS.jld2")
+
+@test solutionCS.success == true
+@test solutionCS.success == anotherSolutionCS.success
+@test solutionCS.values.saveval == anotherSolutionCS.values.saveval
+@test solutionCS.values.t == anotherSolutionCS.values.t
+
+# unload the FMU, remove unpacked data on disc ("clean up")
+fmiUnload(myFMU)
\ No newline at end of file
diff --git a/test/FMI3/model_description.jl b/test/FMI3/model_description.jl
new file mode 100644
index 00000000..af364e98
--- /dev/null
+++ b/test/FMI3/model_description.jl
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+using FMI: fmi2VariableNamingConventionStructured, fmi2DependencyKindDependent, fmi2DependencyKindFixed
+
+myFMU = fmiLoad("SpringDFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+@test fmiGetVersion(myFMU) == "2.0"
+@test fmiGetTypesPlatform(myFMU) == "default"
+
+@test fmiGetModelName(myFMU) == "SpringFrictionPendulum1D"
+@test fmiGetVariableNamingConvention(myFMU) == fmi2VariableNamingConventionStructured
+@test fmiIsCoSimulation(myFMU) == true
+@test fmiIsModelExchange(myFMU) == true
+
+@test fmiGetGUID(myFMU) == "{df491d8d-0598-4495-913e-5b025e54d7f2}"
+@test fmiGetGenerationTool(myFMU) == "Dymola Version 2022x (64-bit), 2021-10-08"
+@test fmiGetGenerationDateAndTime(myFMU) == "2022-03-03T15:09:18Z"
+@test fmiGetNumberOfEventIndicators(myFMU) == 24
+@test fmiCanGetSetState(myFMU) == true
+@test fmiCanSerializeFMUstate(myFMU) == true
+@test fmiProvidesDirectionalDerivative(myFMU) == true
+
+depMtx = fmi2GetDependencies(myFMU)
+@test fmi2DependencyKindFixed in depMtx
+@test fmi2DependencyKindDependent in depMtx
+
+@test fmi2GetDefaultStartTime(myFMU.modelDescription) ≈ 0.0
+@test fmi2GetDefaultStopTime(myFMU.modelDescription) ≈ 1.0
+@test fmi2GetDefaultTolerance(myFMU.modelDescription) ≈ 1e-4
+@test fmi2GetDefaultStepSize(myFMU.modelDescription) === nothing
+
+fmiUnload(myFMU)
diff --git a/test/FMI3/plots.jl b/test/FMI3/plots.jl
new file mode 100644
index 00000000..217ce953
--- /dev/null
+++ b/test/FMI3/plots.jl
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+using Plots
+
+# our simulation setup
+t_start = 0.0
+t_stop = 8.0
+
+# load the FMU container
+myFMU = fmiLoad("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# print some useful FMU-information into the REPL
+fmiInfo(myFMU)
+
+# make an instance from the FMU
+fmiInstantiate!(myFMU)
+
+recordValues = ["mass.s", "mass.v"]
+solutionME = fmiSimulateME(myFMU, t_start, t_stop; recordValues=recordValues)
+solutionCS = fmiSimulateCS(myFMU, t_start, t_stop; recordValues=recordValues)
+
+# plot the results
+fig = fmiPlot(solutionME)
+
+fig = Plots.plot()
+fmiPlot!(fig, solutionME)
+
+fig = fmiPlot(solutionCS)
+
+fig = Plots.plot()
+fmiPlot!(fig, solutionCS)
+
+# unload the FMU, remove unpacked data on disc ("clean up")
+fmiUnload(myFMU)
\ No newline at end of file
diff --git a/test/FMI3/sens.jl b/test/FMI3/sens.jl
new file mode 100644
index 00000000..f34a5813
--- /dev/null
+++ b/test/FMI3/sens.jl
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+import ForwardDiff
+import Zygote
+
+using FMI.FMIImport: fmi3SampleDirectionalDerivative, fmi3GetJacobian, fmi3SetContinuousStates
+
+FMUPaths = [get_model_filename("SpringFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"]),
+ get_model_filename("BouncingBall1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])]
+
+t_start = 0.0
+t_step = 0.01
+t_stop = 5.0
+tData = t_start:t_step:t_stop
+
+for FMUPath in FMUPaths
+ myFMU = fmiLoad(FMUPath)
+ comp = fmiInstantiate!(myFMU; loggingOn=false)
+
+ fmiSetupExperiment(comp, t_start, t_stop)
+ fmiEnterInitializationMode(comp)
+ fmiExitInitializationMode(comp)
+
+ x0 = fmiGetContinuousStates(comp)
+ numStates = length(x0)
+
+ dx = zeros(numStates)
+ t = 0.0
+ p = []
+
+ # Jacobians for x0
+ FD_jac = ForwardDiff.jacobian(x -> FMI.fx(comp, dx, x, p, t), x0)
+ ZG_jac = Zygote.jacobian(FMI.fx, comp, dx, x0, p, t)[3]
+ fmiSetContinuousStates(comp, x0)
+ samp_jac = fmi3SampleDirectionalDerivative(comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+ auto_jac = fmi3GetJacobian(comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+
+ @test (abs.(auto_jac - FD_jac) .< ones(numStates, numStates).*1e-6) == ones(Bool, numStates, numStates)
+ @test (abs.(auto_jac - ZG_jac) .< ones(numStates, numStates).*1e-6) == ones(Bool, numStates, numStates)
+ @test (abs.(auto_jac - samp_jac) .< ones(numStates, numStates).*1e-6) == ones(Bool, numStates, numStates)
+ #@info "A: $(auto_jac)"
+ #@info "S: $(samp_jac)"
+
+ # Jacobians for random x0 / dx
+ x0 = x0 + rand(numStates)
+ dx = dx + rand(numStates)
+ FD_jac = ForwardDiff.jacobian(x -> FMI.fx(comp, dx, x, p, t), x0)
+ ZG_jac = Zygote.jacobian(FMI.fx, comp, dx, x0, p, t)[3]
+ fmi2SetContinuousStates(comp, x0)
+ samp_jac = fmi3SampleDirectionalDerivative(comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+ auto_jac = fmi3GetJacobian(comp, comp.fmu.modelDescription.derivativeValueReferences, comp.fmu.modelDescription.stateValueReferences)
+
+ @test (abs.(auto_jac - FD_jac) .< ones(numStates, numStates).*1e-6) == ones(Bool, numStates, numStates)
+ @test (abs.(auto_jac - ZG_jac) .< ones(numStates, numStates).*1e-6) == ones(Bool, numStates, numStates)
+ @test (abs.(auto_jac - samp_jac) .< ones(numStates, numStates).*1e-6) == ones(Bool, numStates, numStates)
+
+ fmiUnload(myFMU)
+end
diff --git a/test/FMI3/sim_CS.jl b/test/FMI3/sim_CS.jl
new file mode 100644
index 00000000..4f1d6dfb
--- /dev/null
+++ b/test/FMI3/sim_CS.jl
@@ -0,0 +1,87 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+# case 1: CS-FMU Simulation
+
+myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.16", "3.0")
+
+comp = fmi3InstantiateCoSimulation!(myFMU; loggingOn=false)
+@test comp != 0
+
+# choose FMU or FMUComponent
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = comp
+end
+@assert fmuStruct !== nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+t_start = 0.0
+t_stop = 3.0
+
+# test without recording values (but why?)
+# solution = fmiSimulateCS(fmuStruct, t_start, t_stop)
+# @test solution.success
+
+# test with recording values
+solution = fmiSimulateCS(fmuStruct, t_start, t_stop; dt=1e-2, recordValues=["h", "v"])
+@test solution.success
+@test length(solution.values.saveval) == t_start:1e-2:t_stop |> length
+@test length(solution.values.saveval[1]) == 2
+
+t = solution.values.t
+s = collect(d[1] for d in solution.values.saveval)
+v = collect(d[2] for d in solution.values.saveval)
+@test t[1] == t_start
+@test t[end] == t_stop
+
+
+# reference values from Simulation in Dymola2020x (Dassl)
+@test s[1] == 1.0
+@test v[1] == 0.0
+
+if ENV["EXPORTINGTOOL"] == "ModelicaReferenceFMUs" # ToDo: Linux FMU was corrupted
+ @test s[end] ≈ 0.0 atol=1e-1
+ @test v[end] ≈ 0.0 atol=1e-1
+end
+
+fmiUnload(myFMU)
+
+# case 2: CS-FMU with input signal not supported with BouncingBall FMU
+
+# function extForce(t)
+# [sin(t)]
+# end
+
+# myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct !== nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# solution = fmiSimulateCS(fmuStruct, t_start, t_stop; dt=1e-2, recordValues=["mass.s", "mass.v"], inputValueReferences=["extForce"], inputFunction=extForce)
+# @test solution.success
+# @test length(solution.values.saveval) > 0
+# @test length(solution.values.t) > 0
+
+# @test t[1] == t_start
+# @test t[end] == t_stop
+
+# # reference values from Simulation in Dymola2020x (Dassl)
+# @test [solution.values.saveval[1]...] == [0.5, 0.0]
+# @test sum(abs.([solution.values.saveval[end]...] - [0.613371, 0.188633])) < 0.2
+# fmiUnload(myFMU)
+
diff --git a/test/FMI3/sim_ME.jl b/test/FMI3/sim_ME.jl
new file mode 100644
index 00000000..d0e9e0dd
--- /dev/null
+++ b/test/FMI3/sim_ME.jl
@@ -0,0 +1,261 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+using DifferentialEquations: Tsit5, Rosenbrock23
+
+t_start = 0.0
+t_stop = 3.0
+
+# case 1: ME-FMU with state events
+
+myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.16", "3.0")
+
+comp = fmi3InstantiateModelExchange!(myFMU; loggingOn=false)
+@test comp != 0
+
+# choose FMU or FMUComponent
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = comp
+end
+@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+solution = fmiSimulateME(fmuStruct, t_start, t_stop)
+@test length(solution.states.u) > 0
+@test length(solution.states.t) > 0
+
+@test solution.states.t[1] == t_start
+@test solution.states.t[end] == t_stop
+
+# reference values from Simulation in Dymola2020x (Dassl)
+@test solution.states.u[1] == [1.0, 0.0]
+@test sum(abs.(solution.states.u[end] - [1.0, 0.0])) < 0.1
+fmiUnload(myFMU)
+
+# case 2: ME-FMU with state and time events
+
+# myFMU = fmiLoad("SpringTimeFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# ### test without recording values
+
+# solution = fmiSimulateME(fmuStruct, t_start, t_stop; dtmax=0.001) # dtmax to force resolution
+# @test length(solution.states.u) > 0
+# @test length(solution.states.t) > 0
+
+# @test solution.states.t[1] == t_start
+# @test solution.states.t[end] == t_stop
+
+# # reference values from Simulation in Dymola2020x (Dassl)
+# @test solution.states.u[1] == [0.5, 0.0]
+# @test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01
+
+# ### test with recording values (variable step record values)
+
+# solution= fmiSimulateME(fmuStruct, t_start, t_stop; recordValues="mass.f", dtmax=0.001) # dtmax to force resolution
+# dataLength = length(solution.states.u)
+# @test dataLength > 0
+# @test length(solution.states.t) == dataLength
+# @test length(solution.values.saveval) == dataLength
+# @test length(solution.values.t) == dataLength
+
+# @test solution.states.t[1] == t_start
+# @test solution.states.t[end] == t_stop
+# @test solution.values.t[1] == t_start
+# @test solution.values.t[end] == t_stop
+
+# # reference values from Simulation in Dymola2020x (Dassl)
+# @test sum(abs.(solution.states.u[1] - [0.5, 0.0])) < 1e-4
+# @test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01
+# @test abs(solution.values.saveval[1][1] - 0.75) < 1e-4
+# @test sum(abs.(solution.values.saveval[end][1] - -0.54435 )) < 0.015
+
+# ### test with recording values (fixed step record values)
+
+# tData = t_start:0.1:t_stop
+# solution = fmiSimulateME(fmuStruct, t_start, t_stop; recordValues="mass.f", saveat=tData, dtmax=0.001) # dtmax to force resolution
+# @test length(solution.states.u) == length(tData)
+# @test length(solution.states.t) == length(tData)
+# @test length(solution.values.saveval) == length(tData)
+# @test length(solution.values.t) == length(tData)
+
+# @test solution.states.t[1] == t_start
+# @test solution.states.t[end] == t_stop
+# @test solution.values.t[1] == t_start
+# @test solution.values.t[end] == t_stop
+
+# # reference values from Simulation in Dymola2020x (Dassl)
+# @test sum(abs.(solution.states.u[1] - [0.5, 0.0])) < 1e-4
+# @test sum(abs.(solution.states.u[end] - [1.05444, 1e-10])) < 0.01
+# @test abs(solution.values.saveval[1][1] - 0.75) < 1e-4
+# @test sum(abs.(solution.values.saveval[end][1] - -0.54435 )) < 0.015
+
+# fmiUnload(myFMU)
+
+# # case 3a: ME-FMU without events, but with input signal (explicit solver: Tsit5)
+
+# function extForce(t)
+# [sin(t)]
+# end
+
+# myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# solution = fmiSimulateME(fmuStruct, t_start, t_stop; inputValueReferences=["extForce"], inputFunction=extForce, solver=Tsit5(), dtmax=0.001) # dtmax to force resolution
+# @test length(solution.states.u) > 0
+# @test length(solution.states.t) > 0
+
+# @test solution.states.t[1] == t_start
+# @test solution.states.t[end] == t_stop
+
+# # reference values from Simulation in Dymola2020x (Dassl)
+# @test solution.states.u[1] == [0.5, 0.0]
+# @test sum(abs.(solution.states.u[end] - [0.613371, 0.188633])) < 0.012
+# fmiUnload(myFMU)
+
+# # case 3b: ME-FMU without events, but with input signal (implicit solver: Rosenbrock23, autodiff)
+
+# myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# # ToDo: autodiff=true not working currently!
+# # solution = fmiSimulateME(fmuStruct, t_start, t_stop; inputValueReferences=["extForce"], inputFunction=extForce, solver=Rosenbrock23(autodiff=true), dtmax=0.001) # dtmax to force resolution
+# # @test length(solution.states.u) > 0
+# # @test length(solution.states.t) > 0
+
+# # @test solution.states.t[1] == t_start
+# # @test solution.states.t[end] == t_stop
+
+# # # reference values from Simulation in Dymola2020x (Dassl)
+# # @test solution.states.u[1] == [0.5, 0.0]
+# # @test sum(abs.(solution.states.u[end] - [0.613371, 0.188633])) < 0.01
+# fmiUnload(myFMU)
+
+# # case 3c: ME-FMU without events, but with input signal (implicit solver: Rosenbrock23, no autodiff)
+
+# myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# solution = fmiSimulateME(fmuStruct, t_start, t_stop; inputValueReferences=["extForce"], inputFunction=extForce, solver=Rosenbrock23(autodiff=false), dtmax=0.001) # dtmax to force resolution
+# @test length(solution.states.u) > 0
+# @test length(solution.states.t) > 0
+
+# @test solution.states.t[1] == t_start
+# @test solution.states.t[end] == t_stop
+
+# # reference values from Simulation in Dymola2020x (Dassl)
+# @test solution.states.u[1] == [0.5, 0.0]
+# @test sum(abs.(solution.states.u[end] - [0.613371, 0.188633])) < 0.01
+# fmiUnload(myFMU)
+
+# # case 4: ME-FMU without events, but saving value interpolation
+
+# myFMU = fmiLoad("SpringPendulumExtForce1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# solution = fmiSimulateME(fmuStruct, t_start, t_stop; saveat=tData, recordValues=myFMU.modelDescription.stateValueReferences)
+# @test length(solution.states.u) == length(tData)
+# @test length(solution.states.t) == length(tData)
+# @test length(solution.values.saveval) == length(tData)
+# @test length(solution.values.t) == length(tData)
+
+# for i in 1:length(tData)
+# @test sum(abs(solution.states.t[i] - solution.states.t[i])) < 1e-6
+# @test sum(abs(solution.states.u[i][1] - solution.values.saveval[i][1])) < 1e-6
+# @test sum(abs(solution.states.u[i][2] - solution.values.saveval[i][2])) < 1e-6
+# end
+
+# fmiUnload(myFMU)
+
+# # case 5: ME-FMU with different (random) start state
+
+# myFMU = fmiLoad("SpringFrictionPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# comp = fmiInstantiate!(myFMU; loggingOn=false)
+# @test comp != 0
+
+# # choose FMU or FMUComponent
+# fmuStruct = nothing
+# envFMUSTRUCT = ENV["FMUSTRUCT"]
+# if envFMUSTRUCT == "FMU"
+# fmuStruct = myFMU
+# elseif envFMUSTRUCT == "FMUCOMPONENT"
+# fmuStruct = comp
+# end
+# @assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+# rand_x0 = rand(2)
+# solution = fmiSimulateME(fmuStruct, t_start, t_stop; x0=rand_x0)
+# @test length(solution.states.u) > 0
+# @test length(solution.states.t) > 0
+
+# @test solution.states.t[1] == t_start
+# @test solution.states.t[end] == t_stop
+
+# @test solution.states.u[1] == rand_x0
+# fmiUnload(myFMU)
\ No newline at end of file
diff --git a/test/FMI3/sim_auto.jl b/test/FMI3/sim_auto.jl
new file mode 100644
index 00000000..b1c61c8f
--- /dev/null
+++ b/test/FMI3/sim_auto.jl
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+pathToFMU = get_model_filename("SpringPendulum1D", ENV["EXPORTINGTOOL"], ENV["EXPORTINGVERSION"])
+
+# load FMU in temporary directory
+myFMU = fmiLoad(pathToFMU)
+@test isfile(myFMU.zipPath) == true
+@test isdir(splitext(myFMU.zipPath)[1]) == true
+fmiUnload(myFMU)
+
+# load FMU in source directory
+fmuDir = joinpath(splitpath(pathToFMU)[1:end-1]...)
+myFMU = fmiLoad(pathToFMU; unpackPath=fmuDir)
+@test isfile(splitext(pathToFMU)[1] * ".zip") == true
+@test isdir(splitext(pathToFMU)[1]) == true
+
+comp = fmiInstantiate!(myFMU; loggingOn=false)
+@test comp != 0
+
+# choose FMU or FMUComponent
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = comp
+end
+@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+t_start = 0.0
+t_stop = 8.0
+
+# test without recording values (but why?)
+sol = fmiSimulate(fmuStruct, t_start, t_stop; dt=1e-2)
+@test sol.success
+
+# test with recording values
+solution = fmiSimulate(fmuStruct, t_start, t_stop; dt=1e-2, recordValues=["mass.s", "mass.v"], setup=true)
+@test solution.success
+@test length(solution.values.saveval) == t_start:1e-2:t_stop |> length
+@test length(solution.values.saveval[1]) == 2
+
+t = solution.values.t
+s = collect(d[1] for d in solution.values.saveval)
+v = collect(d[2] for d in solution.values.saveval)
+@test t[1] == t_start
+@test t[end] == t_stop
+
+# reference values from Simulation in Dymola2020x (Dassl)
+@test s[1] == 0.5
+@test v[1] == 0.0
+
+if ENV["EXPORTINGTOOL"] == "Dymola/2020x" # ToDo: Linux FMU was corrupted
+ @test s[end] ≈ 0.509219 atol=1e-1
+ @test v[end] ≈ 0.314074 atol=1e-1
+end
+
+fmiUnload(myFMU)
diff --git a/test/FMI3/state.jl b/test/FMI3/state.jl
new file mode 100644
index 00000000..5de45d6b
--- /dev/null
+++ b/test/FMI3/state.jl
@@ -0,0 +1,63 @@
+#
+# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher
+# Licensed under the MIT license. See LICENSE file in the project root for details.
+#
+
+###############
+# Prepare FMU #
+###############
+
+myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.16", "3.0")
+
+inst = fmi3InstantiateCoSimulation!(myFMU; loggingOn=true)
+@test inst != 0
+
+# choose FMU or FMUComponent
+fmuStruct = nothing
+envFMUSTRUCT = ENV["FMUSTRUCT"]
+if envFMUSTRUCT == "FMU"
+ fmuStruct = myFMU
+elseif envFMUSTRUCT == "FMUCOMPONENT"
+ fmuStruct = inst
+end
+@assert fmuStruct != nothing "Unknown fmuStruct, environment variable `FMUSTRUCT` = `$envFMUSTRUCT`"
+
+@test fmi3EnterInitializationMode(fmuStruct) == 0
+@test fmi3ExitInitializationMode(fmuStruct) == 0
+
+###########################
+# Testing state functions #
+###########################
+
+if fmiCanGetSetState(myFMU) && fmiCanSerializeFMUstate(myFMU)
+ @test fmi3GetFloat64(fmuStruct, "h") == 1.0
+ FMUstate = fmiGetFMUstate(fmuStruct)
+ @test typeof(FMUstate) == FMI.fmi3FMUState
+ len = fmiSerializedFMUstateSize(fmuStruct, FMUstate)
+ @test len > 0
+ serial = fmiSerializeFMUstate(fmuStruct, FMUstate)
+ @test length(serial) == len
+ @test typeof(serial) == Array{UInt8,1}
+
+ fmi3SetFloat64(fmuStruct, "h", 10.0)
+ FMUstate = fmiGetFMUstate(fmuStruct)
+ @test fmi3GetFloat64(fmuStruct, "h") == 10.0
+
+ FMUstate2 = fmiDeSerializeFMUstate(fmuStruct, serial)
+ @test typeof(FMUstate2) == FMI.fmi3FMUState
+ fmiSetFMUstate(fmuStruct, FMUstate2)
+ @test fmi3GetFloat64(fmuStruct, "h") == 1.0
+ fmiSetFMUstate(fmuStruct, FMUstate)
+ @test fmi3GetFloat64(fmuStruct, "h") == 10.0
+ fmiFreeFMUstate!(fmuStruct, FMUstate)
+ fmiFreeFMUstate!(fmuStruct, FMUstate2)
+else
+ @info "The FMU provided from the tool `$(ENV["EXPORTINGTOOL"])` does not support state get, set, serialization and deserialization. Skipping related tests."
+end
+
+############
+# Clean up #
+############
+
+@test fmiTerminate(myFMU) == 0
+fmiUnload(myFMU)
diff --git a/test/runtests.jl b/test/runtests.jl
index a17d93b4..e501ef81 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -20,7 +20,7 @@ for exec in [FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATI
exec.assertOnWarning = true
end
-function runtests(exportingTool)
+function runtestsFMI2(exportingTool)
ENV["EXPORTINGTOOL"] = exportingTool[1]
ENV["EXPORTINGVERSION"] = exportingTool[2]
@@ -28,12 +28,12 @@ function runtests(exportingTool)
@info "Sensitivities (sens.jl)"
@testset "Sensitivities" begin
- include("sens.jl")
+ include("FMI2/sens.jl")
end
@info "Model Description (model_description.jl)"
@testset "Model Description" begin
- include("model_description.jl")
+ include("FMI2/model_description.jl")
end
for str in fmuStructs
@@ -42,49 +42,95 @@ function runtests(exportingTool)
@info "Variable Getters / Setters (getter_setter.jl)"
@testset "Variable Getters / Setters" begin
- include("getter_setter.jl")
+ include("FMI2/getter_setter.jl")
end
@info "State Manipulation (state.jl)"
@testset "State Manipulation" begin
- include("state.jl")
+ include("FMI2/state.jl")
end
@info "Directional derivatives (dir_ders.jl)"
@testset "Directional derivatives" begin
- include("dir_ders.jl")
+ include("FMI2/dir_ders.jl")
end
@info "Automatic Simulation (sim_auto.jl)"
@testset "Automatic Simulation (CS or ME)" begin
- include("sim_auto.jl")
+ include("FMI2/sim_auto.jl")
end
@info "CS Simulation (sim_CS.jl)"
@testset "CS Simulation" begin
- include("sim_CS.jl")
+ include("FMI2/sim_CS.jl")
end
@info "ME Simulation (sim_ME.jl)"
@testset "ME Simulation" begin
- include("sim_ME.jl")
+ include("FMI2/sim_ME.jl")
end
@info "Support CS and ME simultaneously (cs_me.jl)"
@testset "Support CS and ME simultaneously" begin
- include("cs_me.jl")
+ include("FMI2/cs_me.jl")
end
@info "Loading/Saving simulation results (load_save.jl)"
@testset "Loading/Saving simulation results" begin
- include("load_save.jl")
+ include("FMI2/load_save.jl")
end
end
end
@info "Plotting (plots.jl)"
@testset "Plotting" begin
- include("plots.jl")
+ include("FMI2/plots.jl")
+ end
+ end
+end
+
+function runtestsFMI3(exportingTool)
+ ENV["EXPORTINGTOOL"] = exportingTool[1]
+ ENV["EXPORTINGVERSION"] = exportingTool[2]
+
+ @testset "Testing FMUs exported from $exportingTool" begin
+
+ @testset "FMI3/Sensitivities" begin
+ # include("FMI3/sens.jl")
+ end
+
+ for str in fmuStructs
+ @testset "Functions for $str" begin
+ ENV["FMUSTRUCT"] = str
+ @testset "Variable Getters / Setters" begin
+ include("FMI3/getter_setter.jl")
+ end
+ @testset "State Manipulation" begin
+ include("FMI3/state.jl")
+ end
+ @testset "Directional derivatives" begin
+ # include("FMI3/dir_ders.jl")
+ end
+ @testset "Automatic Simulation (CS or ME)" begin
+ # include("FMI3/sim_auto.jl")
+ end
+ @testset "CS Simulation" begin
+ include("FMI3/sim_CS.jl")
+ end
+ @testset "ME Simulation" begin
+ include("FMI3/sim_ME.jl")
+ end
+ @testset "Support CS and ME simultaneously" begin
+ # include("FMI3/cs_me.jl")
+ end
+ @testset "Loading/Saving simulation results" begin
+ # include("FMI3/load_save.jl")
+ end
+ end
+ end
+
+ @testset "Plotting" begin
+ # include("FMI3/plots.jl")
end
end
end
@@ -93,12 +139,14 @@ end
if Sys.iswindows()
@info "Automated testing is supported on Windows."
for exportingTool in exportingToolsWindows
- runtests(exportingTool)
+ runtestsFMI2(exportingTool)
+ runtestsFMI3(exportingTool)
end
elseif Sys.islinux()
@info "Automated testing is supported on Linux."
for exportingTool in exportingToolsLinux
- runtests(exportingTool)
+ runtestsFMI2(exportingTool)
+ runtestsFMI3(exportingTool)
end
elseif Sys.isapple()
@warn "Test-sets are currrently using Windows- and Linux-FMUs, automated testing for macOS is currently not supported."