diff --git a/README.md b/README.md index a6254820..d27f4eb8 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ If you want to migrate your project from [*FMI.jl*](https://github.com/ThummeTo/ - [x] [*FMICore.jl*](https://github.com/ThummeTo/FMICore.jl) and [*FMIImport.jl*](https://github.com/ThummeTo/FMIImport.jl) were divided into [*FMICore.jl*](https://github.com/ThummeTo/FMICore.jl), [*FMIImport.jl*](https://github.com/ThummeTo/FMIImport.jl) and [*FMIBase.jl*](https://github.com/ThummeTo/FMIBase.jl). [*FMICore.jl*](https://github.com/ThummeTo/FMICore.jl) now holds the pure standard definition (C-types and -functions), while [*FMIBase.jl*](https://github.com/ThummeTo/FMIBase.jl) holds everything that is needed on top of that in [*FMIImport.jl*](https://github.com/ThummeTo/FMIImport.jl) as well as in [*FMIExport.jl*](https://github.com/ThummeTo/FMIExport.jl). +- [ ] Updated all library examples. + - [ ] Updated all library tests for a better code coverage. - [ ] We tried to document every function, if you find undocumented user-level functions, please open an issue or PR. @@ -73,24 +75,26 @@ unloadFMU(myFMU) ``` ## What is currently supported in FMI.jl? -- importing the full FMI 2.0.3 and FMI 3.0.0 command set, including optional specials like `fmi2GetState`, `fmi2SetState` and `fmi2GetDirectionalDerivatives` +- importing the full FMI 2.0.3 and FMI 3.0.0 command set, including optional specials like `fmi2GetFMUstate`, `fmi2SetFMUstate` and `fmi2GetDirectionalDerivatives` - parameterization, simulation & plotting of CS- and ME-FMUs - event-handling for imported discontinuous ME-FMUs | | **FMI2.0.3** | | **FMI3.0** | | **SSP1.0** | | |-----------------------------------|--------------|--------|------------|--------|------------|--------| | | Import | Export | Import | Export | Import | Export | -| CS | ✔️✔️ | 🚧 | ✔️✔️ | 📅 | 📅 | 📅 | -| ME (continuous) | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | -| ME (discontinuous) | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | -| SE | 🚫 | 🚫 | 🚧 | 📅 | 🚫 | 🚫 | -| Explicit solvers | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | -| Implicit solvers (autodiff=false) | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | -| Implicit solvers (autodiff=true) | ✔️ | ✔️✔️ | ✔️ | 📅 | 📅 | 📅 | -| get/setState | ✔️✔️ | 📅 | ✔️✔️ | 📅 | 🚫 | 🚫 | -| getDirectionalDerivatives | ✔️✔️ | 📅 | ✔️✔️ | 📅 | 🚫 | 🚫 | -| getAdjointDerivatives | 🚫 | 🚫 | ✔️✔️ | 📅 | 🚫 | 🚫 | -| FMI Cross Checks | ✔️✔️ | 📅 | 📅 | 📅 | 🚫 | 🚫 | +| CS | ✔️✔️ | 🚧 | ✔️✔️ | 📅 | 📅 | 📅 | +| ME (continuous) | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | +| ME (discontinuous) | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | +| SE | 🚫 | 🚫 | 🚧 | 📅 | 🚫 | 🚫 | +| Explicit solvers | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | +| Implicit solvers (autodiff=false) | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 📅 | 📅 | +| Implicit solvers (autodiff=true) | ✔️ | ✔️✔️ | ✔️ | 📅 | 📅 | 📅 | +| get/setFMUstate | ✔️✔️ | 📅 | ✔️✔️ | 📅 | 🚫 | 🚫 | +| getDirectionalDerivatives | ✔️✔️ | 📅 | ✔️✔️ | 📅 | 🚫 | 🚫 | +| getAdjointDerivatives | 🚫 | 🚫 | ✔️✔️ | 📅 | 🚫 | 🚫 | +| FMI Cross Checks | ✔️✔️ | 📅 | 📅 | 📅 | 🚫 | 🚫 | +| 64-bit binaries in FMUs | ✔️✔️ | ✔️✔️ | ✔️✔️ | 📅 | 🚫 | 🚫 | +| 32-bit binaries in FMUs | ✔️ | 📅 | 📅 | 📅 | 🚫 | 🚫 | ✔️✔️ supported & CI-tested @@ -130,7 +134,7 @@ Tobias Thummerer, Johannes Tintenherr, Lars Mikelsons. 2021 **Hybrid modeling of ## 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: +During development of new implementations or optimizations on existing code, one will have to make design decisions that influence the library performance and usability. The following prioritization 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](https://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 and feel "the Julia way" (e.g. by using multiple dispatch instead of the "C coding style"), as long as being fully compliant with the FMI standard. diff --git a/examples/src/inputs.ipynb b/examples/src/inputs.ipynb index f2c14fd9..2cd2e25a 100644 --- a/examples/src/inputs.ipynb +++ b/examples/src/inputs.ipynb @@ -8,6 +8,8 @@ "# Simulate an FMU with inputs\n", "Tutorial by Tobias Thummerer\n", "\n", + "🚧 This tutorial is under revision and will be replaced by an up-to-date version soon 🚧\n", + "\n", "## License" ] }, @@ -115,7 +117,7 @@ "outputs": [], "source": [ "# we use an FMU from the FMIZoo.jl\n", - "fmu = fmiLoad(\"SpringPendulumExtForce1D\", \"Dymola\", \"2022x\"; type=:ME) # load FMU in ME-Mode (\"Model Exchange\")" + "fmu = loadFMU(\"SpringPendulumExtForce1D\", \"Dymola\", \"2022x\"; type=:ME) # load FMU in ME-Mode (\"Model Exchange\")" ] }, { @@ -125,7 +127,7 @@ "source": [ "#### Simulate as Model-Exchange\n", "\n", - "In the function `fmiSimulate()` the FMU is simulated 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." + "In the function `simulate()` the FMU is simulated 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." ] }, { @@ -147,12 +149,12 @@ "end \n", "\n", "# simulate while setting inputs\n", - "data_extForce_t = fmiSimulate(fmu, (tStart, tStop); # FMU, start and stop time\n", - " saveat=tSave, # timepoints for the ODE solution to be saved\n", - " inputValueReferences=[\"extForce\"], # the value references that should be set (inputs)\n", - " inputFunction=extForce_t, # the input function to be used\n", - " dtmax=1e-2, # limit max step size to capture inputs\n", - " showProgress=false) # disable progress bar\n", + "data_extForce_t = simulate(fmu, (tStart, tStop); # FMU, start and stop time\n", + " saveat=tSave, # timepoints for the ODE solution to be saved\n", + " inputValueReferences=[\"extForce\"], # the value references that should be set (inputs)\n", + " inputFunction=extForce_t, # the input function to be used\n", + " dtmax=1e-2, # limit max step size to capture inputs\n", + " showProgress=false) # disable progress bar\n", "plot(data_extForce_t)" ] }, @@ -168,7 +170,8 @@ " 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", - " u[1] =sin(t) * x1\n", + " u[1] = sin(t) * x1\n", + " nothing\n", "end \n", "\n", "# simulate while setting inputs\n", @@ -198,7 +201,7 @@ }, "outputs": [], "source": [ - "fmiUnload(fmu)" + "unloadFMU(fmu)" ] } ], diff --git a/examples/src/manipulation.ipynb b/examples/src/manipulation.ipynb index 5df47fd6..571bc09b 100644 --- a/examples/src/manipulation.ipynb +++ b/examples/src/manipulation.ipynb @@ -7,6 +7,8 @@ "# Manipulate a function\n", "Tutorial by Tobias Thummerer, Johannes Stoljar\n", "\n", + "🚧 This tutorial is under revision and will be replaced by an up-to-date version soon 🚧\n", + "\n", "## License" ] }, diff --git a/examples/src/parameter_optimization.ipynb b/examples/src/parameter_optimization.ipynb index 6d204bf2..08d3804c 100644 --- a/examples/src/parameter_optimization.ipynb +++ b/examples/src/parameter_optimization.ipynb @@ -137,8 +137,8 @@ "outputs": [], "source": [ "# we use an FMU from the FMIZoo.jl\n", - "fmu = fmiLoad(\"SpringPendulum1D\", \"Dymola\", \"2022x\"; type=:ME)\n", - "fmiInfo(fmu)" + "fmu = loadFMU(\"SpringPendulum1D\", \"Dymola\", \"2022x\"; type=:ME)\n", + "info(fmu)" ] }, { @@ -177,10 +177,10 @@ " x0 = [s0, v0]\n", "\n", " # simulate with given start stae and parameters\n", - " sol = fmiSimulate(fmu, (tStart, tStop); x0=x0, parameters=paramDict, saveat=tSave)\n", + " sol = simulate(fmu, (tStart, tStop); x0=x0, parameters=paramDict, saveat=tSave)\n", "\n", " # get state with index 1 (the position) from the solution\n", - " s_res = fmiGetSolutionState(sol, 1; isIndex=true) \n", + " s_res = getState(sol, 1; isIndex=true) \n", "\n", " return s_res\n", "end\n", diff --git a/src/sim.jl b/src/sim.jl index a558cb59..0b2c03ee 100644 --- a/src/sim.jl +++ b/src/sim.jl @@ -30,15 +30,15 @@ You can force a specific simulation mode by calling [`simulateCS`](@ref), [`simu # Keyword arguments - `recordValues::fmi2ValueReferenceFormat` = nothing: Array of variables (Strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues` - `saveat = nothing`: Time points to save (interpolated) values at (default = nothing: save at each solver timestep) -- `setup::Bool`: call fmi2SetupExperiment, fmi2EnterInitializationMode and fmi2ExitInitializationMode before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `reset::Bool`: call fmi2Reset before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `instantiate::Bool`: call fmi2Instantiate! before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `freeInstance::Bool`: call fmi2FreeInstance after each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `terminate::Bool`: call fmi2Terminate after each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) +- `setup::Bool`: call fmi2SetupExperiment, fmi2EnterInitializationMode and fmi2ExitInitializationMode before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `reset::Bool`: call fmi2Reset before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `instantiate::Bool`: call fmi2Instantiate! before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `freeInstance::Bool`: call fmi2FreeInstance after each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `terminate::Bool`: call fmi2Terminate after each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) - `inputValueReferences::fmi2ValueReferenceFormat = nothing`: Input variables (Strings or variableIdentifiers) to set at each simulation step - `inputFunction = nothing`: Function to get values for the input variables at each simulation step. - `parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing`: Dict of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization -- `showProgress::Bool = true`: print simulation progressmeter in REPL +- `showProgress::Bool = true`: print simulation progress meter in REPL ## Input function pattern [`c`: current component, `u`: current state ,`t`: current time, returning array of values to be passed to `fmi2SetReal(..., inputValueReferences, inputFunction(...))` or `fmi3SetFloat64`]: @@ -110,18 +110,18 @@ State- and Time-Events are handled correctly. - `recordEventIndicators::Union{AbstractArray{<:Integer, 1}, UnitRange{<:Integer}, Nothing} = nothing`: Array or Range of event indicators to record - `recordEigenvalues::Bool=false`: compute and record eigenvalues - `saveat = nothing`: Time points to save (interpolated) values at (default = nothing: save at each solver timestep) -- `x0::Union{AbstractArray{<:Real}, Nothing} = nothing`: inital fmu State (default = nothing: use current or default-inital fmu state) -- `setup::Bool`: call fmi2SetupExperiment, fmi2EnterInitializationMode and fmi2ExitInitializationMode before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `reset::Bool`: call fmi2Reset before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `instantiate::Bool`: call fmi2Instantiate! before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `freeInstance::Bool`: call fmi2FreeInstance after each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `terminate::Bool`: call fmi2Terminate after each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) +- `x0::Union{AbstractArray{<:Real}, Nothing} = nothing`: initial fmu State (default = nothing: use current or default-initial fmu state) +- `setup::Bool`: call fmi2SetupExperiment, fmi2EnterInitializationMode and fmi2ExitInitializationMode before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `reset::Bool`: call fmi2Reset before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `instantiate::Bool`: call fmi2Instantiate! before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `freeInstance::Bool`: call fmi2FreeInstance after each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `terminate::Bool`: call fmi2Terminate after each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) - `inputValueReferences::fmi2ValueReferenceFormat = nothing`: Input variables (Strings or variableIdentifiers) to set at each simulation step - `inputFunction = nothing`: Function to get values for the input variables at each simulation step. - `parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing`: Dict of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization - `callbacksBefore = []`: callbacks to call *before* the internal callbacks for state- and time-events are called - `callbacksAfter = []`: callbacks to call *after* the internal callbacks for state- and time-events are called -- `showProgress::Bool = true`: print simulation progressmeter in REPL +- `showProgress::Bool = true`: print simulation progress meter in REPL - `solveKwargs...`: keyword arguments that get passed onto the solvers solve call ## Input function pattern @@ -161,7 +161,7 @@ function simulateME( solveKwargs..., ) - @assert isModelExchange(fmu) "simulateME(...): This function supports Model Excahnge FMUs only." + @assert isModelExchange(fmu) "simulateME(...): This function supports Model Exchange FMUs only." recordValues = prepareValueReference(fmu, recordValues) inputValueReferences = prepareValueReference(fmu, inputValueReferences) @@ -309,15 +309,15 @@ State- and Time-Events are handled internally by the FMU. - `tolerance::Union{Real, Nothing} = nothing`: The tolerance for the internal FMU solver. - `recordValues::fmi2ValueReferenceFormat` = nothing: Array of variables (Strings or variableIdentifiers) to record. Results are returned as `DiffEqCallbacks.SavedValues` - `saveat = nothing`: Time points to save (interpolated) values at (default = nothing: save at each solver timestep) -- `setup::Bool`: call fmi2SetupExperiment, fmi2EnterInitializationMode and fmi2ExitInitializationMode before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `reset::Bool`: call fmi2Reset before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `instantiate::Bool`: call fmi2Instantiate! before each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `freeInstance::Bool`: call fmi2FreeInstance after each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) -- `terminate::Bool`: call fmi2Terminate after each step (default = nothing: use value from `fmu`'s `FMU2ExecutionConfiguration`) +- `setup::Bool`: call fmi2SetupExperiment, fmi2EnterInitializationMode and fmi2ExitInitializationMode before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `reset::Bool`: call fmi2Reset before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `instantiate::Bool`: call fmi2Instantiate! before each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `freeInstance::Bool`: call fmi2FreeInstance after each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) +- `terminate::Bool`: call fmi2Terminate after each step (default = nothing: use value from `fmu`'s `FMUExecutionConfiguration`) - `inputValueReferences::fmi2ValueReferenceFormat = nothing`: Input variables (Strings or variableIdentifiers) to set at each simulation step - `inputFunction = nothing`: Function to get values for the input variables at each simulation step. - `parameters::Union{Dict{<:Any, <:Any}, Nothing} = nothing`: Dict of parameter variables (strings or variableIdentifiers) and values (Real, Integer, Boolean, String) to set parameters during initialization -- `showProgress::Bool = true`: print simulation progressmeter in REPL +- `showProgress::Bool = true`: print simulation progress meter in REPL ## Input function pattern [`c`: current component, `u`: current state ,`t`: current time, returning array of values to be passed to `fmi2SetReal(..., inputValueReferences, inputFunction(...))` or `fmi3SetFloat64`]: diff --git a/test/runtests.jl b/test/runtests.jl index 748ab0e2..06ccb802 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ using FMI.FMIImport using DifferentialEquations: FBDF -fmuStructs = ("FMU", "FMUCOMPONENT") +fmuStructs = ("FMU", "INSTANCE") # enable assertions for warnings/errors for all default execution configurations for exec in FMU_EXECUTION_CONFIGURATIONS @@ -37,7 +37,7 @@ function getFMUStruct( kwargs..., ) - # choose FMU or FMUComponent + # choose FMU or FMUInstance if endswith(modelname, ".fmu") fmu = loadFMU(modelname; kwargs...) else @@ -47,7 +47,7 @@ function getFMUStruct( if fmustruct == "FMU" return fmu, fmu - elseif fmustruct == "FMUCOMPONENT" + elseif fmustruct == "INSTANCE" inst, _ = FMI.prepareSolveFMU(fmu, nothing, mode; loggingOn = true) @test !isnothing(inst) return inst, fmu @@ -57,52 +57,52 @@ function getFMUStruct( end end +toolversions = [("Dymola", "2023x")] # ("SimulationX", "4.5.2") + @testset "FMI.jl" begin if Sys.iswindows() || Sys.islinux() @info "Automated testing is supported on Windows/Linux." - ENV["EXPORTINGTOOL"] = "Dymola" - ENV["EXPORTINGVERSION"] = "2023x" + for (tool, version) in toolversions - for fmiversion in (2.0, 3.0) - ENV["FMIVERSION"] = fmiversion + ENV["EXPORTINGTOOL"] = tool + ENV["EXPORTINGVERSION"] = version - @testset "Testing FMI $(ENV["FMIVERSION"]) FMUs exported from $(ENV["EXPORTINGTOOL"]) $(ENV["EXPORTINGVERSION"])" begin + for fmiversion in (2.0, 3.0) + ENV["FMIVERSION"] = fmiversion - for fmustruct in fmuStructs - ENV["FMUSTRUCT"] = fmustruct + @testset "Testing FMI $(ENV["FMIVERSION"]) FMUs exported from $(ENV["EXPORTINGTOOL"]) $(ENV["EXPORTINGVERSION"])" begin - @testset "Functions for $(ENV["FMUSTRUCT"])" begin + for fmustruct in fmuStructs + ENV["FMUSTRUCT"] = fmustruct - @info "CS Simulation (sim_CS.jl)" - @testset "CS Simulation" begin - include("sim_CS.jl") - end + @testset "Functions for $(ENV["FMUSTRUCT"])" begin - @info "ME Simulation (sim_ME.jl)" - @testset "ME Simulation" begin - include("sim_ME.jl") - end + @info "CS Simulation (sim_CS.jl)" + @testset "CS Simulation" begin + include("sim_CS.jl") + end - @info "SE Simulation (sim_SE.jl)" - @testset "SE Simulation" begin - include("sim_SE.jl") - end + @info "ME Simulation (sim_ME.jl)" + @testset "ME Simulation" begin + include("sim_ME.jl") + end + + @info "SE Simulation (sim_SE.jl)" + if fmiversion == 3.0 + @testset "SE Simulation" begin + include("sim_SE.jl") + end + else + @info "Skipping SE tests for FMI $(fmiversion), because this is not supported by the corresponding FMI version." + end - @info "Simulation FMU without states (sim_zero_state.jl)" - @testset "Simulation FMU without states" begin - include("sim_zero_state.jl") + @info "Simulation FMU without states (sim_zero_state.jl)" + @testset "Simulation FMU without states" begin + include("sim_zero_state.jl") + end end end - - # if VERSION >= v"1.9.0" - # @info "Performance (performance.jl)" - # @testset "Performance" begin - # include("FMI2/performance.jl") - # end - # else - @info "Julia Version $(VERSION), skipping performance tests ..." - #end end end end