OMCompiler/Compiler/Template/CodegenFMU3.tpl
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of OpenModelica. | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), | ||
| 5 | * c/o Linköpings universitet, Department of Computer and Information Science, | ||
| 6 | * SE-58183 Linköping, Sweden. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR | ||
| 11 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. | ||
| 12 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES | ||
| 13 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL | ||
| 14 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 15 | * | ||
| 16 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) | ||
| 17 | * Public License (OSMC-PL) are obtained from OSMC, either from the above | ||
| 18 | * address, from the URLs: | ||
| 19 | * http://www.openmodelica.org or | ||
| 20 | * https://github.com/OpenModelica/ or | ||
| 21 | * http://www.ida.liu.se/projects/OpenModelica, | ||
| 22 | * and in the OpenModelica distribution. | ||
| 23 | * | ||
| 24 | * GNU AGPL version 3 is obtained from: | ||
| 25 | * https://www.gnu.org/licenses/licenses.html#GPL | ||
| 26 | * | ||
| 27 | * This program is distributed WITHOUT ANY WARRANTY; without | ||
| 28 | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 29 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH | ||
| 30 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. | ||
| 31 | * | ||
| 32 | * See the full OSMC Public License conditions for more details. | ||
| 33 | * | ||
| 34 | */ | ||
| 35 | |||
| 36 | // This file defines templates for generating the modelDescription.xml file for | ||
| 37 | // the FMI 3.0 (released, "3.0") export of OpenModelica. | ||
| 38 | // | ||
| 39 | // The most important differences from FMI 2.0 (CodegenFMU2.tpl) are: | ||
| 40 | // | ||
| 41 | // - the root attribute is `instantiationToken` instead of `guid`; | ||
| 42 | // - the FMU may declare ModelExchange, CoSimulation and ScheduledExecution; | ||
| 43 | // - ModelVariables contains typed variable elements (<Float64>, <Int32>, | ||
| 44 | // <Boolean>, <String>, <Enumeration>, ...) instead of <ScalarVariable> with | ||
| 45 | // a nested type element; | ||
| 46 | // - the independent variable (time) must be declared explicitly with | ||
| 47 | // causality="independent"; | ||
| 48 | // - value references must be globally unique (in FMI 2.0 they only had to be | ||
| 49 | // unique per base type); we achieve this with the per-base-type offset | ||
| 50 | // scheme implemented in SimCodeCodegenUtil.getFMI3ValueReference; | ||
| 51 | // - ModelStructure references unknowns by valueReference (Output, | ||
| 52 | // ContinuousStateDerivative, InitialUnknown, EventIndicator) instead of by | ||
| 53 | // a 1-based index. | ||
| 54 | |||
| 55 | package CodegenFMU3 | ||
| 56 | |||
| 57 | import interface SimCodeTV; | ||
| 58 | import interface SimCodeBackendTV; | ||
| 59 | import CodegenUtil.*; | ||
| 60 | import CodegenUtilSimulation.*; | ||
| 61 | import CodegenC.*; //unqualified import, no need the CodegenC is optional when calling a template; or mandatory when the same named template exists in this package (name hiding) | ||
| 62 | import CodegenFMUCommon.*; | ||
| 63 | |||
| 64 | // Code for generating modelDescription.xml file for FMI 3.0. | ||
| 65 | 16 | template fmiModelDescription(SimCode simCode, String guid, String FMUType, list<String> sourceFiles) | |
| 66 | "Generates code for ModelDescription file for FMI 3.0 FMU target." | ||
| 67 | ::= | ||
| 68 | match simCode | ||
| 69 | case SIMCODE(__) then | ||
| 70 | << | ||
| 71 | <fmiModelDescription | ||
| 72 | <%fmiModelDescriptionAttributes(simCode,guid)%>> | ||
| 73 | <%if isFMIMEType(FMUType) then ModelExchange3(simCode, sourceFiles)%> | ||
| 74 | <%if isFMICSType(FMUType) then CoSimulation3(simCode, sourceFiles)%> | ||
| 75 | <%if isFMISEType(FMUType) then ScheduledExecution3(simCode, sourceFiles)%> | ||
| 76 | <%UnitDefinitions(simCode)%> | ||
| 77 | <%TypeDefinitions3(simCode)%> | ||
| 78 | <% if Flags.isSet(Flags.FMU_EXPERIMENTAL) then | ||
| 79 | << | ||
| 80 | <LogCategories> | ||
| 81 | <Category name="logEvents" description="logEvents" /> | ||
| 82 | <Category name="logSingularLinearSystems" description="logSingularLinearSystems" /> | ||
| 83 | <Category name="logNonlinearSystems" description="logNonlinearSystems" /> | ||
| 84 | <Category name="logDynamicStateSelection" description="logDynamicStateSelection" /> | ||
| 85 | <Category name="logStatusWarning" description="logStatusWarning" /> | ||
| 86 | <Category name="logStatusDiscard" description="logStatusDiscard" /> | ||
| 87 | <Category name="logStatusError" description="logStatusError" /> | ||
| 88 | <Category name="logStatusFatal" description="logStatusFatal" /> | ||
| 89 | <Category name="logStatusPending" description="logStatusPending" /> | ||
| 90 | <Category name="logAll" description="logAll" /> | ||
| 91 | <Category name="logFmi3Call" description="logFmi3Call" /> | ||
| 92 | </LogCategories> | ||
| 93 | >> else | ||
| 94 | << | ||
| 95 | <LogCategories> | ||
| 96 | <Category name="logEvents" /> | ||
| 97 | <Category name="logSingularLinearSystems" /> | ||
| 98 | <Category name="logNonlinearSystems" /> | ||
| 99 | <Category name="logDynamicStateSelection" /> | ||
| 100 | <Category name="logStatusWarning" /> | ||
| 101 | <Category name="logStatusDiscard" /> | ||
| 102 | <Category name="logStatusError" /> | ||
| 103 | <Category name="logStatusFatal" /> | ||
| 104 | <Category name="logStatusPending" /> | ||
| 105 | <Category name="logAll" /> | ||
| 106 | <Category name="logFmi3Call" /> | ||
| 107 | </LogCategories> | ||
| 108 | >> %> | ||
| 109 | <%DefaultExperiment3(simulationSettingsOpt)%> | ||
| 110 | <%fmiModelVariablesAndStructure3(simCode, FMUType)%> | ||
| 111 | <%fmiOpenModelicaAnnotations(simCode)%> | ||
| 112 | </fmiModelDescription> | ||
| 113 | >> | ||
| 114 | end fmiModelDescription; | ||
| 115 | |||
| 116 | 16 | template fmiModelVariablesAndStructure3(SimCode simCode, String FMUType) | |
| 117 | "<ModelVariables> and <ModelStructure>, from the view with one variable per | ||
| 118 | array, with the alias and value reference tables built for the length of them: | ||
| 119 | without them each entry searches every variable the model has." | ||
| 120 | ::= | ||
| 121 | let _ = SimCodeCodegenUtil.cacheFMI3VariableAliases(simCode) | ||
| 122 | match SimCodeCodegenUtil.fmi3ArrayView(simCode) | ||
| 123 | case view as SIMCODE(modelStructure=viewStructure) then | ||
| 124 | let variables = fmiModelVariables3(view, FMUType) | ||
| 125 | let structure = modelStructure3(view, viewStructure) | ||
| 126 | let _ = SimCodeCodegenUtil.clearFMI3VariableAliases() | ||
| 127 | << | ||
| 128 | <%variables%> | ||
| 129 | <%structure%> | ||
| 130 | >> | ||
| 131 | end fmiModelVariablesAndStructure3; | ||
| 132 | |||
| 133 | 16 | template fmiOpenModelicaAnnotations(SimCode simCode) | |
| 134 | "OpenModelica vendor annotations (<Figures> and <Visualization>) under one Annotation | ||
| 135 | element; empty when the model has neither. FMI 3.0 replaced 2.0\'s <Tool name=...> | ||
| 136 | with <Annotation type=...>, which is what fmi3Annotation.xsd validates." | ||
| 137 | ::= | ||
| 138 | let figures = fmiFiguresBody(simCode) | ||
| 139 | let visualization = fmiVisualizationElement(simCode) | ||
| 140 | if boolAnd(stringEq(figures,""), stringEq(visualization,"")) then '' else | ||
| 141 | << | ||
| 142 | <Annotations> | ||
| 143 | <Annotation type="org.openmodelica"> | ||
| 144 | <%figures%> | ||
| 145 | <%visualization%> | ||
| 146 | </Annotation> | ||
| 147 | </Annotations> | ||
| 148 | >> | ||
| 149 | end fmiOpenModelicaAnnotations; | ||
| 150 | |||
| 151 | ✗ | template fmiFiguresBody(SimCode simCode) | |
| 152 | "The <Figures> element, or nothing when no figures resolve." | ||
| 153 | ::= | ||
| 154 | match simCode | ||
| 155 | case SIMCODE(fmiFigures = {}) then '' | ||
| 156 | case SIMCODE(fmiFigures = figures) then | ||
| 157 | << | ||
| 158 | <Figures version="1"> | ||
| 159 | <%figures |> f => Figure3(f) ;separator="\n"%> | ||
| 160 | </Figures> | ||
| 161 | >> | ||
| 162 | end fmiFiguresBody; | ||
| 163 | |||
| 164 | 16 | template fmiVisualizationElement(SimCode simCode) | |
| 165 | "A <Visualization> element pointing at the _visual.xml resource, or nothing." | ||
| 166 | ::= | ||
| 167 | let resource = SimCodeCodegenUtil.getFMI3VisualizationResource(simCode) | ||
| 168 | if stringEq(resource, "") then '' else | ||
| 169 | '<Visualization version="1" file="<%Util.escapeModelicaStringToXmlString(resource)%>"/>' | ||
| 170 | end fmiVisualizationElement; | ||
| 171 | |||
| 172 | ✗ | template Figure3(FmiFigure figure) | |
| 173 | ::= | ||
| 174 | match figure | ||
| 175 | case FMI_FIGURE(__) then | ||
| 176 | let titleAttr = figureStrAttr("title", title) | ||
| 177 | let groupAttr = figureStrAttr("group", group) | ||
| 178 | let prefAttr = if preferred then ' preferred="true"' | ||
| 179 | let captionElt = if boolNot(stringEq(caption, "")) then '<%\n%> <Caption><%Util.escapeModelicaStringToXmlString(caption)%></Caption>' | ||
| 180 | << | ||
| 181 | <Figure<%titleAttr%><%groupAttr%><%prefAttr%>> | ||
| 182 | <%plots |> p => FigurePlot3(p) ;separator="\n"%><%captionElt%> | ||
| 183 | </Figure> | ||
| 184 | >> | ||
| 185 | end Figure3; | ||
| 186 | |||
| 187 | ✗ | template FigurePlot3(FmiPlot plot) | |
| 188 | ::= | ||
| 189 | match plot | ||
| 190 | case FMI_PLOT(__) then | ||
| 191 | let titleAttr = figureStrAttr("title", title) | ||
| 192 | let xAxisElt = FigureAxis3("x", xAxis) | ||
| 193 | let yAxisElt = FigureAxis3("y", yAxis) | ||
| 194 | let curveElts = (curves |> c => FigureCurve3(c) ;separator="\n") | ||
| 195 | let termRef = match terminal case SOME(t) then '<%\n%> <TerminalRef terminal="<%Util.escapeModelicaStringToXmlString(t)%>"/>' | ||
| 196 | << | ||
| 197 | <Plot<%titleAttr%>> | ||
| 198 | <%xAxisElt%><%yAxisElt%><%curveElts%><%termRef%> | ||
| 199 | </Plot> | ||
| 200 | >> | ||
| 201 | end FigurePlot3; | ||
| 202 | |||
| 203 | ✗ | template FigureAxis3(String role, FmiFigureAxis axis) | |
| 204 | "An <Axis> element, emitted only when it carries a non-default attribute." | ||
| 205 | ::= | ||
| 206 | match axis | ||
| 207 | case FMI_FIGURE_AXIS(__) then | ||
| 208 | let labelAttr = figureStrAttr("label", label) | ||
| 209 | let unitAttr = figureStrAttr("unit", unit) | ||
| 210 | let minAttr = match min case SOME(r) then ' min="<%r%>"' | ||
| 211 | let maxAttr = match max case SOME(r) then ' max="<%r%>"' | ||
| 212 | let scaleAttr = if logScale then ' scale="Log"' | ||
| 213 | let body = '<%labelAttr%><%unitAttr%><%minAttr%><%maxAttr%><%scaleAttr%>' | ||
| 214 | if stringEq(body, "") then '' else '<Axis role="<%role%>"<%body%>/><%\n%> ' | ||
| 215 | end FigureAxis3; | ||
| 216 | |||
| 217 | ✗ | template FigureCurve3(FmiCurve curve) | |
| 218 | ::= | ||
| 219 | match curve | ||
| 220 | case FMI_CURVE(__) then | ||
| 221 | let yAttr = ' y="<%figureCrefName(yVariable)%>"' | ||
| 222 | let xAttr = match xVariable case SOME(x) then ' x="<%figureCrefName(x)%>"' | ||
| 223 | let legendAttr = figureStrAttr("legend", legend) | ||
| 224 | '<Curve<%yAttr%><%xAttr%><%legendAttr%>/>' | ||
| 225 | end FigureCurve3; | ||
| 226 | |||
| 227 | ✗ | template figureCrefName(DAE.ComponentRef cref) | |
| 228 | "A cref formatted exactly like a variable name in modelDescription.xml." | ||
| 229 | ::= | ||
| 230 | Util.escapeModelicaStringToXmlString(System.stringReplace(crefStrNoUnderscore(cref),"$", "_D_")) | ||
| 231 | end figureCrefName; | ||
| 232 | |||
| 233 | ✗ | template figureStrAttr(String name, String value) | |
| 234 | "A non-empty XML attribute, or nothing." | ||
| 235 | ::= | ||
| 236 | if stringEq(value, "") then '' else ' <%name%>="<%Util.escapeModelicaStringToXmlString(value)%>"' | ||
| 237 | end figureStrAttr; | ||
| 238 | |||
| 239 | 15 | template fmiBuildDescriptionFile(SimCode simCode, list<String> sourceFiles, String fileNamePrefixHash) | |
| 240 | "Writes sources/buildDescription.xml (FMI 3.0). Returns the empty string (the | ||
| 241 | content is written to a file). Skipped when there are no source files | ||
| 242 | (--fmiFilter=blackBox or fmiSources=false)." | ||
| 243 | ::= | ||
| 244 | match sourceFiles | ||
| 245 | case {} then '' | ||
| 246 | else | ||
| 247 | let()= textFile(fmiBuildDescription(simCode, sourceFiles), '<%fileNamePrefixHash%>.fmutmp/sources/buildDescription.xml') | ||
| 248 | '' | ||
| 249 | end fmiBuildDescriptionFile; | ||
| 250 | |||
| 251 | 2 | template fmiBuildDescription(SimCode simCode, list<String> sourceFiles) | |
| 252 | "Generates the FMI 3.0 sources/buildDescription.xml: the C source files, include | ||
| 253 | directories and preprocessor definitions needed to build the source FMU. In FMI | ||
| 254 | 2.0 the source file list lived in modelDescription.xml (<SourceFiles>); FMI 3.0 | ||
| 255 | moves it here. Paths are relative to the sources/ directory." | ||
| 256 | ::= | ||
| 257 | match simCode | ||
| 258 | case SIMCODE(__) then | ||
| 259 | let modelIdentifier = modelNamePrefix(simCode) | ||
| 260 | << | ||
| 261 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 262 | <fmiBuildDescription fmiVersion="3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/modelica/fmi-standard/v3.0.2/schema/fmi3BuildDescription.xsd"> | ||
| 263 | <BuildConfiguration modelIdentifier="<%modelIdentifier%>"> | ||
| 264 | <SourceFileSet language="C17"> | ||
| 265 | <%sourceFiles |> file => if boolNot(StringUtil.endsWith(file, ".rs")) then '<SourceFile name="<%file%>"/>' ;separator="\n"%> | ||
| 266 | <PreprocessorDefinition name="FMI2_OVERRIDE_FUNCTION_PREFIX"/> | ||
| 267 | <PreprocessorDefinition name="FMI3_OVERRIDE_FUNCTION_PREFIX"/> | ||
| 268 | <IncludeDirectory name="."/> | ||
| 269 | <IncludeDirectory name="fmi"/> | ||
| 270 | </SourceFileSet> | ||
| 271 | <%rustSourceFileSet(sourceFiles)%> | ||
| 272 | </BuildConfiguration> | ||
| 273 | </fmiBuildDescription> | ||
| 274 | >> | ||
| 275 | end fmiBuildDescription; | ||
| 276 | |||
| 277 | 2 | template rustSourceFileSet(list<String> sourceFiles) | |
| 278 | "The Rust half of a --simCodeTarget=C source FMU: the crate roots, built into | ||
| 279 | the libSimulationRuntimeRust archive the C set links (see sources/CMakeLists.txt)." | ||
| 280 | ::= | ||
| 281 | let files = (sourceFiles |> file => if StringUtil.endsWith(file, ".rs") then '<SourceFile name="<%file%>"/>' ;separator="\n") | ||
| 282 | if files then | ||
| 283 | << | ||
| 284 | <SourceFileSet language="Rust2024" compiler="cargo" compilerOptions="rustc --release --manifest-path rust/SimulationRuntime/rust/Cargo.toml -p openmodelica_simulation_runtime --lib --crate-type staticlib"> | ||
| 285 | <%files%> | ||
| 286 | </SourceFileSet> | ||
| 287 | >> | ||
| 288 | end rustSourceFileSet; | ||
| 289 | |||
| 290 | 15 | template fmiTerminalsAndIconsFile(SimCode simCode, String fileNamePrefixHash) | |
| 291 | "Writes terminalsAndIcons/terminalsAndIcons.xml into the FMU when the model has | ||
| 292 | connector-derived terminals. Returns the empty string (the content is written to | ||
| 293 | a file). The terminalsAndIcons/ directory is created in SimCodeMain beforehand." | ||
| 294 | ::= | ||
| 295 | match SimCodeCodegenUtil.getFMI3Terminals(simCode) | ||
| 296 | case {} then '' | ||
| 297 | case terminals then | ||
| 298 | let()= textFile(fmiTerminalsAndIcons(terminals), '<%fileNamePrefixHash%>.fmutmp/terminalsAndIcons/terminalsAndIcons.xml') | ||
| 299 | '' | ||
| 300 | end fmiTerminalsAndIconsFile; | ||
| 301 | |||
| 302 | 8 | template fmiTerminalsAndIcons(list<FmiTerminal> terminals) | |
| 303 | "Generates the terminalsAndIcons.xml content (FMI 3.0 Terminals): one <Terminal> | ||
| 304 | per connector instance, grouping its member variables. The connector membership | ||
| 305 | is detected from the flat-model component type (a connector-typed cref qualifier) | ||
| 306 | in SimCodeCodegenUtil.getFMI3Terminals." | ||
| 307 | ::= | ||
| 308 | << | ||
| 309 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 310 | <fmiTerminalsAndIcons fmiVersion="3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/modelica/fmi-standard/v3.0.2/schema/fmi3TerminalsAndIcons.xsd"> | ||
| 311 | <Terminals> | ||
| 312 | <%terminals |> t => Terminal3(t) ;separator="\n"%> | ||
| 313 | </Terminals> | ||
| 314 | </fmiTerminalsAndIcons> | ||
| 315 | >> | ||
| 316 | end fmiTerminalsAndIcons; | ||
| 317 | |||
| 318 | 130 | template Terminal3(FmiTerminal terminal) | |
| 319 | "Generates one <Terminal>. Members are matched by name (memberName) when two | ||
| 320 | terminals are connected: matchingRule=\"plug\" for an ordinary, fully-defined | ||
| 321 | Modelica connector (all members must match), \"bus\" for an expandable connector | ||
| 322 | (partial matching allowed). terminalKind, when known, carries the connector type | ||
| 323 | path so importers can check type compatibility." | ||
| 324 | ::= | ||
| 325 | match terminal | ||
| 326 | case FMI_TERMINAL(__) then | ||
| 327 | let rule = if isExpandable then "bus" else "plug" | ||
| 328 | let kindAttr = if stringEq(terminalKind, "") then "" else ' terminalKind="<%Util.escapeModelicaStringToXmlString(terminalKind)%>"' | ||
| 329 | << | ||
| 330 | <Terminal name="<%Util.escapeModelicaStringToXmlString(name)%>" matchingRule="<%rule%>"<%kindAttr%>> | ||
| 331 | <%members |> m => TerminalMember3(m) ;separator="\n"%> | ||
| 332 | </Terminal> | ||
| 333 | >> | ||
| 334 | end Terminal3; | ||
| 335 | |||
| 336 | 376 | template TerminalMember3(FmiTerminalMember member) | |
| 337 | "Generates a <TerminalMemberVariable> referencing a modelDescription variable. | ||
| 338 | variableName is formatted exactly like the variable name in modelDescription.xml." | ||
| 339 | ::= | ||
| 340 | match member | ||
| 341 | case FMI_TERMINAL_MEMBER(__) then | ||
| 342 | let varName = Util.escapeModelicaStringToXmlString(System.stringReplace(crefStrNoUnderscore(variable),"$", "_D_")) | ||
| 343 | '<TerminalMemberVariable variableName="<%varName%>" memberName="<%Util.escapeModelicaStringToXmlString(memberName)%>" variableKind="<%variableKind%>"/>' | ||
| 344 | end TerminalMember3; | ||
| 345 | |||
| 346 | 16 | template fmiModelDescriptionAttributes(SimCode simCode, String guid) | |
| 347 | "Generates the attributes of the fmiModelDescription element for FMI 3.0." | ||
| 348 | ::= | ||
| 349 | match simCode | ||
| 350 | case SIMCODE(modelInfo = MODELINFO(varInfo = vi as VARINFO(__), vars = SIMVARS(stateVars = listStates))) then | ||
| 351 | let fmiVersion = '3.0' | ||
| 352 | let modelName = dotPath(modelInfo.name) | ||
| 353 | let description = modelInfo.description | ||
| 354 | let author = modelInfo.author | ||
| 355 | let version = modelInfo.version | ||
| 356 | let copyright = modelInfo.copyright | ||
| 357 | let license = modelInfo.license | ||
| 358 | let generationTool= 'OpenModelica Compiler <%getVersionNr()%>' | ||
| 359 | let generationDateAndTime = xsdateTime(Util.getCurrentDateTime()) | ||
| 360 | let variableNamingConvention = 'structured' | ||
| 361 | let numberOfEventIndicators = getNumberOfEventIndicators(simCode) | ||
| 362 | << | ||
| 363 | fmiVersion="<%fmiVersion%>" | ||
| 364 | modelName="<%Util.escapeModelicaStringToXmlString(modelName)%>" | ||
| 365 | instantiationToken="{<%guid%>}" | ||
| 366 | description="<%Util.escapeModelicaStringToXmlString(description)%>" | ||
| 367 | version="<%Util.escapeModelicaStringToXmlString(version)%>" | ||
| 368 | <% if stringEq(author, "") then '' else 'author="<%Util.escapeModelicaStringToXmlString(author)%>"'%> | ||
| 369 | <% if stringEq(copyright, "") then '' else 'copyright="<%Util.escapeModelicaStringToXmlString(copyright)%>"'%> | ||
| 370 | <% if stringEq(license, "") then '' else 'license="<%Util.escapeModelicaStringToXmlString(license)%>"'%> | ||
| 371 | generationTool="<%Util.escapeModelicaStringToXmlString(generationTool)%>" | ||
| 372 | generationDateAndTime="<%Util.escapeModelicaStringToXmlString(generationDateAndTime)%>" | ||
| 373 | variableNamingConvention="<%variableNamingConvention%>" | ||
| 374 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 375 | xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/modelica/fmi-standard/main/schema/fmi3ModelDescription.xsd" | ||
| 376 | >> | ||
| 377 | end fmiModelDescriptionAttributes; | ||
| 378 | |||
| 379 | 14 | template ModelExchange3(SimCode simCode, list<String> sourceFiles) | |
| 380 | "Generates the ModelExchange element for FMI 3.0." | ||
| 381 | ::= | ||
| 382 | match simCode | ||
| 383 | case SIMCODE(__) then | ||
| 384 | let modelIdentifier = modelNamePrefix(simCode) | ||
| 385 | << | ||
| 386 | <ModelExchange | ||
| 387 | modelIdentifier="<%modelIdentifier%>" | ||
| 388 | needsExecutionTool="false" | ||
| 389 | canBeInstantiatedOnlyOncePerProcess="false" | ||
| 390 | canGetAndSetFMUState="true" | ||
| 391 | canSerializeFMUState="true" | ||
| 392 | <% if providesDirectionalDerivative(simCode) then 'providesDirectionalDerivatives="true"' else 'providesDirectionalDerivatives="false"'%> | ||
| 393 | providesAdjointDerivatives="false" | ||
| 394 | providesPerElementDependencies="false" | ||
| 395 | needsCompletedIntegratorStep="true"/> | ||
| 396 | >> | ||
| 397 | end ModelExchange3; | ||
| 398 | |||
| 399 | 2 | template CoSimulation3(SimCode simCode, list<String> sourceFiles) | |
| 400 | "Generates the CoSimulation element for FMI 3.0." | ||
| 401 | ::= | ||
| 402 | match simCode | ||
| 403 | case SIMCODE(__) then | ||
| 404 | let modelIdentifier = modelNamePrefix(simCode) | ||
| 405 | << | ||
| 406 | <CoSimulation | ||
| 407 | modelIdentifier="<%Util.escapeModelicaStringToXmlString(modelIdentifier)%>" | ||
| 408 | needsExecutionTool="false" | ||
| 409 | canHandleVariableCommunicationStepSize="true" | ||
| 410 | canBeInstantiatedOnlyOncePerProcess="false" | ||
| 411 | maxOutputDerivativeOrder="1" | ||
| 412 | providesIntermediateUpdate="false" | ||
| 413 | mightReturnEarlyFromDoStep="true" | ||
| 414 | canReturnEarlyAfterIntermediateUpdate="false" | ||
| 415 | hasEventMode="true" | ||
| 416 | providesEvaluateDiscreteStates="false" | ||
| 417 | recommendedIntermediateInputSmoothness="0" | ||
| 418 | canGetAndSetFMUState="true" | ||
| 419 | canSerializeFMUState="true" | ||
| 420 | <% if providesDirectionalDerivative(simCode) then 'providesDirectionalDerivatives="true"' else 'providesDirectionalDerivatives="false"'%> | ||
| 421 | providesAdjointDerivatives="false" | ||
| 422 | providesPerElementDependencies="false"/> | ||
| 423 | >> | ||
| 424 | end CoSimulation3; | ||
| 425 | |||
| 426 | ✗ | template ScheduledExecution3(SimCode simCode, list<String> sourceFiles) | |
| 427 | "Generates the ScheduledExecution element for FMI 3.0." | ||
| 428 | ::= | ||
| 429 | match simCode | ||
| 430 | case SIMCODE(__) then | ||
| 431 | let modelIdentifier = modelNamePrefix(simCode) | ||
| 432 | << | ||
| 433 | <ScheduledExecution | ||
| 434 | modelIdentifier="<%Util.escapeModelicaStringToXmlString(modelIdentifier)%>" | ||
| 435 | needsExecutionTool="false" | ||
| 436 | canBeInstantiatedOnlyOncePerProcess="false" | ||
| 437 | canGetAndSetFMUState="true" | ||
| 438 | canSerializeFMUState="true" | ||
| 439 | <% if providesDirectionalDerivative(simCode) then 'providesDirectionalDerivatives="true"' else 'providesDirectionalDerivatives="false"'%> | ||
| 440 | providesAdjointDerivatives="false" | ||
| 441 | providesPerElementDependencies="false"/> | ||
| 442 | >> | ||
| 443 | end ScheduledExecution3; | ||
| 444 | |||
| 445 | 16 | template DefaultExperiment3(Option<SimulationSettings> simulationSettingsOpt) | |
| 446 | "Generates the DefaultExperiment element for FMI 3.0." | ||
| 447 | ::= | ||
| 448 | match simulationSettingsOpt | ||
| 449 | case SOME(SIMULATION_SETTINGS(startTime=startTime, stopTime=stopTime, tolerance=tolerance, stepSize=stepSize)) then | ||
| 450 | << | ||
| 451 | <DefaultExperiment startTime="<%startTime%>" stopTime="<%stopTime%>" tolerance="<%tolerance%>" stepSize="<%stepSize%>"/> | ||
| 452 | >> | ||
| 453 | end DefaultExperiment3; | ||
| 454 | |||
| 455 | 16 | template TypeDefinitions3(SimCode simCode) | |
| 456 | "Generates TypeDefinitions for FMI 3.0 (enumeration declared types)." | ||
| 457 | ::= | ||
| 458 | match simCode | ||
| 459 | case SIMCODE(modelInfo=modelInfo) then | ||
| 460 | match modelInfo | ||
| 461 | case MODELINFO(vars=SIMVARS(__)) then | ||
| 462 | let types = (SimCodeCodegenUtil.getEnumerationTypes(vars) |> var => TypeDefinition3(var) ;separator="\n") | ||
| 463 | if boolNot(stringEq(types, "")) then | ||
| 464 | << | ||
| 465 | <TypeDefinitions> | ||
| 466 | <%types%> | ||
| 467 | </TypeDefinitions> | ||
| 468 | >> | ||
| 469 | end TypeDefinitions3; | ||
| 470 | |||
| 471 | 4 | template TypeDefinition3(SimVar simVar) | |
| 472 | ::= | ||
| 473 | match simVar | ||
| 474 | case SIMVAR(type_ = T_ENUMERATION(path=path, names=names)) then | ||
| 475 | << | ||
| 476 | <EnumerationType name="<%AbsynUtil.pathString(path, ".", false, false)%>" quantity="<%AbsynUtil.pathString(path, ".", false, false)%>"> | ||
| 477 | <%names |> name hasindex i0 fromindex 1 => '<Item name="<%name%>" value="<%i0%>"/>' ;separator="\n"%> | ||
| 478 | </EnumerationType> | ||
| 479 | >> | ||
| 480 | end TypeDefinition3; | ||
| 481 | |||
| 482 | 16 | template fmiModelVariables3(SimCode simCode, String FMUType) | |
| 483 | "Generates the ModelVariables element for FMI 3.0. FMUType selects the clock | ||
| 484 | causality: Scheduled Execution clocks are input clocks (the importer activates | ||
| 485 | the model partition), all others are output clocks." | ||
| 486 | ::= | ||
| 487 | match simCode | ||
| 488 | case SIMCODE(modelInfo=modelInfo) then | ||
| 489 | match modelInfo | ||
| 490 | case MODELINFO(vars=SIMVARS(stateVars=stateVars)) then | ||
| 491 | let numScalarStates = SimCodeCodegenUtil.numScalarElems(stateVars) | ||
| 492 | << | ||
| 493 | <ModelVariables> | ||
| 494 | <%TimeVariable3(simCode)%> | ||
| 495 | <%vars.stateVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 496 | <%vars.derivativeVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 497 | <%vars.algVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 498 | <%vars.discreteAlgVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 499 | <%vars.paramVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 500 | <%vars.aliasVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 501 | <%vars.intAlgVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 502 | <%vars.intParamVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 503 | <%vars.intAliasVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 504 | <%vars.boolAlgVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 505 | <%vars.boolParamVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 506 | <%vars.boolAliasVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 507 | <%vars.stringAlgVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 508 | <%vars.stringParamVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 509 | <%vars.stringAliasVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 510 | <%vars.extObjVars |> var => Variable3(var, simCode, numScalarStates) ;separator="\n"%> | ||
| 511 | <%SimCodeCodegenUtil.getFMI3Clocks(simCode) |> clk => Clock3(clk, FMUType) ;separator="\n"%> | ||
| 512 | <%EventIndicatorVariables3(simCode)%> | ||
| 513 | <%if isFMIMEType(FMUType) then DaeModeVariables3(simCode)%> | ||
| 514 | </ModelVariables> | ||
| 515 | >> | ||
| 516 | end fmiModelVariables3; | ||
| 517 | |||
| 518 | ✗ | template DaeModeVariables3(SimCode simCode) | |
| 519 | "fmi-ls-dae, for a Model Exchange FMU of a --daeMode model: the structural | ||
| 520 | parameter that switches it into DAE mode (false: an ordinary ODE FMU), and one | ||
| 521 | Float64 per residual of the DAE formulation. Their value references follow the | ||
| 522 | event indicators; the manifest (fmiLsDaeManifest) names them." | ||
| 523 | ::= | ||
| 524 | match simCode | ||
| 525 | case SIMCODE(daeModeData=SOME(dmd as DAEMODEDATA(__))) then | ||
| 526 | let daeModeVR = SimCodeCodegenUtil.getFMI3DaeModeValueReference(simCode) | ||
| 527 | << | ||
| 528 | <Boolean name="_D_daeMode" valueReference="<%daeModeVR%>" causality="structuralParameter" variability="fixed" start="false" description="Set to true to enable DAE mode as defined by FMI-LS-DAE."/> | ||
| 529 | <%dmd.residualVars |> var => DaeResidualVariable3(var, daeModeVR) ;separator="\n"%> | ||
| 530 | >> | ||
| 531 | end DaeModeVariables3; | ||
| 532 | |||
| 533 | ✗ | template DaeResidualVariable3(SimVar simVar, String daeModeVR) | |
| 534 | ::= | ||
| 535 | match simVar | ||
| 536 | case SIMVAR(__) then | ||
| 537 | let nm = Util.escapeModelicaStringToXmlString(System.stringReplace(crefStrNoUnderscore(name),"$", "_D_")) | ||
| 538 | '<Float64 name="<%nm%>" valueReference="<%intAdd(stringInt(daeModeVR), intAdd(1, index))%>" causality="local" variability="continuous" initial="calculated" description="DAE-mode residual <%index%>"/>' | ||
| 539 | end DaeResidualVariable3; | ||
| 540 | |||
| 541 | ✗ | template fmiLsDaeManifest(SimCode simCode) | |
| 542 | "fmi-ls-dae's manifest (extra/org.fmi-standard.fmi-ls-dae/fmi-ls-manifest.xml) | ||
| 543 | for a --daeMode Model Exchange FMU: the switch, the algebraic variables the | ||
| 544 | importer solves for beside the states, and a ModelStructure that restates the | ||
| 545 | model description's and adds the residuals — the fully implicit form | ||
| 546 | F(der(x), x, a, t) = 0, so no ContinuousStateDerivative entries." | ||
| 547 | ::= | ||
| 548 | match simCode | ||
| 549 | case SIMCODE(daeModeData=SOME(dmd as DAEMODEDATA(__)), modelStructure=modelStructure) then | ||
| 550 | let _ = SimCodeCodegenUtil.cacheFMI3ValueReferences(simCode) | ||
| 551 | let structure = match modelStructure | ||
| 552 | case SOME(fmistruct as FMIMODELSTRUCTURE(__)) then | ||
| 553 | << | ||
| 554 | <%ModelStructureOutputs3(simCode, fmistruct.fmiOutputs)%> | ||
| 555 | <%ModelStructureInitialUnknowns3(simCode, fmistruct.fmiInitialUnknowns)%> | ||
| 556 | >> | ||
| 557 | else '' | ||
| 558 | let indicators = EventIndicators3(simCode) | ||
| 559 | let residuals = (SimCodeCodegenUtil.fmi3DaeResiduals(simCode) |> (vr, dependencyAttributes) => DaeResidual3(vr, dependencyAttributes) ;separator="\n") | ||
| 560 | let _ = SimCodeCodegenUtil.clearFMI3ValueReferences() | ||
| 561 | << | ||
| 562 | <fmiDAEManifest | ||
| 563 | xmlns:fmi-ls="http://fmi-standard.org/fmi-ls-manifest" | ||
| 564 | fmi-ls:fmi-ls-name="org.fmi-standard.fmi-ls-dae" | ||
| 565 | fmi-ls:fmi-ls-version="<%SimCodeCodegenUtil.fmiLsDaeVersion()%>" | ||
| 566 | fmi-ls:fmi-ls-description="Layered standard for DAE support in FMI."> | ||
| 567 | <EnableDAEParameter valueReference="<%SimCodeCodegenUtil.getFMI3DaeModeValueReference(simCode)%>"/> | ||
| 568 | <AlgebraicVariables> | ||
| 569 | <%dmd.algebraicVars |> var => '<AlgebraicVariable valueReference="<%SimCodeCodegenUtil.getFMI3ValueReference(var, simCode)%>"/>' ;separator="\n"%> | ||
| 570 | </AlgebraicVariables> | ||
| 571 | <ModelStructure> | ||
| 572 | <%structure%> | ||
| 573 | <%indicators%> | ||
| 574 | <%residuals%> | ||
| 575 | </ModelStructure> | ||
| 576 | </fmiDAEManifest> | ||
| 577 | >> | ||
| 578 | end fmiLsDaeManifest; | ||
| 579 | |||
| 580 | ✗ | template DaeResidual3(String vr, String dependencyAttributes) | |
| 581 | ::= | ||
| 582 | '<Residual valueReference="<%vr%>"<%dependencyAttributes%>/>' | ||
| 583 | end DaeResidual3; | ||
| 584 | |||
| 585 | 16 | template EventIndicatorVariables3(SimCode simCode) | |
| 586 | "FMI 3.0 requires event indicators to be exposed as Float64 variables that are | ||
| 587 | then referenced by valueReference from the ModelStructure EventIndicator list. | ||
| 588 | OpenModelica does not expose event indicators as named Modelica variables, so | ||
| 589 | we declare synthetic local Float64 variables for them. Their value references | ||
| 590 | form a contiguous block starting right after the time value reference | ||
| 591 | (FMI3_EVENT_INDICATOR_VR_START in the generated runtime), and the runtime | ||
| 592 | returns their values from the event indicators array in fmi3GetFloat64." | ||
| 593 | ::= | ||
| 594 | let n = getNumberOfEventIndicators(simCode) | ||
| 595 | let timeVR = SimCodeCodegenUtil.getFMI3TimeValueReference(simCode) | ||
| 596 | if intGt(stringInt(n), 0) then | ||
| 597 | (List.intRange(stringInt(n)) |> i => | ||
| 598 | '<Float64 name="__zc_<%intSub(i,1)%>" valueReference="<%intAdd(stringInt(timeVR), i)%>" causality="local" variability="continuous" initial="calculated" description="event indicator <%intSub(i,1)%>"/>' ;separator="\n") | ||
| 599 | end EventIndicatorVariables3; | ||
| 600 | |||
| 601 | 16 | template TimeVariable3(SimCode simCode) | |
| 602 | "Generates the mandatory independent variable (time) for FMI 3.0." | ||
| 603 | ::= | ||
| 604 | << | ||
| 605 | <Float64 name="time" valueReference="<%SimCodeCodegenUtil.getFMI3TimeValueReference(simCode)%>" causality="independent" variability="continuous" description="Simulation time"/> | ||
| 606 | >> | ||
| 607 | end TimeVariable3; | ||
| 608 | |||
| 609 | 3134 | template Variable3(SimVar simVar, SimCode simCode, String numScalarStates) | |
| 610 | "Generates a typed variable element for FMI 3.0." | ||
| 611 | ::= | ||
| 612 | match simVar | ||
| 613 | case SIMVAR(name = name, type_ = T_COMPLEX(complexClassType = ClassInf.EXTERNAL_OBJ(__))) then | ||
| 614 | // FMI 3.0 Binary variable: a Modelica ExternalObject is an opaque handle | ||
| 615 | // (void*); it is exported as a Binary whose value is the raw handle bytes (see | ||
| 616 | // fmi3GetBinary/fmi3SetBinary in fmu3_model_interface.c). | ||
| 617 | '<Binary <%BinaryVariableAttributes3(simVar, simCode)%>/>' | ||
| 618 | case SIMVAR(name = name, exportVar = exportVar, type_ = T_ARRAY(ty = arrayElementType)) then | ||
| 619 | // FMI 3.0 native array variable: one element with one valueReference and | ||
| 620 | // <Dimension> children (the value reference of the first scalar element; the | ||
| 621 | // array occupies a contiguous block of scalar value references). | ||
| 622 | if boolNot(isSome(exportVar)) then '' | ||
| 623 | else | ||
| 624 | match arrayElementType | ||
| 625 | case T_REAL(__) then | ||
| 626 | '<Float64 <%VariableCommonAttributes3(simVar, simCode)%><%DerivativeAttribute3(simVar, simCode, numScalarStates)%><%ArrayStartString3(simVar)%><%MinString2(simVar)%><%MaxString2(simVar)%><%NominalString2(simVar)%><%UnitString3(simVar, simCode)%><%relativeQuantity(simVar)%>><%Dimensions3(simVar)%></Float64>' | ||
| 627 | case T_INTEGER(__) then | ||
| 628 | '<Int32 <%VariableCommonAttributes3(simVar, simCode)%><%ArrayStartString3(simVar)%><%MinString2(simVar)%><%MaxString2(simVar)%>><%Dimensions3(simVar)%></Int32>' | ||
| 629 | case T_BOOL(__) then | ||
| 630 | '<Boolean <%VariableCommonAttributes3(simVar, simCode)%><%ArrayStartString3(simVar)%>><%Dimensions3(simVar)%></Boolean>' | ||
| 631 | case T_STRING(__) then | ||
| 632 | '<String <%VariableCommonAttributes3(simVar, simCode)%>><%Dimensions3(simVar)%></String>' | ||
| 633 | case T_ENUMERATION(path = path) then | ||
| 634 | '<Enumeration <%VariableCommonAttributes3(simVar, simCode)%>declaredType="<%AbsynUtil.pathString(path, ".", false, false)%>"<%ArrayStartString3(simVar)%>><%Dimensions3(simVar)%></Enumeration>' | ||
| 635 | else '<!-- UNKNOWN_ARRAY_TYPE <%CodegenUtil.crefStr(name)%> -->' | ||
| 636 | case SIMVAR(__) then | ||
| 637 | if SimCodeCodegenUtil.isFMI3NestableAlias(simVar) then | ||
| 638 | // emitted as an <Alias> child of its canonical variable (shares its | ||
| 639 | // valueReference), not as a separate ModelVariables entry | ||
| 640 | '' | ||
| 641 | else if stringEq(CodegenUtil.crefStr(name),"$dummy") then | ||
| 642 | <<>> | ||
| 643 | else if stringEq(CodegenUtil.crefStr(name),"der($dummy)") then | ||
| 644 | <<>> | ||
| 645 | else if boolNot(isSome(exportVar)) then | ||
| 646 | '' | ||
| 647 | else | ||
| 648 | match type_ | ||
| 649 | case T_REAL(__) then | ||
| 650 | '<Float64 <%VariableCommonAttributes3(simVar, simCode)%><%DerivativeAttribute3(simVar, simCode, numScalarStates)%><%StartString2(simVar)%><%MinString2(simVar)%><%MaxString2(simVar)%><%NominalString2(simVar)%><%UnitString3(simVar, simCode)%><%relativeQuantity(simVar)%><%CloseWithAliases3("Float64", simVar, simCode)%>' | ||
| 651 | case T_INTEGER(__) then | ||
| 652 | '<Int32 <%VariableCommonAttributes3(simVar, simCode)%><%StartString2(simVar)%><%MinString2(simVar)%><%MaxString2(simVar)%><%CloseWithAliases3("Int32", simVar, simCode)%>' | ||
| 653 | case T_BOOL(__) then | ||
| 654 | '<Boolean <%VariableCommonAttributes3(simVar, simCode)%><%StartString2(simVar)%><%CloseWithAliases3("Boolean", simVar, simCode)%>' | ||
| 655 | case T_STRING(__) then | ||
| 656 | '<String <%VariableCommonAttributes3(simVar, simCode)%>><%StringStartChild3(simVar)%><%AliasElements3(simVar, simCode)%></String>' | ||
| 657 | case T_ENUMERATION(path=path) then | ||
| 658 | '<Enumeration <%VariableCommonAttributes3(simVar, simCode)%>declaredType="<%AbsynUtil.pathString(path, ".", false, false)%>"<%StartString2(simVar)%><%MinString2(simVar)%><%MaxString2(simVar)%><%CloseWithAliases3("Enumeration", simVar, simCode)%>' | ||
| 659 | else '<!-- UNKNOWN_TYPE <%CodegenUtil.crefStr(name)%> -->' | ||
| 660 | end Variable3; | ||
| 661 | |||
| 662 | 997 | template CloseWithAliases3(String tag, SimVar simVar, SimCode simCode) | |
| 663 | "Close a typed FMI 3.0 variable element: self-closing when it has no FMI 3.0 | ||
| 664 | <Alias> members, otherwise an open/close pair wrapping the <Alias> children." | ||
| 665 | ::= | ||
| 666 | let kids = AliasElements3(simVar, simCode) | ||
| 667 | if stringEq(kids, "") then '/>' | ||
| 668 | else '><%\n%><%kids%><%\n%> </<%tag%>>' | ||
| 669 | end CloseWithAliases3; | ||
| 670 | |||
| 671 | 1009 | template AliasElements3(SimVar simVar, SimCode simCode) | |
| 672 | "Emit the FMI 3.0 <Alias> child elements of a canonical variable: one per | ||
| 673 | positive local alias that shares this variable's valueReference." | ||
| 674 | ::= | ||
| 675 | match simVar | ||
| 676 | case SIMVAR(__) then | ||
| 677 | match SimCodeCodegenUtil.getFMI3VariableAliases(simCode, simVar) | ||
| 678 | case {} then '' | ||
| 679 | case aliases then (aliases |> a => AliasElement3(a) ;separator="\n") | ||
| 680 | end AliasElements3; | ||
| 681 | |||
| 682 | 802 | template AliasElement3(SimVar aliasVar) | |
| 683 | "One FMI 3.0 <Alias> element. The name is formatted exactly like a variable name | ||
| 684 | in modelDescription.xml; the alias shares the parent variable's valueReference." | ||
| 685 | ::= | ||
| 686 | match aliasVar | ||
| 687 | case SIMVAR(__) then | ||
| 688 | let nm = Util.escapeModelicaStringToXmlString(System.stringReplace(crefStrNoUnderscore(Util.getOption(exportVar)),"$", "_D_")) | ||
| 689 | let desc = if comment then ' description="<%Util.escapeModelicaStringToXmlString(comment)%>"' | ||
| 690 | ' <Alias name="<%nm%>"<%desc%>/>' | ||
| 691 | end AliasElement3; | ||
| 692 | |||
| 693 | 1 | template Clock3(FmiClock clock, String FMUType) | |
| 694 | "Generates an FMI 3.0 <Clock> variable for a model clock. For Scheduled | ||
| 695 | Execution the clock is an input clock (the importer activates the associated | ||
| 696 | model partition via fmi3ActivateModelPartition); otherwise it is an output | ||
| 697 | clock the FMU activates. intervalVariability is required; the period | ||
| 698 | (intervalDecimal) or the counter/resolution fraction is emitted when known." | ||
| 699 | ::= | ||
| 700 | match clock | ||
| 701 | case FMI_CLOCK(__) then | ||
| 702 | let causality = if isFMISEType(FMUType) then "input" else "output" | ||
| 703 | let nm = Util.escapeModelicaStringToXmlString(System.stringReplace(name,"$", "_D_")) | ||
| 704 | let intervalDec = if boolNot(stringEq(intervalDecimal, "")) then ' intervalDecimal="<%intervalDecimal%>"' | ||
| 705 | let fraction = if supportsFraction then ' supportsFraction="true"' | ||
| 706 | let counter = if boolNot(stringEq(intervalCounter, "")) then ' intervalCounter="<%intervalCounter%>"' | ||
| 707 | let res = if boolNot(stringEq(resolution, "")) then ' resolution="<%resolution%>"' | ||
| 708 | '<Clock name="<%nm%>" valueReference="<%valueReference%>" causality="<%causality%>" intervalVariability="<%intervalVariability%>"<%intervalDec%><%fraction%><%counter%><%res%>/>' | ||
| 709 | end Clock3; | ||
| 710 | |||
| 711 | 52 | template ArrayStartString3(SimVar simVar) | |
| 712 | "Generates the start attribute (a space separated list of the scalar element | ||
| 713 | start values) for an FMI 3.0 array variable. Emitted only where a scalar would | ||
| 714 | emit a start (exact/approx initial or input), to keep the schema consistent." | ||
| 715 | ::= | ||
| 716 | match simVar | ||
| 717 | case SIMVAR(aliasvar = SimCodeVar.ALIAS(__)) then '' | ||
| 718 | case SIMVAR(varKind = STATE(__)) then arrayStartAttr(simVar) | ||
| 719 | case SIMVAR(initial_ = SOME(SimCodeVar.EXACT())) then arrayStartAttr(simVar) | ||
| 720 | case SIMVAR(initial_ = SOME(SimCodeVar.APPROX())) then arrayStartAttr(simVar) | ||
| 721 | case SIMVAR(causality = SOME(SimCodeVar.INPUT())) then arrayStartAttr(simVar) | ||
| 722 | else '' | ||
| 723 | end ArrayStartString3; | ||
| 724 | |||
| 725 | 55 | template arrayStartAttr(SimVar simVar) | |
| 726 | ::= | ||
| 727 | let s = SimCodeCodegenUtil.getFMI3ArrayStart(simVar) | ||
| 728 | if stringEq(s, "") then '' else ' start="<%s%>"' | ||
| 729 | end arrayStartAttr; | ||
| 730 | |||
| 731 | 216 | template Dimensions3(SimVar simVar) | |
| 732 | "Generates the <Dimension start='N'/> children of an FMI 3.0 array variable | ||
| 733 | from the variable's dimension sizes (row major)." | ||
| 734 | ::= | ||
| 735 | match simVar | ||
| 736 | case SIMVAR(numArrayElement = dims) then | ||
| 737 | (dims |> d => '<Dimension start="<%d%>"/>' ;separator="") | ||
| 738 | end Dimensions3; | ||
| 739 | |||
| 740 | 1198 | template FmiInitialAttribute3(SimVar simVar) | |
| 741 | "Generates the FMI 3.0 initial attribute. Continuous-time states are special: | ||
| 742 | the non-scalarized array backend does not populate their initial_ field, so we | ||
| 743 | derive it from the variable's fixed attribute here. A fixed start is an exact | ||
| 744 | initial value (initial=\"exact\"), an unfixed start is a guess (initial=\"approx\"). | ||
| 745 | All other variables fall back to the generic SimCode-derived attribute." | ||
| 746 | ::= | ||
| 747 | match simVar | ||
| 748 | case SIMVAR(varKind = STATE(__), isFixed = true) then "exact" | ||
| 749 | case SIMVAR(varKind = STATE(__)) then "approx" | ||
| 750 | else getFmiInitialAttributeStr(simVar) | ||
| 751 | end FmiInitialAttribute3; | ||
| 752 | |||
| 753 | 1225 | template VariableCommonAttributes3(SimVar simVar, SimCode simCode) | |
| 754 | "Generates the common attributes (name, valueReference, description, causality, | ||
| 755 | variability, initial) shared by all FMI 3.0 typed variable elements. Note the | ||
| 756 | trailing space so type-specific attributes can be appended directly." | ||
| 757 | ::= | ||
| 758 | match simVar | ||
| 759 | case SIMVAR(__) then | ||
| 760 | let name = Util.escapeModelicaStringToXmlString(System.stringReplace(crefStrNoUnderscore(Util.getOption(exportVar)),"$", "_D_")) | ||
| 761 | let valueReference = SimCodeCodegenUtil.getFMI3ValueReference(simVar, simCode) | ||
| 762 | let description = if comment then 'description="<%Util.escapeModelicaStringToXmlString(comment)%>" ' | ||
| 763 | let variability_ = getVariability2(variability) | ||
| 764 | let caus = getCausality2(causality) | ||
| 765 | let initial = FmiInitialAttribute3(simVar) | ||
| 766 | << | ||
| 767 | name="<%name%>" valueReference="<%valueReference%>" <%description%><%if boolNot(stringEq(caus, "")) then 'causality="'+caus+'" '%><%if boolNot(stringEq(variability_, "")) then 'variability="'+variability_+'" '%><%if boolNot(stringEq(initial, "")) then 'initial="'+initial+'" '%> | ||
| 768 | >> | ||
| 769 | end VariableCommonAttributes3; | ||
| 770 | |||
| 771 | 1 | template BinaryVariableAttributes3(SimVar simVar, SimCode simCode) | |
| 772 | "Generates the attributes for an FMI 3.0 Binary variable (a Modelica | ||
| 773 | ExternalObject). External objects have no exportVar/start; the name is taken | ||
| 774 | directly from the SimVar cref. They are constructed during initialization and | ||
| 775 | constant afterwards, hence variability=\"fixed\", causality=\"local\"." | ||
| 776 | ::= | ||
| 777 | match simVar | ||
| 778 | case SIMVAR(__) then | ||
| 779 | let nm = Util.escapeModelicaStringToXmlString(System.stringReplace(crefStrNoUnderscore(name),"$", "_D_")) | ||
| 780 | let valueReference = SimCodeCodegenUtil.getFMI3ValueReference(simVar, simCode) | ||
| 781 | let description = if comment then 'description="<%Util.escapeModelicaStringToXmlString(comment)%>" ' | ||
| 782 | << | ||
| 783 | name="<%nm%>" valueReference="<%valueReference%>" <%description%>causality="local" variability="fixed" | ||
| 784 | >> | ||
| 785 | end BinaryVariableAttributes3; | ||
| 786 | |||
| 787 | 27 | template DerivativeAttribute3(SimVar simVar, SimCode simCode, String numScalarStates) | |
| 788 | "Generates the derivative attribute (FMI 3.0 references the *valueReference* of | ||
| 789 | the state variable). For the C runtime the state value references precede the | ||
| 790 | state derivative value references contiguously in the real block, hence the | ||
| 791 | state value reference is the derivative value reference minus the number of | ||
| 792 | states. Scalar elements, not list entries: the new backend leaves a | ||
| 793 | non-scalarized array state as one entry (and one `varInfo.numStateVars`)." | ||
| 794 | ::= | ||
| 795 | match simVar | ||
| 796 | case SIMVAR(varKind = STATE_DER(__)) then | ||
| 797 | ' derivative="<%intSub(stringInt(SimCodeCodegenUtil.getFMI3ValueReference(simVar, simCode)), stringInt(numScalarStates))%>"' | ||
| 798 | else '' | ||
| 799 | end DerivativeAttribute3; | ||
| 800 | |||
| 801 | 12 | template StringStartChild3(SimVar simVar) | |
| 802 | "Generates the nested <Start value=.../> element for an FMI 3.0 String variable." | ||
| 803 | ::= | ||
| 804 | match simVar | ||
| 805 | case SIMVAR(aliasvar = SimCodeVar.ALIAS(__)) then '' | ||
| 806 | case SIMVAR(initialValue = SOME(e as SCONST(__))) then | ||
| 807 | '<Start value="<%initValXml(e, "")%>"/>' | ||
| 808 | case SIMVAR(causality = SOME(SimCodeVar.INPUT())) then | ||
| 809 | '<Start value=""/>' | ||
| 810 | else '' | ||
| 811 | end StringStartChild3; | ||
| 812 | |||
| 813 | 16 | template modelStructure3(SimCode simCode, Option<FmiModelStructure> fmiModelStructure) | |
| 814 | "Generates the FMI 3.0 ModelStructure. Unknowns are referenced by valueReference." | ||
| 815 | ::= | ||
| 816 | match fmiModelStructure | ||
| 817 | case SOME(fmistruct as FMIMODELSTRUCTURE(__)) then | ||
| 818 | // Every entry below looks its index up; without the table each lookup searches | ||
| 819 | // every variable the model has. | ||
| 820 | let _ = SimCodeCodegenUtil.cacheFMI3ValueReferences(simCode) | ||
| 821 | let outputs = ModelStructureOutputs3(simCode, fmistruct.fmiOutputs) | ||
| 822 | let derivatives = ModelStructureDerivatives3(simCode, fmistruct.fmiDerivatives) | ||
| 823 | let initials = ModelStructureInitialUnknowns3(simCode, fmistruct.fmiInitialUnknowns) | ||
| 824 | let indicators = EventIndicators3(simCode) | ||
| 825 | let _ = SimCodeCodegenUtil.clearFMI3ValueReferences() | ||
| 826 | << | ||
| 827 | <ModelStructure> | ||
| 828 | <%outputs%> | ||
| 829 | <%derivatives%> | ||
| 830 | <%initials%> | ||
| 831 | <%indicators%> | ||
| 832 | </ModelStructure> | ||
| 833 | >> | ||
| 834 | else | ||
| 835 | << | ||
| 836 | <ModelStructure> | ||
| 837 | <%EventIndicators3(simCode)%> | ||
| 838 | </ModelStructure> | ||
| 839 | >> | ||
| 840 | end modelStructure3; | ||
| 841 | |||
| 842 | 16 | template ModelStructureOutputs3(SimCode simCode, FmiOutputs fmiOutputs) | |
| 843 | ::= | ||
| 844 | match fmiOutputs | ||
| 845 | case FMIOUTPUTS(fmiUnknownsList={}) then "" | ||
| 846 | case FMIOUTPUTS(__) then | ||
| 847 | (fmiUnknownsList |> u => FmiUnknown3(simCode, u, "Output") ;separator="\n") | ||
| 848 | end ModelStructureOutputs3; | ||
| 849 | |||
| 850 | 16 | template ModelStructureDerivatives3(SimCode simCode, FmiDerivatives fmiDerivatives) | |
| 851 | ::= | ||
| 852 | match fmiDerivatives | ||
| 853 | case FMIDERIVATIVES(fmiUnknownsList={}) then "" | ||
| 854 | case FMIDERIVATIVES(__) then | ||
| 855 | (fmiUnknownsList |> u => FmiUnknown3(simCode, u, "ContinuousStateDerivative") ;separator="\n") | ||
| 856 | end ModelStructureDerivatives3; | ||
| 857 | |||
| 858 | 16 | template ModelStructureInitialUnknowns3(SimCode simCode, FmiInitialUnknowns fmiInitialUnknowns) | |
| 859 | ::= | ||
| 860 | match fmiInitialUnknowns | ||
| 861 | case FMIINITIALUNKNOWNS(fmiUnknownsList={}) then "" | ||
| 862 | case FMIINITIALUNKNOWNS(__) then | ||
| 863 | (fmiUnknownsList |> u => FmiUnknown3(simCode, u, "InitialUnknown") ;separator="\n") | ||
| 864 | end ModelStructureInitialUnknowns3; | ||
| 865 | |||
| 866 | 510 | template FmiUnknown3(SimCode simCode, FmiUnknown fmiUnknown, String element) | |
| 867 | "Generates a single FMI 3.0 ModelStructure entry, mapping the stored FMI index | ||
| 868 | to its value reference. Dependencies (also stored as indices) are mapped to | ||
| 869 | the value references of the corresponding knowns." | ||
| 870 | ::= | ||
| 871 | match fmiUnknown | ||
| 872 | case FMIUNKNOWN(__) then | ||
| 873 | << | ||
| 874 | <<%element%> valueReference="<%SimCodeCodegenUtil.getFMI3ValueReferenceFromFMIIndex(simCode, index)%>"<%SimCodeCodegenUtil.fmi3UnknownDependencyAttributes(simCode, fmiUnknown)%>/> | ||
| 875 | >> | ||
| 876 | end FmiUnknown3; | ||
| 877 | |||
| 878 | 16 | template EventIndicators3(SimCode simCode) | |
| 879 | "Generates the EventIndicator entries for FMI 3.0. The event indicators have | ||
| 880 | their own value reference range starting at FMI3_EVENT_INDICATOR_VR_START in | ||
| 881 | the generated runtime; here we only need them to be unique within the FMU." | ||
| 882 | ::= | ||
| 883 | let n = getNumberOfEventIndicators(simCode) | ||
| 884 | let timeVR = SimCodeCodegenUtil.getFMI3TimeValueReference(simCode) | ||
| 885 | if intGt(stringInt(n), 0) then | ||
| 886 | (List.intRange(stringInt(n)) |> i => | ||
| 887 | '<EventIndicator valueReference="<%intAdd(stringInt(timeVR), i)%>"/>' ;separator="\n") | ||
| 888 | end EventIndicators3; | ||
| 889 | |||
| 890 | annotation(__OpenModelica_Interface="codegen_fmu"); | ||
| 891 | end CodegenFMU3; | ||
| 892 | |||
| 893 | // vim: filetype=susan sw=2 sts=2 | ||
| 894 |