diff --git a/.github/workflows/Example.yml b/.github/workflows/Example.yml index 324b0f7f..80482a7f 100644 --- a/.github/workflows/Example.yml +++ b/.github/workflows/Example.yml @@ -1,4 +1,4 @@ -name: Run Examples +name: Examples v1 (latest) on: workflow_dispatch: @@ -20,7 +20,7 @@ jobs: matrix: os: [windows-latest, ubuntu-latest] file-name: [manipulation, modelica_conference_2021, multiple_instances, multiprocessing, multithreading, parameterize, simulate] - julia-version: ['1.7'] + julia-version: ['1.8'] julia-arch: [x64] experimental: [false] @@ -44,7 +44,7 @@ jobs: env: FILE: examples/src/${{ matrix.file-name }}.ipynb run: | - jupyter nbconvert --ExecutePreprocessor.kernel_name="julia-1.7" --to notebook --inplace --execute ${{ env.FILE }} + jupyter nbconvert --ExecutePreprocessor.kernel_name="julia-1.8" --to notebook --inplace --execute ${{ env.FILE }} jupyter nbconvert --to script ${{ env.FILE }} jupyter nbconvert --to markdown ${{ env.FILE }} diff --git a/.github/workflows/TestLTS.yml b/.github/workflows/TestLTS.yml new file mode 100644 index 00000000..b5dbb134 --- /dev/null +++ b/.github/workflows/TestLTS.yml @@ -0,0 +1,67 @@ +name: Test v1.6 (LTS) + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + paths: + - 'src/**' + - 'test/**' + - '.github/workflows/Test.yml' + - 'Project.toml' + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + julia-version: ['1.6'] + julia-arch: [x64] + os: [ubuntu-latest, windows-latest] + experimental: [false] + + steps: + # Checks-out your repository + - name: Check out repository + uses: actions/checkout@v3 + + # Set up Julia + - name: "Set up Julia" + uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.julia-version }} + arch: ${{ matrix.julia-arch }} + + # Set up cache + - name: "Set up cache" + uses: actions/cache@v3 + env: + cache-name: cache-artifacts + with: + path: ~/.julia/artifacts + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ env.cache-name }}- + ${{ runner.os }}-test- + ${{ runner.os }}- + + # Build package + - name: "Build package" + uses: julia-actions/julia-buildpkg@v1 + + # Run the tests + - name: "Run tests" + uses: julia-actions/julia-runtest@v1 + + # Preprocess Coverage + - name: "Preprocess Coverage" + uses: julia-actions/julia-processcoverage@v1 + + # Run codecov + - name: "Run CodeCov" + uses: codecov/codecov-action@v3 + with: + file: lcov.info diff --git a/.github/workflows/Test.yml b/.github/workflows/TestLatest.yml similarity index 96% rename from .github/workflows/Test.yml rename to .github/workflows/TestLatest.yml index 0ecac4fe..723b787f 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/TestLatest.yml @@ -1,4 +1,4 @@ -name: Run Tests +name: Test v1 (latest) on: workflow_dispatch: @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false matrix: - julia-version: ['1.6', '1'] + julia-version: ['1'] julia-arch: [x64] os: [ubuntu-latest, windows-latest] experimental: [false] diff --git a/Project.toml b/Project.toml index 4c50c527..537a46e6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,28 +1,26 @@ name = "FMI" uuid = "14a09403-18e3-468f-ad8a-74f8dda2d9ac" authors = ["TT ", "LM ", "JK "] -version = "0.11.3" +version = "0.12.0" [deps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" +Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" FMIExport = "31b88311-cab6-44ed-ba9c-fe5a9abbd67a" FMIImport = "9fcbc62e-52a0-44e9-a616-1359a0008194" -ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Requires = "ae029012-a4dd-5104-9daa-d747884805df" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" [compat] ChainRulesCore = "1.15.0" -DiffEqCallbacks = "2.24.0" -DifferentialEquations = "7.5.0" +DiffEqCallbacks = "2.26.0" +DifferentialEquations = "7.7.0" FMIExport = "0.1.0" -FMIImport = "0.14.0" -ForwardDiff = "0.10.0" +FMIImport = "0.15" ProgressMeter = "1.7.0" Requires = "1.3.0" -SciMLBase = "1.59.0" +SciMLBase = "1.89.0" julia = "1.6" diff --git a/README.md b/README.md index 3c41332d..c2e2626e 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ [*FMI.jl*](https://github.com/ThummeTo/FMI.jl) is a free-to-use software library for the Julia programming language which integrates the **F**unctional **M**ock-Up **I**nterface ([fmi-standard.org](http://fmi-standard.org/)): load or create, parameterize, simulate and plot FMUs seamlessly inside the Julia programming language! [![Dev Docs](https://img.shields.io/badge/docs-dev-blue.svg)](https://ThummeTo.github.io/FMI.jl/dev) -[![Run Tests](https://github.com/ThummeTo/FMI.jl/actions/workflows/Test.yml/badge.svg)](https://github.com/ThummeTo/FMI.jl/actions/workflows/Test.yml) -[![Run Examples](https://github.com/ThummeTo/FMI.jl/actions/workflows/Example.yml/badge.svg)](https://github.com/ThummeTo/FMI.jl/actions/workflows/Example.yml) +[![Test (latest)](https://github.com/ThummeTo/FMI.jl/actions/workflows/TestLatest.yml/badge.svg)](https://github.com/ThummeTo/FMI.jl/actions/workflows/TestLatest.yml) +[![Test (LTS)](https://github.com/ThummeTo/FMI.jl/actions/workflows/TestLTS.yml/badge.svg)](https://github.com/ThummeTo/FMI.jl/actions/workflows/TestLTS.yml) +[![Examples (latest)](https://github.com/ThummeTo/FMI.jl/actions/workflows/Example.yml/badge.svg)](https://github.com/ThummeTo/FMI.jl/actions/workflows/Example.yml) [![Build Docs](https://github.com/ThummeTo/FMI.jl/actions/workflows/Documentation.yml/badge.svg)](https://github.com/ThummeTo/FMI.jl/actions/workflows/Documentation.yml) [![Coverage](https://codecov.io/gh/ThummeTo/FMI.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/ThummeTo/FMI.jl) [![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor's%20Guide-blueviolet)](https://github.com/SciML/ColPrac) @@ -107,5 +108,12 @@ Tobias Thummerer, Johannes Stoljar and Lars Mikelsons. 2022. **NeuralFMU: presen Tobias Thummerer, Johannes Tintenherr, Lars Mikelsons. 2021 **Hybrid modeling of the human cardiovascular system using NeuralFMUs** Journal of Physics: Conference Series 2090, 1, 012155. [DOI: 10.1088/1742-6596/2090/1/012155](https://doi.org/10.1088/1742-6596/2090/1/012155) +## Notes for contributors +Contributors are welcome. Before contributing, please read, understand and follow the [Contributor's Guide on Collaborative Practices for Community Packages](https://github.com/SciML/ColPrac). +During development of new implementations or optimizations on exisitng code, one will have to make design decissions that influence the library performance and usability. The following priorization should be the basis for decision-making: +- **#1 Compliance with standard:** It is the highest priority to be compliant with the FMI standard ([fmi-standard.org](http://fmi-standard.org/)). Identifiers described in the standard must be used. Topologies should follow the specification as far as the possibilities of the Julia programming language allows. +- **#2 Performance:** Because [*FMI.jl*](https://github.com/ThummeTo/FMI.jl) is a simulation tool, performance is very important. This applies to the efficient use of CPU and GPU, but also the conscientious use of RAM and disc space. +- **#3 Usability:** The library should be as usable as possible, as long as being fully compliant with the FMI standard. + ## Interested in Hybrid Modelling in Julia using FMUs? See [*FMIFlux.jl*](https://github.com/ThummeTo/FMIFlux.jl). diff --git a/docs/src/examples/overview.md b/docs/src/examples/overview.md index 9d9dc52b..b79ae55e 100644 --- a/docs/src/examples/overview.md +++ b/docs/src/examples/overview.md @@ -6,6 +6,7 @@ The examples are: - [__Simulate__](https://thummeto.github.io/FMI.jl/dev/examples/simulate/): Showing how you can simulate a CS-FMU and a ME-FMU. - [__Parameterize__](https://thummeto.github.io/FMI.jl/dev/examples/parameterize/): A short example explaining how to parameterize a FMU before simulation. +- [__Inputs__](https://thummeto.github.io/FMI.jl/dev/examples/inputs/): A short example explaining how to simulate a FMU with inputs. - [__Multiple instances__](https://thummeto.github.io/FMI.jl/dev/examples/multiple_instances/): Showing the use of multiple instances. - [__Modelica conference 2021__](https://thummeto.github.io/FMI.jl/dev/examples/modelica_conference_2021/): Showing the different variants of simulating an FMU. - [__Manipulation__](https://thummeto.github.io/FMI.jl/dev/examples/manipulation/): Showing how to redefine a library function. diff --git a/docs/src/features.md b/docs/src/features.md index ce2a78e5..eaed6a50 100644 --- a/docs/src/features.md +++ b/docs/src/features.md @@ -46,8 +46,8 @@ If you want to force a specific simulation mode, you can use `fmiSimulateME` (fo ## Simulate arbitrary time intervals You can simply simulate arbitrary time intervals by passing a `startTime` unequal zero to `fmi2SetupExperiment`. -Because many FMUs don't support `startTime != 0.0` and will throw an error or warning, a time shifting feature inside *FMI.jl* performs all necessary steps in the background - all corresponding commands like e.g. `fmi2SetTime` or `fmi2NewDiscreteStates` act like the desired time interval is simulated. -If you don't want this feature (maybe because you are simulating time-dependent systems), you may use the execution configuration `myFMU.executionConfig.autoTimeShift=false` while providing a `startTime != 0.0`. +Because many FMUs don't support `startTime != 0.0` and will throw an error or warning, a time shifting feature inside *FMI.jl* can be used, that performs all necessary steps in the background - corresponding commands like e.g. `fmi2SetTime` or `fmi2NewDiscreteStates` act like the desired time interval is simulated. +This feature is disabled by default, but can be activated in the execution configuration using `myFMU.executionConfig.autoTimeShift=true` while providing a `startTime != 0.0`. ## Performance **In- and Out-of-Place:** Many commands in *FMI.jl* are available in in-place and out-of-place semantics. Of course, in-place-calls are faster, because they don't need to allocate new memory at every call (for the return values). diff --git a/examples/src/inputs.ipynb b/examples/src/inputs.ipynb new file mode 100644 index 00000000..a8fc6584 --- /dev/null +++ b/examples/src/inputs.ipynb @@ -0,0 +1,226 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Simulate an FMU with inputs\n", + "Tutorial (WIP) by Tobias Thummerer\n", + "\n", + "## License" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "execution": { + "iopub.execute_input": "2022-10-22T13:28:04.723000Z", + "iopub.status.busy": "2022-10-22T13:28:04.017000Z", + "iopub.status.idle": "2022-10-22T13:28:04.999000Z", + "shell.execute_reply": "2022-10-22T13:28:04.926000Z" + } + }, + "outputs": [], + "source": [ + "# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n", + "# Licensed under the MIT license. \n", + "# See LICENSE (https://github.com/thummeto/FMI.jl/blob/main/LICENSE) file in the project root for details." + ] + }, + { + "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": null, + "metadata": { + "execution": { + "iopub.execute_input": "2022-10-22T13:28:05.004000Z", + "iopub.status.busy": "2022-10-22T13:28:05.004000Z", + "iopub.status.idle": "2022-10-22T13:28:49.082000Z", + "shell.execute_reply": "2022-10-22T13:28:49.082000Z" + }, + "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": null, + "metadata": { + "execution": { + "iopub.execute_input": "2022-10-22T13:28:51.448000Z", + "iopub.status.busy": "2022-10-22T13:28:49.085000Z", + "iopub.status.idle": "2022-10-22T13:28:52.234000Z", + "shell.execute_reply": "2022-10-22T13:28:52.233000Z" + }, + "scrolled": false + }, + "outputs": [], + "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": null, + "metadata": { + "execution": { + "iopub.execute_input": "2022-10-22T13:28:52.238000Z", + "iopub.status.busy": "2022-10-22T13:28:52.237000Z", + "iopub.status.idle": "2022-10-22T13:28:57.034000Z", + "shell.execute_reply": "2022-10-22T13:28:57.034000Z" + }, + "scrolled": false + }, + "outputs": [], + "source": [ + "# we use an FMU from the FMIZoo.jl\n", + "pathToFMU = get_model_filename(\"SpringPendulumExtForce1D\", \"Dymola\", \"2022x\")\n", + "\n", + "myFMU = fmiLoad(pathToFMU; type=:ME) # load FMU in ME-Mode (\"Model Exchange\")\n", + "\n", + "fmiInfo(myFMU)" + ] + }, + { + "attachments": {}, + "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. Note, that the dynamics of the input variables are not considered by the steps ize control of the solver, so it is highly recommended to limit the solver step size with the keyword argument `dtmax` if the input is more dynamic than the system." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "execution": { + "iopub.execute_input": "2022-10-22T13:29:04.979000Z", + "iopub.status.busy": "2022-10-22T13:29:04.978000Z", + "iopub.status.idle": "2022-10-22T13:29:21.052000Z", + "shell.execute_reply": "2022-10-22T13:29:21.052000Z" + } + }, + "outputs": [], + "source": [ + "# input function format \"t\", dependent on `t` (time)\n", + "function extForce_t(t)\n", + " [sin(t)]\n", + "end \n", + "\n", + "# simulate while setting inputs\n", + "data_extForce_t = fmiSimulateME(myFMU, (tStart, tStop); saveat=tSave, inputValueReferences=[\"extForce\"], inputFunction=extForce_t, dtmax=1e-2)\n", + "fmiPlot(data_extForce_t)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# input function format \"cxt\", dependent on `c` (component), `x` (state) and `t` (time)\n", + "function extForce_cxt(c::Union{FMU2Component, Nothing}, x::Union{AbstractArray{fmi2Real}, Nothing}, t::fmi2Real)\n", + " x1 = 0.0\n", + " if x != nothing # this check is important, because inputs may be needed before the system state is known\n", + " x1 = x[1] \n", + " end\n", + " [sin(t) * x1]\n", + "end \n", + "\n", + "# simulate while setting inputs\n", + "data_extForce_cxt = fmiSimulateME(myFMU, (tStart, tStop); saveat=tSave, inputValueReferences=[\"extForce\"], inputFunction=extForce_cxt, dtmax=1e-2)\n", + "fmiPlot(data_extForce_cxt)" + ] + }, + { + "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": null, + "metadata": { + "execution": { + "iopub.execute_input": "2022-10-22T13:30:01.970000Z", + "iopub.status.busy": "2022-10-22T13:30:01.970000Z", + "iopub.status.idle": "2022-10-22T13:30:02.010000Z", + "shell.execute_reply": "2022-10-22T13:30:02.010000Z" + } + }, + "outputs": [], + "source": [ + "fmiUnload(myFMU)" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "037537ff7419c497b9325f7d495147943224d408cf5d5ed915294a5b960167b0" + }, + "jupytext": { + "cell_metadata_filter": "-all", + "comment_magics": "false", + "notebook_metadata_filter": "-all" + }, + "kernelspec": { + "display_name": "Julia 1.8.5", + "language": "julia", + "name": "julia-1.8" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.8.5" + }, + "nteract": { + "version": "0.28.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/FMI.jl b/src/FMI.jl index 38021adb..27259385 100644 --- a/src/FMI.jl +++ b/src/FMI.jl @@ -10,6 +10,9 @@ module FMI using Requires using FMIImport + +# TODO recheck export import list +# 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 +39,56 @@ import FMIImport: fmi2Get, fmi2Get!, fmi2Set import FMIImport: fmi2GetSolutionTime, fmi2GetSolutionState, fmi2GetSolutionValue export fmi2GetSolutionTime, fmi2GetSolutionState, fmi2GetSolutionValue -export fmiCanGetSetState +# 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 +import FMIImport: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 +export fmiCanGetSetState + +import FMIImport: fmi3TypeModelExchange, fmi3TypeCoSimulation, fmi3TypeScheduledExecution, fmi3Type +export fmi2TypeModelExchange, fmi2TypeCoSimulation, fmi3TypeScheduledExecution, fmi2Type + using FMIExport using FMIExport: fmi2Create, fmi2CreateSimple @@ -60,6 +108,8 @@ using FMIImport: prepareValue, prepareValueReference export fmi2Real, fmi2Integer, fmi2String, fmi2Enumeration, fmi2Boolean +include("check.jl") + include("FMI2/additional.jl") include("FMI3/additional.jl") include("assertions.jl") @@ -90,6 +140,23 @@ 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 + # Requires init function __init__() @require Plots="91a5bcdd-55d7-5caf-9e0b-520d859cae80" begin @@ -100,7 +167,8 @@ function __init__() @require JLD2="033835bb-8acc-5ee8-8aae-3f567f8a3819" begin import .JLD2 include("FMI2/extensions/JLD2.jl") - end + include("FMI3/extensions/JLD2.jl") + end end ### EXPORTING LISTS START ### @@ -128,7 +196,7 @@ function (str::Union{fmi2Struct, fmi3Struct})(; t::Tuple{Float64, Float64}, kwar end """ -#ToDo +#ToDo fmi3Docs for all functions fmiGetState(str::fmi2Struct) @@ -154,6 +222,10 @@ function fmiGetState(str::fmi2Struct) fmi2GetFMUstate(str) end +function fmiGetState(str::fmi3Struct) + fmi3GetFMUState(str) +end + """ fmiFreeState!(str::fmi2Struct, c::FMU2Component, state::fmi2FMUstate) @@ -192,6 +264,9 @@ See also [`fmi2FreeFMUstate!`](@ref),[`fmi2FMUstate`](@ref), [`fmi2Struct`](@ref function fmiFreeState!(str::fmi2Struct, args...; kwargs...) fmi2FreeFMUstate!(str, args...; kwargs...) end +function fmiFreeState!(str::fmi3Struct, args...; kwargs...) + fmi3FreeFMUState!(str, args...; kwargs...) +end """ @@ -228,10 +303,13 @@ See also [`fmi2GetFMUstate`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2 function fmiSetState(str::fmi2Struct, args...; kwargs...) fmi2SetFMUstate(str, args...; kwargs...) end +function fmiSetState(str::fmi3Struct, args...; kwargs...) + fmi3SetFMUState(str, args...; kwargs...) +end """ - fmi2GetDependencies(fmu::FMU2) + fmiGetDependencies(fmu::FMU2) Building dependency matrix `dim x dim` for fast look-ups on variable dependencies (`dim` is number of states). @@ -251,6 +329,9 @@ See also [`fmi2GetDependencies`](@ref). function fmiGetDependencies(fmu::FMU2) fmi2GetDependencies(fmu) end +function fmiGetDependencies(fmu::FMU3) + fmi3GetDependencies(fmu) +end """ @@ -285,12 +366,12 @@ 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. # Source @@ -298,7 +379,7 @@ Returns the tag 'modelName' from the model description. - 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 -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) @@ -307,7 +388,8 @@ function fmiGetModelName(str::fmi3StructMD) fmi3GetModelName(str) end -""" +# TODO call differently in fmi3: getInstantationToken +""" fmiGetGUID(str::fmi2StructMD) @@ -330,18 +412,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`). @@ -356,19 +443,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`). @@ -383,19 +476,26 @@ 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`). @@ -405,24 +505,31 @@ Returns the tag 'varaiblenamingconvention' from the model description. - 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 -See also [`fmi2GetVariableNamingConvention`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref). +See also [`fmi2GetVariableNamingConvention`](@ref), [`fmi2StructMD`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ModelDescription`](@ref), [`fmi3GetVariableNamingConvention`](@ref), [`fmi3StructMD`](@ref), [`FMU3`](@ref), [`FMU3Instance`](@ref), [`fmi3ModelDescription`](@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`). @@ -432,75 +539,92 @@ More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription} - 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 -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 # 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 -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. - # Source +# 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 -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. @@ -510,82 +634,151 @@ More detailed: `fmi2StructMD = Union{FMU2, FMU2Component, fmi2ModelDescription} - 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 -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. # 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 -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 @@ -593,14 +786,14 @@ end 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. @@ -610,35 +803,46 @@ Load FMUs independent of the FMI version, currently supporting version 2.0.X. - 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 -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/). # 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 - -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, @@ -657,18 +861,21 @@ 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 - `dt::Union{Real, Nothing} = nothing`: Real number to set the step size of the OED-solver. Defaults to an automatic choice if the method is adaptive. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control) - `solver = nothing`: Any Julia-supported OED-solver (default is Tsit5). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/solvers/ode_solve/#ode_solve) -- `customFx = nothing`: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t) +- `customFx = nothing`: [deprecated] Ability to give a custom state derivative function ẋ=f(x,t) - `recordValues::fmi2ValueReferenceFormat = nothing`: AbstractArray of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues` - `saveat = []`: Time points to save values at (interpolated). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Output-Control) - `setup::Bool = true`: Boolean, if FMU should be setup (default: setup=true) @@ -693,13 +900,15 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` See also [`fmi2Simulate`](@ref), [`fmi2SimulateME`](@ref), [`fmi2SimulateCS`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref). """ -function fmiSimulate(str::fmi2Struct, tspan::Union{Tuple{Float64, Float64}, Nothing}=nothing, args...; kwargs...) - fmi2Simulate(str, tspan, args...; kwargs...) +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, @@ -718,10 +927,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. @@ -729,7 +941,7 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` - `tolerance::Union{Real, Nothing} = nothing`: Real number to set the tolerance for any OED-solver - `dt::Union{Real, Nothing} = nothing`: Real number to set the step size of the OED-solver. Defaults to an automatic choice if the method is adaptive. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control) - `solver = nothing`: Any Julia-supported OED-solver (default is Tsit5). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/solvers/ode_solve/#ode_solve) -- `customFx = nothing`: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t) +- `customFx = nothing`: [deprecated] Ability to give a custom state derivative function ẋ=f(x,t) - `recordValues::fmi2ValueReferenceFormat = nothing`: AbstractArray of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues` - `saveat = []`: Time points to save values at (interpolated). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Output-Control) - `setup::Bool = true`: Boolean, if FMU should be setup (default: setup=true) @@ -749,16 +961,19 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` - 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 -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, tspan::Union{Tuple{Float64, Float64}, Nothing}=nothing, args...; kwargs...) fmi2SimulateCS(str, tspan, 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, @@ -777,10 +992,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. @@ -788,7 +1006,7 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` - `tolerance::Union{Real, Nothing} = nothing`: Real number to set the tolerance for any OED-solver - `dt::Union{Real, Nothing} = nothing`: Real number to set the step size of the OED-solver. Defaults to an automatic choice if the method is adaptive. More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Stepsize-Control) - `solver = nothing`: Any Julia-supported OED-solver (default is Tsit5). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/solvers/ode_solve/#ode_solve) -- `customFx = nothing`: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t) +- `customFx = nothing`: [deprecated] Ability to give a custom state derivative function ẋ=f(x,t) - `recordValues::fmi2ValueReferenceFormat = nothing`: AbstractArray of variables (strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues` - `saveat = []`: Time points to save values at (interpolated). More Info: [DifferentialEquations.jl Documentation](https://diffeq.sciml.ai/stable/basics/common_solver_opts/#Output-Control) - `setup::Bool = true`: Boolean, if FMU should be setup (default: setup=true) @@ -811,41 +1029,50 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` See also [`fmi2SimulateME`](@ref) [`fmi2SimulateCS`](@ref), [`fmi2Simulate`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref). """ + function fmiSimulateME(str::fmi2Struct, tspan::Union{Tuple{Float64, Float64}, Nothing}=nothing, args...; kwargs...) fmi2SimulateME(str, tspan, 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/). # 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 - -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. @@ -855,12 +1082,16 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` - 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 -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 +# TODO not in FMI3 """ fmiGetTypesPlatform(str::fmi2Struct) @@ -889,29 +1120,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) @@ -919,22 +1157,30 @@ 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/). # Returns - Prints FMU related information. + # 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 """ @@ -962,7 +1208,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) @@ -992,7 +1240,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) @@ -1025,6 +1275,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 """ @@ -1048,12 +1301,16 @@ More detailed: `fmi2ValueReferenceFormat = Union{Nothing, String, Array{String,1 - FMISpec2.0.2[p.24]: 2.1.7 Getting and Setting Variable Values - FMISpec2.0.2[p.18]: 2.1.3 Status Returned by Functions - See also [`fmi2GetReal`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref). +See also [`fmi2GetReal`](@ref),[`fmi2ValueReferenceFormat`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref). """ function fmiGetReal(str::fmi2Struct, args...; kwargs...) fmi2GetReal(str, args...; kwargs...) end +function fmiGetReal(str::fmi3Struct, args...; kwargs...) + fmi3GetReal(str, args...; kwargs...) +end + """ #TODO @@ -1061,16 +1318,11 @@ end vUnknown_ref::Array{fmi2ValueReference}, vKnown_ref::Array{fmi2ValueReference}, steps::Array{fmi2Real} = ones(fmi2Real, length(vKnown_ref)).*1e-5) - fmiSampleJacobian(str::fmi2Struct, c::FMU2Component, vUnknown_ref::AbstractArray{fmi2ValueReference}, vKnown_ref::AbstractArray{fmi2ValueReference}, steps::Union{AbstractArray{fmi2Real}, Nothing} = nothing) - - - This function samples the jacobian by manipulating corresponding values (central differences). - # Arguments - `str::fmi2Struct`: Representative for an FMU in the FMI 2.0.2 Standard. More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` @@ -1080,24 +1332,27 @@ More detailed: `fmi2Struct = Union{FMU2, FMU2Component}` - `vKnown_ref::Array{fmi2ValueReference}`: Argument `vKnown_ref` contains values of type `fmi2ValueReference` which are identifiers of a variable value of the model.`vKnown_ref` is the Array of the vector values of Real input variables of function h that changes its value in the actual Mode. - `steps::Array{fmi2Real} = ones(fmi2Real, length(vKnown_ref)).*1e-5`: - `steps::Union{AbstractArray{fmi2Real}, Nothing} = nothing`: - # Returns - `dvUnknown::Arrya{fmi2Real}`: - # Source - FMISpec2.0.2 Link: [https://fmi-standard.org/](https://fmi-standard.org/) - FMISpec2.0.2[p.16]: 2.1.2 Platform Dependent Definitions (fmi2TypesPlatform.h) - See also [`fmi2SampleJacobian`](@ref), [`fmi2Struct`](@ref), [`FMU2`](@ref), [`FMU2Component`](@ref), [`fmi2ValueReference`](@ref). """ function fmiSampleJacobian(str::fmi2Struct, args...; kwargs...) fmi2SampleJacobian(str, args...; kwargs...) end +function fmiSampleJacobian(str::fmi3Struct, args...; kwargs...) + fmi3SampleJacobian(str, args...; kwargs...) +end """ #TODO Samples the values of the jacobian (in-place). """ +function fmiSampleJacobian!(str::fmi3Struct, args...; kwargs...) + fmi3SampleJacobian!(str, args...; kwargs...) +end function fmiSampleJacobian!(str::fmi2Struct, args...; kwargs...) fmi2SampleJacobian!(str, args...; kwargs...) end @@ -1121,5 +1376,8 @@ 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 end # module FMI diff --git a/src/FMI2/extensions/Plots.jl b/src/FMI2/extensions/Plots.jl index 8b0c61cd..b477c62a 100644 --- a/src/FMI2/extensions/Plots.jl +++ b/src/FMI2/extensions/Plots.jl @@ -4,7 +4,7 @@ # using FMIImport: FMU2Solution -import ForwardDiff +import FMIImport.ForwardDiff """ diff --git a/src/FMI2/sim.jl b/src/FMI2/sim.jl index 652ddabe..f45df61c 100644 --- a/src/FMI2/sim.jl +++ b/src/FMI2/sim.jl @@ -14,8 +14,8 @@ using FMIImport: FMU2Solution, FMU2Event import FMIImport: prepareSolveFMU, finishSolveFMU, handleEvents import FMIImport: undual -using ChainRulesCore -import ForwardDiff +using FMIImport.ChainRulesCore +import FMIImport.ForwardDiff import ProgressMeter @@ -192,7 +192,7 @@ Via the optional keyword arguemnts `inputValues` and `inputFunction`, a custom i Keywords: - solver: Any Julia-supported ODE-solver (default is Tsit5) - - customFx: [deperecated] Ability to give a custom state derivative function ẋ=f(x,t) + - customFx: [deprecated] 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) @@ -401,7 +401,11 @@ function fmi2SimulateME(fmu::FMU2, c::Union{FMU2Component, Nothing}=nothing, tsp fmusol.states = solve(c.problem, solver; callback = CallbackSet(cbs...), dtmax=dtmax, solveKwargs..., kwargs...) end - fmusol.success = (fmusol.states.retcode == :Success) + fmusol.success = (fmusol.states.retcode == SciMLBase.ReturnCode.Success) + + if !fmusol.success + @warn "FMU simulation failed with solver return code `$(fmusol.states.retcode)`, please check log for hints." + end # cleanup progress meter if showProgress @@ -554,7 +558,7 @@ function fmi2SimulateCS(fmu::FMU2, c::Union{FMU2Component, Nothing}=nothing, tsp ProgressMeter.finish!(progressMeter) end - fmusol.success = true + fmusol.success = true # ToDo: Check successful simulation! else i = 1 @@ -584,7 +588,7 @@ function fmi2SimulateCS(fmu::FMU2, c::Union{FMU2Component, Nothing}=nothing, tsp ProgressMeter.finish!(progressMeter) end - fmusol.success = true + fmusol.success = true # ToDo: Check successful simulation! end finishSolveFMU(fmu, c, freeInstance, terminate) @@ -610,7 +614,7 @@ Keywords: - 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) + - customFx: [ME only, deprecated] Ability to give a custom state derivative function ẋ=f(x,t) Returns: - `success::Bool` for CS-FMUs diff --git a/src/FMI3/additional.jl b/src/FMI3/additional.jl index 9d6747d5..625d2407 100644 --- a/src/FMI3/additional.jl +++ b/src/FMI3/additional.jl @@ -3,1317 +3,158 @@ # 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 - -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 FMIImport: fmi3True, fmi3False +using FMIImport: fmi3DependencyKindDependent, fmi3DependencyKindFixed +using FMIImport: fmi3CallbackLogger, fmi3CallbackIntermediateUpdate, fmi3CallbackClockUpdate, fmi3Instance +import FMIImport: fmi3VariableNamingConventionFlat, fmi3VariableNamingConventionStructured +""" +Returns how a variable depends on another variable based on the model description. """ -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 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})) + 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 - if fmu.modelDescription.CShasEventMode - mode = eventModeUsed - else - mode = false - end - - 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) + println("\tEvent indicators:\t\t$(fmi3GetNumberOfEventIndicators(fmu))") - 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 - -""" -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, values::Union{Array{fmi3Int32}, fmi3Int32}) - @assert length(fmu.components) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] - - fmi3GetInt32!(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 fmi3Int32 variables. - -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?"] - - fmi3SetInt32(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 fmi3UInt32 variables. - -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?"] - - fmi3GetUInt32(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 fmi3UInt32 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 ?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?"] + 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 - fmi3GetUInt32!(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 - -Set the values of an array of fmi3UInt32 variables. - -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?"] - - fmi3SetUInt32(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 fmi3Int64 variables. - -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?"] - - fmi3GetInt64(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 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..9ac7c94f 100644 --- a/src/FMI3/comp_wraps.jl +++ b/src/FMI3/comp_wraps.jl @@ -1,4 +1,896 @@ # # 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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3Get(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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3Get!(fmu.instances[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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3Set(fmu.instances[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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3SampleDirectionalDerivative!(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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3SampleDirectionalDerivative(fmu.instances[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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3GetJacobian!(fmu.instances[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.instances) > 0 ["No FMU instance allocated, have you already called fmiInstantiate?"] + fmi3GetJacobian(fmu.instances[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/extensions/JLD2.jl b/src/FMI3/extensions/JLD2.jl new file mode 100644 index 00000000..c93ba612 --- /dev/null +++ b/src/FMI3/extensions/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/extensions/Plots.jl b/src/FMI3/extensions/Plots.jl index b56dd818..4ebb927c 100644 --- a/src/FMI3/extensions/Plots.jl +++ b/src/FMI3/extensions/Plots.jl @@ -3,7 +3,8 @@ # Licensed under the MIT license. See LICENSE file in the project root for details. # -using SciMLBase: ODESolution +using FMIImport: FMU3Solution +import FMIImport.ForwardDiff """ Plots data from a ME-FMU. @@ -11,70 +12,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..cb510c0d 100644 --- a/src/FMI3/sim.jl +++ b/src/FMI3/sim.jl @@ -4,37 +4,62 @@ # using DifferentialEquations, DiffEqCallbacks +import SciMLBase: RightRootFind -using FMIImport: fmi3Instance +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.ChainRulesCore +import FMIImport.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 - fmi3EnterEventMode(c, c.stepEvent, c.stateEvent, c.rootsFound, Csize_t(c.fmu.modelDescription.numberOfEventIndicators), c.timeEvent) - # TODO inputEvent handling + # TODO set inputs + discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime = fmi3UpdateDiscreteStates(c) + if c.state != fmi3InstanceStateEventMode + fmi3EnterEventMode(c, c.stepEvent, c.stateEvent, c.rootsFound, Csize_t(c.fmu.modelDescription.numberOfEventIndicators), c.timeEvent) + end + # TODO inputEvent handling discreteStatesNeedUpdate = fmi3True while discreteStatesNeedUpdate == fmi3True - # update discrete states discreteStatesNeedUpdate, terminateSimulation, nominalsOfContinuousStatesChanged, valuesOfContinuousStatesChanged, nextEventTimeDefined, nextEventTime = fmi3UpdateDiscreteStates(c) @@ -51,280 +76,1040 @@ function handleEvents(c::fmi3Instance, enterEventMode::Bool, exitInContinuousMod end 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 == fmi3InstanceStateContinuousTimeMode "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 == fmi3InstanceStateContinuousTimeMode "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 (fmi3GetFloat64(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...) + + ys = [] - (fmi3GetFloat64(c, recordValues)...,) + #[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 -Simulates a FMU instance for the given simulation time interval. -State- and Time-Events are handled correctly. + 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 -Returns a tuple of type (ODESolution, DiffEqCallbacks.SavedValues). -If keyword `recordValues` is not set, a tuple of type (ODESolution, nothing) is returned for consitency. -""" -function fmi3SimulateME(c::fmi3Instance, t_start::Union{Real, Nothing} = nothing, t_stop::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, - inputFunction = nothing, - rootSearchInterpolationPoints = 100, - kwargs...) + if Δt != NoTangent() + dt = 1e-6 + dx1 = fmi3GetContinuousStateDerivatives(comp) + fmi3SetTime(comp, t + dt) + dx2 = fmi3GetContinuousStateDerivatives(comp) + ∂t = (dx2-dx1)/dt + t̄ = ∂t * Δt + end - if t_start == nothing - t_start = c.fmu.modelDescription.defaultStartTime + 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 - if t_stop == nothing - t_stop = c.fmu.modelDescription.defaultStopTime + 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 - recordValues = prepareValueReference(c, recordValues) - inputValues = prepareValueReference(c, inputValues) - solution = nothing - callbacks = [] - savedValues = nothing +# 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.fmi3Variable) + return mv.variability != fmi3VariabilityConstant && mv.initial ∈ (fmi3InitialApprox, fmi3InitialExact) +end - savingValues = (length(recordValues) > 0) - hasInputs = (length(inputValues) > 0) +import FMIImport: fmi3CausalityInput, fmi3CausalityParameter, fmi3VariabilityTunable +function setInInitialization(mv::FMIImport.fmi3Variable) + return mv.causality == fmi3CausalityInput || (mv.causality != fmi3CausalityParameter && mv.variability == fmi3VariabilityTunable) || (mv.variability != fmi3VariabilityConstant && mv.initial == fmi3InitialExact) +end - if savingValues - savedValues = SavedValues(Float64, Tuple{collect(Float64 for i in 1:length(recordValues))...}) +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) - savingCB = SavingCallback((u,t,integrator) -> saveValues(c, recordValues, u, t, integrator), - savedValues, - saveat=saveat) - push!(callbacks, savingCB) + if instantiate === nothing + instantiate = fmu.executionConfig.instantiate + end + + if terminate === nothing + terminate = fmu.executionConfig.terminate end - # auto correct reset if only setup is given if reset === nothing - reset = setup + 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 + if length(fmu.instances) > 0 + c = fmu.instances[end] + else + @warn "Found no FMU instance, but executionConfig doesn't force allocation. Allocating one. Use `fmi2Instantiate(fmu)` to prevent this message." + if type == fmi3TypeCoSimulation + c = fmi3InstantiateCoSimulation!(fmu) + elseif type == fmi3TypeModelExchange + c = fmi3InstantiateModelExchange!(fmu) + else + c = fmi3InstantiateScheduledExecution!(fmu) + end + end + end end - @assert !(setup==false && reset==true) "fmi3SimulateME(...): keyword argument `setup=false`, but `reset=true`. This may cause a FMU crash." + @assert c !== nothing "No FMU instance available, allocate one or use `fmu.executionConfig.instantiate=true`." + # 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 - fmi3Reset(c) + 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 - fmi3EnterInitializationMode(c, t_start, t_stop) - fmi3ExitInitializationMode(c) + 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 - eventHandling = c.fmu.modelDescription.numberOfEventIndicators > 0 - timeEventHandling = false + 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].instances[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. + +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 instance, `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(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 = 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, + parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing, + dtmax::Union{Real, Nothing} = nothing, + callbacks = [], + showProgress::Bool = true, + kwargs...) + @warn "ME-simulation is not working properly right now!" - 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 + @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)`." # TODO why not using this?? + + # 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(fmu, recordValues) + inputValueReferences = prepareValueReference(fmu, inputValueReferences) - if customFx === nothing - customFx = (x, p, t) -> fx(c, x, p, t) + fmusol = FMU3Solution(fmu) + + savingValues = (length(recordValues) > 0) + hasInputs = (length(inputValueReferences) > 0) + hasParameters = (parameters !== nothing) + hasStartState = (x0 !== nothing) + + cbs = [] + + for cb in callbacks + push!(cbs, cb) end - fmi3EnterContinuousTimeMode(c) + 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 + + if t_stop === nothing + t_stop = fmi3GetDefaultStopTime(fmu.modelDescription) - if eventHandling - c.z_prev = fmi3GetEventIndicators(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 - # First evaluation of the FMU - x0 = fmi3GetContinuousStates(c) - x0_nom = fmi3GetNominalsOfContinuousStates(c) + if tolerance === nothing + tolerance = fmi3GetDefaultTolerance(fmu.modelDescription) + # if no tolerance is given, pick auto-setting from DifferentialEquations.jl + end - p = [] - problem = ODEProblem(customFx, x0, (t_start, t_stop), p,) + if dt === nothing + dt = fmi3GetDefaultStepSize(fmu.modelDescription) + # if no dt is given, pick auto-setting from DifferentialEquations.jl + end + + if dtmax === nothing + dtmax = (t_stop-t_start)/100.0 + end + + # 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) + + # 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." + + # if x0 === nothing + # x0 = fmi3GetContinuousStates(c) + # x0_nom = fmi3GetNominalsOfContinuousStates(c) + # end + + # 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 == SciMLBase.ReturnCode.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 instance `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 - success = false - savedValues = nothing + fmusol = FMU3Solution(fmu) + + + 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 +1118,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 +1136,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 +1154,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 +1171,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 +1197,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 +1213,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/src/check.jl b/src/check.jl new file mode 100644 index 00000000..22a303cd --- /dev/null +++ b/src/check.jl @@ -0,0 +1,85 @@ +# +# 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.EzXML +using FMIImport.ZipFile + +function fmiCheckVersion(pathToFMU::String; unpackPath=nothing) + # Unzip MD + + # Download FMU if necessary + if startswith(pathToFMU, "http") + @info "Downloading FMU for Version extraction from `$(pathToFMU)`." + pathToFMU = download(pathToFMU) + end + + pathToFMU = normpath(pathToFMU) + + 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 + + zipPath = joinpath(unpackPath, fileName * ".zip") + unzippedPath = joinpath(unpackPath, fileName) + + # only copy ZIP if not already there + if !isfile(zipPath) + cp(pathToFMU, zipPath; force=true) + end + + @assert isfile(zipPath) ["fmiCheckVersion(...): ZIP-Archive couldn't be copied to `$zipPath`."] + + zipAbsPath = isabspath(zipPath) ? zipPath : joinpath(pwd(), zipPath) + unzippedAbsPath = isabspath(unzippedPath) ? unzippedPath : joinpath(pwd(), unzippedPath) + + @assert isfile(zipAbsPath) ["fmiCheckVersion(...): Can't deploy ZIP-Archive at `$(zipAbsPath)`."] + + # only unzip if not already done + if !isdir(unzippedAbsPath) + mkpath(unzippedAbsPath) + + zarchive = ZipFile.Reader(zipAbsPath) + for f in zarchive.files + if f.name == "modelDescription.xml" + fileAbsPath = normpath(joinpath(unzippedAbsPath, f.name)) + + # create directory if not forced by zip file folder + mkpath(dirname(fileAbsPath)) + + numBytes = write(fileAbsPath, read(f)) + + @assert numBytes > 0 "fmiCheckVersion(...): Can't unzip file `$(f.name)` at `$(fileAbsPath)`, file is empty." + @assert isfile(fileAbsPath) "fmiCheckVersion(...): Can't unzip file `$(f.name)` at `$(fileAbsPath)`, file does not exist in target directory." + end + + end + close(zarchive) + end + + @assert isdir(unzippedAbsPath) ["fmiCheckVersion(...): ZIP-Archive couldn't be unzipped at `$(unzippedPath)`."] + # @info "fmiUnzipVersion(...): Successfully unzipped modelDescription.xml at `$unzippedAbsPath`." + + # read version tag + + doc = readxml(normpath(joinpath(unzippedAbsPath, "modelDescription.xml"))) + + root = doc.root + version = root["fmiVersion"] + + # 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 + + # return version + return version +end \ No newline at end of file diff --git a/test/FMI2/cs_me.jl b/test/FMI2/cs_me.jl index d67dc162..5ef667fd 100644 --- a/test/FMI2/cs_me.jl +++ b/test/FMI2/cs_me.jl @@ -25,6 +25,7 @@ sol = fmiSimulateCS(fmuStruct, (t_start, t_stop)) @test sol.success sol = fmiSimulateME(fmuStruct, (t_start, t_stop)) @test sol.success + fmiUnload(myFMU) diff --git a/test/FMI3/cs_me.jl b/test/FMI3/cs_me.jl new file mode 100644 index 00000000..98bb0aa7 --- /dev/null +++ b/test/FMI3/cs_me.jl @@ -0,0 +1,54 @@ +############### +# Prepare FMU # +############### + +t_start = 0.0 +t_stop = 1.0 + +myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "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("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "3.0") +inst = fmi3InstantiateModelExchange!(myFMU; loggingOn=false) +@test inst.type == FMI.fmi3TypeModelExchange +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("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "3.0") +inst = fmi3InstantiateCoSimulation!(myFMU; loggingOn=false) +@test inst.type == FMI.fmi3TypeCoSimulation +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/exec_config.jl b/test/FMI3/exec_config.jl new file mode 100644 index 00000000..59286bf0 --- /dev/null +++ b/test/FMI3/exec_config.jl @@ -0,0 +1,80 @@ +# +# 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 + +t_start = 0.0 +t_stop = 1.0 + +myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "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`" + +for execConf in (FMU3_EXECUTION_CONFIGURATION_NO_FREEING, FMU3_EXECUTION_CONFIGURATION_RESET, FMU3_EXECUTION_CONFIGURATION_NO_RESET) # ToDo: Add `FMU3_EXECUTION_CONFIGURATION_NOTHING` + for mode in ([:CS]) + global fmuStruct + @info "\t$(mode) | $(execConf)" + + myFMU.executionConfig = execConf + + # sim test + numInst = length(myFMU.instances) + + if mode == :CS + fmiSimulateCS(fmuStruct, t_start, t_stop) + elseif mode == :ME + fmiSimulateME(fmuStruct, t_start, t_stop) + else + @assert false "Unknown mode `$(mode)`." + end + + if execConf.instantiate + numInst += 1 + end + if execConf.freeInstance + numInst -= 1 + end + + @test length(myFMU.instances) == numInst + + # prepare next run start + if envFMUSTRUCT == "FMU" + if execConf.freeInstance + fmi3FreeInstance!(myFMU) + end + + if mode == :CS + fmi3InstantiateCoSimulation!(myFMU) + elseif mode == :ME + fmi3InstantiateModelExchange!(myFMU) + end + + elseif envFMUSTRUCT == "FMUCOMPONENT" + if execConf.freeInstance + fmi3FreeInstance!(fmuStruct) + end + if mode == :CS + fmi3InstantiateCoSimulation!(myFMU) + elseif mode == :ME + fmi3InstantiateModelExchange!(myFMU) + end + + end + # prepare next run end + + end +end + +fmiUnload(myFMU) \ No newline at end of file diff --git a/test/FMI3/getter_setter.jl b/test/FMI3/getter_setter.jl new file mode 100644 index 00000000..45c65d62 --- /dev/null +++ b/test/FMI3/getter_setter.jl @@ -0,0 +1,82 @@ +# +# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher +# Licensed under the MIT license. See LICENSE file in the project root for details. +# + +import FMI.FMIImport.FMICore + +############### +# Prepare FMU # +############### + +myFMU = fmiLoad("Feedthrough", "ModelicaReferenceFMUs", "0.0.20", "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 + +realValueReferences = ["Float32_continuous_input", "Float64_continuous_input"] +integerValueReferences = ["Int32_input", "Int64_input"] +booleanValueReferences = ["Boolean_input", "Boolean_output"] +stringValueReferences = ["String_parameter", "String_parameter"] + +######################### +# 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 = "" + +fmiSet(fmuStruct, + [realValueReferences[1], integerValueReferences[1], booleanValueReferences[1], stringValueReferences[1]], + [Float32(rndReal), rndInteger, rndBoolean, rndString]) +@test fmiGet(fmuStruct, + [realValueReferences[1], integerValueReferences[1], booleanValueReferences[1], stringValueReferences[1]]) == + [Float32(rndReal), rndInteger, FMICore.fmi3Boolean(rndBoolean), rndString] + +#@test fmiGetStartValue(fmuStruct, "p_enumeration") == "myEnumeration1" +# println(fmi3ModelVariablesForValueReference(inst.fmu.modelDescription, UInt32(29))) +@test fmiGetStartValue(fmuStruct, "String_parameter") == "Set me!" +@test fmiGetStartValue(fmuStruct, "Float32_continuous_input") == 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.fmi3Int32(0), FMI.fmi3Int32(0)] +cacheBoolean = [FMI.fmi3Boolean(false), FMI.fmi3Boolean(false)] +cacheString = [pointer(""), pointer("")] + +#@test fmiGetStartValue(fmuStruct, ["p_enumeration", "p_string", "p_real"]) == ["myEnumeration1", "Hello World!", 0.0] +@test fmiGetStartValue(fmuStruct, ["String_parameter", "Float32_continuous_input"]) == ["Set me!", 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..c08dc10b --- /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("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "3.0") + +recordValues = ["h", "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/plots.jl b/test/FMI3/plots.jl new file mode 100644 index 00000000..53b6ebbd --- /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("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "3.0") + +# print some useful FMU-information into the REPL +fmiInfo(myFMU) + +# make an instance from the FMU +fmi3InstantiateCoSimulation!(myFMU) + +recordValues = ["h", "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/sim_CS.jl b/test/FMI3/sim_CS.jl new file mode 100644 index 00000000..9c7236a1 --- /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.20", "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..8f8918bc --- /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.20", "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..193cfadc --- /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("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "3.0") + +# 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..ec4ebcb7 --- /dev/null +++ b/test/FMI3/state.jl @@ -0,0 +1,54 @@ +# +# 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 + +############### +# Prepare FMU # +############### + +myFMU = fmiLoad("BouncingBall", "ModelicaReferenceFMUs", "0.0.20", "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) + @test fmiGet(fmuStruct, "h") == 1 + + FMUstate = fmiGetState(fmuStruct) + + fmiSet(fmuStruct, "h", 10.0) + @test fmiGet(fmuStruct, "h") == 10.0 + + fmiSetState(fmuStruct, FMUstate) + @test fmiGet(fmuStruct, "h") == 1 + + fmiFreeState!(fmuStruct, FMUstate) +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 # +############ + +fmiUnload(myFMU) diff --git a/test/runtests.jl b/test/runtests.jl index 987cc819..e4cbd263 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,7 +8,7 @@ using FMIZoo using Test import Random -import FMI.FMIImport.FMICore: fmi2StatusOK, fmi2ComponentStateTerminated, fmi2ComponentStateInstantiated +import FMI.FMIImport.FMICore: fmi2StatusOK, fmi3StatusOK, fmi2ComponentStateTerminated, fmi2ComponentStateInstantiated, fmi3Boolean import FMI.FMIImport.FMICore: FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATION_NO_RESET, FMU2_EXECUTION_CONFIGURATION_RESET, FMU2_EXECUTION_CONFIGURATION_NOTHING exportingToolsWindows = [("Dymola", "2022x")] @@ -16,12 +16,12 @@ exportingToolsLinux = [("Dymola", "2022x")] fmuStructs = ["FMU", "FMUCOMPONENT"] # enable assertions for warnings/errors for all default execution configurations -for exec in [FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATION_NO_RESET, FMU2_EXECUTION_CONFIGURATION_RESET, FMU2_EXECUTION_CONFIGURATION_NOTHING] +for exec in [FMU2_EXECUTION_CONFIGURATION_NO_FREEING, FMU2_EXECUTION_CONFIGURATION_NO_RESET, FMU2_EXECUTION_CONFIGURATION_RESET, FMU2_EXECUTION_CONFIGURATION_NOTHING, FMU3_EXECUTION_CONFIGURATION_NO_FREEING, FMU3_EXECUTION_CONFIGURATION_NO_RESET, FMU3_EXECUTION_CONFIGURATION_RESET] exec.assertOnError = true exec.assertOnWarning = true end -function runtests(exportingTool) +function runtestsFMI2(exportingTool) ENV["EXPORTINGTOOL"] = exportingTool[1] ENV["EXPORTINGVERSION"] = exportingTool[2] @@ -80,16 +80,61 @@ function runtests(exportingTool) end end +function runtestsFMI3(exportingTool) + ENV["EXPORTINGTOOL"] = exportingTool[1] + ENV["EXPORTINGVERSION"] = exportingTool[2] + + @testset "Testing FMUs exported from $exportingTool" begin + + for str in fmuStructs + @testset "Functions for $str" begin + ENV["FMUSTRUCT"] = str + @testset "Variable Getters / Setters (getter_setter.jl)" begin + include("FMI3/getter_setter.jl") + end + + @info "Execution Configurations (exec_config.jl)" + @testset "Execution Configurations" begin + include("FMI3/exec_config.jl") + end + + @testset "State Manipulation (state.jl)" begin + include("FMI3/state.jl") + end + + @testset "CS Simulation (sim_CS.jl)" begin + include("FMI3/sim_CS.jl") + end + @testset "ME Simulation (sim_ME.jl)" begin + include("FMI3/sim_ME.jl") + end + @testset "Support CS and ME simultaneously (cs_me.jl)" begin + include("FMI3/cs_me.jl") + end + @testset "Loading/Saving simulation results (load_save.jl)" begin + include("FMI3/load_save.jl") + end + end + end + + @testset "Plotting (plots.jl)" begin + include("FMI3/plots.jl") + end + end +end + @testset "FMI.jl" begin 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."