OMCompiler/Compiler/SimCode/SimCodeMain.mo
| 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 | encapsulated package SimCodeMain | ||
| 37 | " file: SimCodeMain.mo | ||
| 38 | package: SimCodeMain | ||
| 39 | description: Code generation using Susan templates | ||
| 40 | |||
| 41 | The entry points to this module are the translateModel function and the | ||
| 42 | translateFunctions function." | ||
| 43 | |||
| 44 | public | ||
| 45 | import Absyn; | ||
| 46 | import ProgramUtil; | ||
| 47 | import BackendDAE; | ||
| 48 | import BackendDAEUtil; | ||
| 49 | import Ceval; | ||
| 50 | import DAE; | ||
| 51 | import FCore; | ||
| 52 | import UnorderedMap; | ||
| 53 | import Tpl; | ||
| 54 | import Values; | ||
| 55 | import SimCode; | ||
| 56 | import SimCodeFunction; | ||
| 57 | import NSimCode; /* used for new backend */ | ||
| 58 | |||
| 59 | protected | ||
| 60 | import AbsynUtil; | ||
| 61 | import Array; | ||
| 62 | import Autoconf; | ||
| 63 | import AvlSetString; | ||
| 64 | import BackendDAECreate; | ||
| 65 | import BackendDump; | ||
| 66 | import NBackendDAE; | ||
| 67 | import BackendVariable; | ||
| 68 | import Builtin; | ||
| 69 | import ClockIndexes; | ||
| 70 | import CevalScriptBackend; | ||
| 71 | import CodegenC; | ||
| 72 | import CodegenEmbeddedC; | ||
| 73 | import CodegenESP32; | ||
| 74 | import CodegenFMU; | ||
| 75 | import CodegenFMU2; | ||
| 76 | import CodegenFMU3; | ||
| 77 | import CodegenFMUCommon; | ||
| 78 | import CodegenFMUCpp; | ||
| 79 | import CodegenFMUCppHpcom; | ||
| 80 | import CodegenCpp; | ||
| 81 | import CodegenCppHpcom; | ||
| 82 | import CodegenXML; | ||
| 83 | import CodegenJS; | ||
| 84 | import CodegenWasmJit; | ||
| 85 | import Config; | ||
| 86 | import DAEMode; | ||
| 87 | import DAEUtil; | ||
| 88 | import Debug; | ||
| 89 | import Error; | ||
| 90 | import ErrorExt; | ||
| 91 | import ExecStat; | ||
| 92 | import FGraph; | ||
| 93 | import Flags; | ||
| 94 | import FlagsUtil; | ||
| 95 | import FlatModel = NFFlatModel; | ||
| 96 | import FindZeroCrossings; | ||
| 97 | import NFFunction; | ||
| 98 | import NFFlatten.{FunctionTree, FunctionTreeImpl}; | ||
| 99 | import NFApi; | ||
| 100 | import FMI; | ||
| 101 | import GCExt; | ||
| 102 | import HashTable; | ||
| 103 | import HashTableCrefSimVar; | ||
| 104 | import HashTableCrIListArray; | ||
| 105 | import HashTableCrILst; | ||
| 106 | import HpcOmSimCodeMain; | ||
| 107 | import HpcOmTaskGraph; | ||
| 108 | import NFConvertDAE; | ||
| 109 | import RuntimeSources; | ||
| 110 | import SemanticVersion; | ||
| 111 | import SerializeInitXML; | ||
| 112 | import SerializeModelInfo; | ||
| 113 | import SerializeSparsityPattern; | ||
| 114 | import SimCodeUtil; | ||
| 115 | import SimCodeFunctionUtil; | ||
| 116 | import StateMachineFlatten; | ||
| 117 | import SimCodeUtilShared; | ||
| 118 | import SimCodeVar; | ||
| 119 | import StackOverflow; | ||
| 120 | import StringUtil; | ||
| 121 | import SymbolicJacobian; | ||
| 122 | import SymbolTable; | ||
| 123 | import System; | ||
| 124 | import Testsuite; | ||
| 125 | import Util; | ||
| 126 | import SerializeTaskSystemInfo; | ||
| 127 | import File; | ||
| 128 | import SimCodeCodegenUtil; | ||
| 129 | |||
| 130 | public | ||
| 131 | uniontype FmuTranslation | ||
| 132 | "The FMU-grade translation translateModelFMU left behind, which the | ||
| 133 | buildModelFMU that follows exports instead of translating the model again." | ||
| 134 | record FMU_TRANSLATION | ||
| 135 | SimCode.SimCode simCode; | ||
| 136 | String FMUVersion; | ||
| 137 | String kind "cs, or other: the preOptModules differ only for a pure Co-Simulation export"; | ||
| 138 | Absyn.Path className; | ||
| 139 | Absyn.Program ast "what it was translated from, by reference: any edit replaces the record"; | ||
| 140 | array<Boolean> debugFlags; | ||
| 141 | array<Flags.FlagData> configFlags; | ||
| 142 | end FMU_TRANSLATION; | ||
| 143 | end FmuTranslation; | ||
| 144 | |||
| 145 | public | ||
| 146 | uniontype TranslateModelKind | ||
| 147 | record NORMAL | ||
| 148 | end NORMAL; | ||
| 149 | record XML | ||
| 150 | end XML; | ||
| 151 | record FMU | ||
| 152 | String kind; | ||
| 153 | String targetName; | ||
| 154 | Boolean translateOnly "keep the translation in memory instead of writing the FMU (translateModelFMU)"; | ||
| 155 | end FMU; | ||
| 156 | end TranslateModelKind; | ||
| 157 | |||
| 158 | public function createSimulationSettings | ||
| 159 | input Real startTime; | ||
| 160 | input Real stopTime; | ||
| 161 | input Integer inumberOfIntervals; | ||
| 162 | input Real tolerance; | ||
| 163 | input String method; | ||
| 164 | input String options; | ||
| 165 | input String outputFormat; | ||
| 166 | input String variableFilter; | ||
| 167 | input String cflags; | ||
| 168 | input String simflags; | ||
| 169 | output SimCode.SimulationSettings simSettings; | ||
| 170 | protected | ||
| 171 | Real stepSize; | ||
| 172 | Integer numberOfIntervals; | ||
| 173 | algorithm | ||
| 174 | 2366 | numberOfIntervals := if inumberOfIntervals <= 0 then 1 else inumberOfIntervals; | |
| 175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2366 times.
|
2366 | stepSize := (stopTime - startTime) / intReal(numberOfIntervals); |
| 176 | 2366 | simSettings := SimCode.SIMULATION_SETTINGS( | |
| 177 | startTime, stopTime, numberOfIntervals, stepSize, tolerance, | ||
| 178 | method, options, outputFormat, variableFilter, cflags, simflags); | ||
| 179 | end createSimulationSettings; | ||
| 180 | |||
| 181 | |||
| 182 | protected function generateModelCodeFMU " | ||
| 183 | Generates code for a model by creating a SimCode structure and calling the | ||
| 184 | template-based code generator on it." | ||
| 185 | input BackendDAE.BackendDAE inBackendDAE; | ||
| 186 | input BackendDAE.BackendDAE inInitDAE; | ||
| 187 | input Option<BackendDAE.BackendDAE> inInitDAE_lambda0; | ||
| 188 | input BackendDAE.SymbolicJacobians inFMIDer; | ||
| 189 | input list<BackendDAE.Equation> inRemovedInitialEquationLst; | ||
| 190 | input Absyn.Program p; | ||
| 191 | input Absyn.Path className; | ||
| 192 | input String FMUVersion; | ||
| 193 | input String FMUType; | ||
| 194 | input String filenamePrefix; | ||
| 195 | input String fmuTargetName; | ||
| 196 | input Option<SimCode.SimulationSettings> simSettings; | ||
| 197 | input Boolean translateOnly = false; | ||
| 198 | output list<String> libs; | ||
| 199 | output String fileDir; | ||
| 200 | output Real timeSimCode; | ||
| 201 | output Real timeTemplates; | ||
| 202 | protected | ||
| 203 | list<String> includes,includeDirs; | ||
| 204 | list<SimCodeFunction.Function> functions; | ||
| 205 | SimCode.SimCode simCode; | ||
| 206 | list<SimCodeFunction.RecordDeclaration> recordDecls; | ||
| 207 | Absyn.ComponentRef a_cref; | ||
| 208 | list<String> libPaths; | ||
| 209 | tuple<Integer,UnorderedMap<DAE.Exp, Integer>,list<DAE.Exp>> literals; | ||
| 210 | algorithm | ||
| 211 | 86 | System.realtimeTick(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 212 | 86 | a_cref := AbsynUtil.pathToCref(className); | |
| 213 | 86 | fileDir := ProgramUtil.getFileDir(a_cref, p); | |
| 214 | 86 | (libs,libPaths,includes, includeDirs, recordDecls, functions, literals) := | |
| 215 | SimCodeUtilShared.createFunctions(p, inBackendDAE.shared.functionTree); | ||
| 216 | 86 | simCode := createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, NONE(), | |
| 217 | inRemovedInitialEquationLst, className, filenamePrefix, fileDir, functions, | ||
| 218 | includes, includeDirs, libs, libPaths, p, simSettings, recordDecls, | ||
| 219 | literals, Absyn.FUNCTIONARGS({},{}), isFMU=true, FMUVersion=FMUVersion, | ||
| 220 | fmuTargetName=fmuTargetName, inFMIDer=inFMIDer); | ||
| 221 | 86 | timeSimCode := System.realtimeTock(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 222 | 86 | ExecStat.execStat("SimCode"); | |
| 223 | |||
| 224 | 86 | System.realtimeTick(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 225 | 86 | callTargetTemplatesFMU(simCode, Config.simCodeTarget(), FMUVersion, FMUType, p, translateOnly); | |
| 226 | 86 | timeTemplates := System.realtimeTock(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 227 | end generateModelCodeFMU; | ||
| 228 | |||
| 229 | |||
| 230 | protected function generateModelCodeXML " | ||
| 231 | Generates code for a model by creating a SimCode structure and calling the | ||
| 232 | template-based code generator on it." | ||
| 233 | input BackendDAE.BackendDAE inBackendDAE; | ||
| 234 | input BackendDAE.BackendDAE inInitDAE; | ||
| 235 | input Option<BackendDAE.BackendDAE> inInitDAE_lambda0; | ||
| 236 | input list<BackendDAE.Equation> inRemovedInitialEquationLst; | ||
| 237 | input Absyn.Program p; | ||
| 238 | input Absyn.Path className; | ||
| 239 | input String filenamePrefix; | ||
| 240 | input Option<SimCode.SimulationSettings> simSettingsOpt; | ||
| 241 | output list<String> libs; | ||
| 242 | output String fileDir; | ||
| 243 | output Real timeSimCode; | ||
| 244 | output Real timeTemplates; | ||
| 245 | protected | ||
| 246 | list<String> includes,includeDirs; | ||
| 247 | list<SimCodeFunction.Function> functions; | ||
| 248 | SimCode.SimCode simCode; | ||
| 249 | list<SimCodeFunction.RecordDeclaration> recordDecls; | ||
| 250 | list<String> libPaths; | ||
| 251 | Absyn.ComponentRef a_cref; | ||
| 252 | tuple<Integer,UnorderedMap<DAE.Exp, Integer>,list<DAE.Exp>> literals; | ||
| 253 | algorithm | ||
| 254 | ✗ | System.realtimeTick(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 255 | ✗ | a_cref := AbsynUtil.pathToCref(className); | |
| 256 | ✗ | fileDir := ProgramUtil.getFileDir(a_cref, p); | |
| 257 | ✗ | (libs, libPaths, includes, includeDirs, recordDecls, functions, literals) := | |
| 258 | SimCodeUtilShared.createFunctions(p, inBackendDAE.shared.functionTree); | ||
| 259 | ✗ | (simCode,_) := SimCodeUtil.createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, NONE(), inRemovedInitialEquationLst, | |
| 260 | className, filenamePrefix, fileDir, functions, includes, includeDirs, libs,libPaths, p, simSettingsOpt, recordDecls, literals,Absyn.FUNCTIONARGS({},{})); | ||
| 261 | ✗ | timeSimCode := System.realtimeTock(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 262 | ✗ | ExecStat.execStat("SimCode"); | |
| 263 | |||
| 264 | ✗ | System.realtimeTick(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 265 | ✗ | callTargetTemplatesXML(simCode, Config.simCodeTarget()); | |
| 266 | ✗ | timeTemplates := System.realtimeTock(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 267 | end generateModelCodeXML; | ||
| 268 | |||
| 269 | public function generateModelCode " | ||
| 270 | Generates code for a model by creating a SimCode structure and calling the | ||
| 271 | template-based code generator on it." | ||
| 272 | input BackendDAE.BackendDAE inBackendDAE; | ||
| 273 | input BackendDAE.BackendDAE inInitDAE; | ||
| 274 | input Option<BackendDAE.BackendDAE> inInitDAE_lambda0; | ||
| 275 | input Option<BackendDAE.InlineData> inInlineData; | ||
| 276 | input list<BackendDAE.Equation> inRemovedInitialEquationLst; | ||
| 277 | input Absyn.Program p; | ||
| 278 | input Absyn.Path className; | ||
| 279 | input String filenamePrefix; | ||
| 280 | input Option<SimCode.SimulationSettings> simSettingsOpt; | ||
| 281 | input Absyn.FunctionArgs args; | ||
| 282 | input BackendDAE.SymbolicJacobians inFMIDer = {}; | ||
| 283 | output list<String> libs; | ||
| 284 | output String fileDir; | ||
| 285 | output Real timeSimCode; | ||
| 286 | output Real timeTemplates; | ||
| 287 | protected | ||
| 288 | list<String> includes, includeDirs,libPaths; | ||
| 289 | list<SimCodeFunction.Function> functions; | ||
| 290 | SimCode.SimCode simCode; | ||
| 291 | list<SimCodeFunction.RecordDeclaration> recordDecls; | ||
| 292 | Absyn.ComponentRef a_cref; | ||
| 293 | tuple<Integer, UnorderedMap<DAE.Exp, Integer>, list<DAE.Exp>> literals; | ||
| 294 | Integer numCheckpoints; | ||
| 295 | |||
| 296 | algorithm | ||
| 297 | 974 | numCheckpoints:=ErrorExt.getNumCheckpoints(); | |
| 298 | try | ||
| 299 | 974 | StackOverflow.clearStacktraceMessages(); | |
| 300 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 974 times.
|
974 | if Flags.isSet(Flags.GRAPHML) then |
| 301 | ✗ | HpcOmTaskGraph.dumpTaskGraph(inBackendDAE, filenamePrefix); | |
| 302 | ✗ | BackendDump.dumpBackendDAEBipartiteGraph(inBackendDAE, "BipartiteGraph_CompleteDAE_"+filenamePrefix); | |
| 303 | end if; | ||
| 304 | 974 | System.realtimeTick(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 305 | 974 | a_cref := AbsynUtil.pathToCref(className); | |
| 306 | 974 | fileDir := ProgramUtil.getFileDir(a_cref, p); | |
| 307 | |||
| 308 | 974 | (libs, libPaths, includes, includeDirs, recordDecls, functions, literals) := SimCodeUtilShared.createFunctions(p, inBackendDAE.shared.functionTree); | |
| 309 | 974 | simCode := createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, inInlineData, inRemovedInitialEquationLst, className, filenamePrefix, fileDir, functions, includes, includeDirs, libs,libPaths, p, simSettingsOpt, recordDecls, literals, args,inFMIDer=inFMIDer); | |
| 310 | 974 | timeSimCode := System.realtimeTock(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 311 | 974 | ExecStat.execStat("SimCode"); | |
| 312 | |||
| 313 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 974 times.
|
974 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 314 | ✗ | serializeNotify(simCode, "SimCode"); | |
| 315 | ✗ | ExecStat.execStat("Serialize simCode"); | |
| 316 | end if; | ||
| 317 | |||
| 318 | 974 | System.realtimeTick(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 319 | 974 | callTargetTemplates(simCode, Config.simCodeTarget()); | |
| 320 | 974 | timeTemplates := System.realtimeTock(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 321 | 974 | ExecStat.execStat("Templates"); | |
| 322 |
1/2✓ Branch 0 taken 974 times.
✗ Branch 1 not taken.
|
974 | return; |
| 323 | else | ||
| 324 | ✗ | setGlobalRoot(Global.stackoverFlowIndex, NONE()); | |
| 325 | ✗ | ErrorExt.rollbackNumCheckpoints(ErrorExt.getNumCheckpoints()-numCheckpoints); | |
| 326 | ✗ | Error.addInternalError(StackOverflow.errorPrefix() + " in " + getInstanceName() + "...\n" + stringDelimitList(StackOverflow.readableStacktraceMessages(), "\n"), sourceInfo()); | |
| 327 | /* Do not fail or we can loop too much */ | ||
| 328 | ✗ | StackOverflow.clearStacktraceMessages(); | |
| 329 | end try annotation(__OpenModelica_stackOverflowCheckpoint=true); | ||
| 330 | ✗ | fail(); | |
| 331 | end generateModelCode; | ||
| 332 | |||
| 333 | protected function createSimCode " | ||
| 334 | SimCode generator switch - if the NUMPROC-Flag is set, the simcode will be extended with parallel informations." | ||
| 335 | input BackendDAE.BackendDAE inBackendDAE; | ||
| 336 | input BackendDAE.BackendDAE inInitDAE; | ||
| 337 | input Option<BackendDAE.BackendDAE> inInitDAE_lambda0; | ||
| 338 | input Option<BackendDAE.InlineData> inInlineData; | ||
| 339 | input list<BackendDAE.Equation> inRemovedInitialEquationLst; | ||
| 340 | input Absyn.Path inClassName; | ||
| 341 | input String filenamePrefix; | ||
| 342 | input String inString11; | ||
| 343 | input list<SimCodeFunction.Function> functions; | ||
| 344 | input list<String> externalFunctionIncludes; | ||
| 345 | input list<String> includeDirs; | ||
| 346 | input list<String> libs; | ||
| 347 | input list<String> libPaths; | ||
| 348 | input Absyn.Program program; | ||
| 349 | input Option<SimCode.SimulationSettings> simSettingsOpt; | ||
| 350 | input list<SimCodeFunction.RecordDeclaration> recordDecls; | ||
| 351 | input tuple<Integer, UnorderedMap<DAE.Exp, Integer>, list<DAE.Exp>> literals; | ||
| 352 | input Absyn.FunctionArgs args; | ||
| 353 | input Boolean isFMU=false; | ||
| 354 | input String FMUVersion=""; | ||
| 355 | input String fmuTargetName=""; | ||
| 356 | input BackendDAE.SymbolicJacobians inFMIDer = {}; | ||
| 357 | output SimCode.SimCode simCode; | ||
| 358 | algorithm | ||
| 359 | // FIXME This matchcontinue is extremely horrible! Please remove it | ||
| 360 | simCode := matchcontinue args | ||
| 361 | local | ||
| 362 | Integer numProc; | ||
| 363 | SimCode.SimCode tmpSimCode; | ||
| 364 | |||
| 365 | case _ algorithm | ||
| 366 | // MULTI_RATE PARTITIONINIG | ||
| 367 |
1/2✓ Branch 1 taken 1060 times.
✗ Branch 2 not taken.
|
1060 | true := Flags.isSet(Flags.MULTIRATE_PARTITION); |
| 368 | ✗ | then HpcOmSimCodeMain.createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, inRemovedInitialEquationLst, inClassName, filenamePrefix, inString11, functions, externalFunctionIncludes, includeDirs, libs,libPaths, program, simSettingsOpt, recordDecls, literals, args); | |
| 369 | |||
| 370 | case _ algorithm | ||
| 371 |
2/2✓ Branch 1 taken 1052 times.
✓ Branch 2 taken 8 times.
|
1060 | true := Flags.isSet(Flags.HPCOM); |
| 372 | |||
| 373 | // either generate code for profiling or for parallel simulation | ||
| 374 | //bcall(not stringEq("none",Flags.getConfigString(Flags.PROFILING_LEVEL)),print,"Deactivate profiling if you want to simulate in parallel.\n"); | ||
| 375 | //_ = bcallret2((not stringEq("none",Flags.getConfigString(Flags.PROFILING_LEVEL))) and (not stringEq("all_perf",Flags.getConfigString(Flags.PROFILING_LEVEL))),FlagsUtil.set,Flags.HPCOM,false,true); | ||
| 376 | //true = stringEq("none",Flags.getConfigString(Flags.PROFILING_LEVEL)) or stringEq("all_perf",Flags.getConfigString(Flags.PROFILING_LEVEL)); | ||
| 377 | |||
| 378 | 8 | numProc := Flags.getConfigInt(Flags.NUM_PROC); | |
| 379 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 time.
|
8 | true := numProc == 0; |
| 380 | 1 | print("hpcom computes the ideal number of processors. If you want to set the number manually, use the flag +n=_\n"); | |
| 381 | 1 | then HpcOmSimCodeMain.createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, inRemovedInitialEquationLst, inClassName, filenamePrefix, inString11, functions, externalFunctionIncludes, includeDirs, libs,libPaths,program, simSettingsOpt, recordDecls, literals, args); | |
| 382 | |||
| 383 | case _ algorithm | ||
| 384 |
2/2✓ Branch 1 taken 1052 times.
✓ Branch 2 taken 7 times.
|
1059 | true := Flags.isSet(Flags.HPCOM); |
| 385 | |||
| 386 | // either generate code for profiling or for parallel simulation | ||
| 387 | //bcall((not stringEq("none",Flags.getConfigString(Flags.PROFILING_LEVEL))) and (not stringEq("all_perf",Flags.getConfigString(Flags.PROFILING_LEVEL))),print,"Deactivate profiling if you want to simulate in parallel.\n"); | ||
| 388 | //_ = bcallret2(not stringEq("none",Flags.getConfigString(Flags.PROFILING_LEVEL)),FlagsUtil.set,Flags.HPCOM,false,true); | ||
| 389 | //true = stringEq("none",Flags.getConfigString(Flags.PROFILING_LEVEL)) or stringEq("all_perf",Flags.getConfigString(Flags.PROFILING_LEVEL)); | ||
| 390 | |||
| 391 | 7 | numProc := Flags.getConfigInt(Flags.NUM_PROC); | |
| 392 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | true := (numProc > 0); |
| 393 | 7 | then HpcOmSimCodeMain.createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, inRemovedInitialEquationLst, inClassName, filenamePrefix, inString11, functions, externalFunctionIncludes, includeDirs, libs, libPaths,program,simSettingsOpt, recordDecls, literals, args); | |
| 394 | |||
| 395 | else algorithm | ||
| 396 | 1052 | (tmpSimCode, _) := SimCodeUtil.createSimCode(inBackendDAE, inInitDAE, inInitDAE_lambda0, inInlineData, inRemovedInitialEquationLst, inClassName, filenamePrefix, inString11, functions, externalFunctionIncludes, includeDirs, libs,libPaths,program, simSettingsOpt, recordDecls, literals, args, isFMU=isFMU, FMUVersion=FMUVersion, fmuTargetName=fmuTargetName, inFMIDer=inFMIDer); | |
| 397 | then tmpSimCode; | ||
| 398 | end matchcontinue; | ||
| 399 | end createSimCode; | ||
| 400 | |||
| 401 | function generateModelCodeNewBackend | ||
| 402 | input NBackendDAE.BackendDAE bdae; | ||
| 403 | input Absyn.Path className; | ||
| 404 | input String fileNamePrefix; | ||
| 405 | input Option<SimCode.SimulationSettings> simSettingsOpt; | ||
| 406 | input TranslateModelKind kind = TranslateModelKind.NORMAL() "FMU() to generate an FMU instead of a simulation"; | ||
| 407 | output list<String> libs; | ||
| 408 | output String fileDir; | ||
| 409 | output Real timeSimCode = 0.0; | ||
| 410 | output Real timeTemplates = 0.0; | ||
| 411 | output AvlTreePathFunction.Tree oldFunctionTree; | ||
| 412 | protected | ||
| 413 | Integer numCheckpoints; | ||
| 414 | NSimCode.SimCode simCode; | ||
| 415 | SimCode.SimCode oldSimCode; | ||
| 416 | algorithm | ||
| 417 | 188 | numCheckpoints := ErrorExt.getNumCheckpoints(); | |
| 418 | 188 | StackOverflow.clearStacktraceMessages(); | |
| 419 | try | ||
| 420 | 188 | System.realtimeTick(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 421 | 188 | (simCode, oldFunctionTree) := NSimCode.SimCode.create(bdae, className, fileNamePrefix, simSettingsOpt, SymbolTable.getAbsyn()); | |
| 422 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 182 times.
|
188 | if Flags.isSet(Flags.DUMP_SIMCODE) then |
| 423 | 6 | print(NSimCode.SimCode.toString(simCode)); | |
| 424 | end if; | ||
| 425 | 188 | (fileDir, libs) := NSimCode.SimCode.getDirectoryAndLibs(simCode); | |
| 426 | 188 | setGlobalRoot(Global.timeIndependentVars, NONE()); | |
| 427 | 188 | oldSimCode := NSimCode.SimCode.convert(simCode); | |
| 428 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 182 times.
|
188 | if Flags.isSet(Flags.DUMP_SIMCODE) then |
| 429 | 6 | SimCodeUtil.dumpSimCodeDebug(oldSimCode); | |
| 430 | end if; | ||
| 431 | 188 | timeSimCode := System.realtimeTock(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 432 | |||
| 433 | 188 | ExecStat.execStat("SimCode"); | |
| 434 | |||
| 435 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 188 times.
|
188 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 436 | ✗ | serializeNotify(oldSimCode, "SimCode"); | |
| 437 | ✗ | ExecStat.execStat("Serialize simCode"); | |
| 438 | end if; | ||
| 439 | |||
| 440 | 188 | System.realtimeTick(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 441 | () := match kind | ||
| 442 | local String fmuType, fmuTarget; | ||
| 443 | case TranslateModelKind.FMU(kind = fmuType, targetName = fmuTarget) algorithm | ||
| 444 | // The FMU model interface source files are written into the FMU's tmp | ||
| 445 | // sources directory; mirror the old backend (see createSimCode). | ||
| 446 | 5 | oldSimCode.fmuTargetName := fmuTarget; | |
| 447 | 10 | oldSimCode.fullPathPrefix := Util.hashFileNamePrefix(fileNamePrefix) + ".fmutmp/sources/"; | |
| 448 | // cref -> value reference map used by the FMU model interface templates (lookupVR). | ||
| 449 | 5 | oldSimCode.valueReferences := SimCodeUtil.getValueReferenceMapping(oldSimCode.modelInfo); | |
| 450 | // Minimal ModelStructure (state derivatives + outputs) so an FMI master | ||
| 451 | // can drive Model Exchange integration. TODO: add dependencies. | ||
| 452 | 5 | oldSimCode.modelStructure := SimCodeUtil.createMinimalFMIModelStructure(oldSimCode.modelInfo); | |
| 453 | 5 | callTargetTemplatesFMU(oldSimCode, Config.simCodeTarget(), FMI.getFMIVersionString(), fmuType, SymbolTable.getAbsyn(), kind.translateOnly); | |
| 454 | then (); | ||
| 455 | else algorithm | ||
| 456 | 183 | callTargetTemplates(oldSimCode, Config.simCodeTarget()); | |
| 457 | then (); | ||
| 458 | end match; | ||
| 459 | 188 | timeTemplates := System.realtimeTock(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 460 | 188 | ExecStat.execStat("Templates"); | |
| 461 | else | ||
| 462 | ✗ | setGlobalRoot(Global.stackoverFlowIndex, NONE()); | |
| 463 | ✗ | ErrorExt.rollbackNumCheckpoints(ErrorExt.getNumCheckpoints()-numCheckpoints); | |
| 464 | ✗ | Error.addInternalError(StackOverflow.errorPrefix() + " in " + getInstanceName() + "...\n" + stringDelimitList(StackOverflow.readableStacktraceMessages(), "\n"), sourceInfo()); | |
| 465 | /* Do not fail or we can loop too much */ | ||
| 466 | ✗ | StackOverflow.clearStacktraceMessages(); | |
| 467 | ✗ | fail(); | |
| 468 | end try annotation(__OpenModelica_stackOverflowCheckpoint=true); | ||
| 469 | end generateModelCodeNewBackend; | ||
| 470 | |||
| 471 | protected | ||
| 472 | partial function PartialRunTpl | ||
| 473 | output tuple<Boolean,list<String>> res; | ||
| 474 | end PartialRunTpl; | ||
| 475 | |||
| 476 | partial function FuncText | ||
| 477 | input Tpl.Text txt; | ||
| 478 | output Tpl.Text out_txt; | ||
| 479 | end FuncText; | ||
| 480 | |||
| 481 | function runTplWriteFile | ||
| 482 | extends PartialRunTpl; | ||
| 483 | input FuncText func; | ||
| 484 | input String file; | ||
| 485 | protected | ||
| 486 | Integer nErr; | ||
| 487 | algorithm | ||
| 488 | res := (false,{}); | ||
| 489 | try | ||
| 490 | 25806 | SimCodeCodegenUtil.resetFunctionIndex(); | |
| 491 | 25806 | SimCodeFunctionUtil.codegenResetTryThrowIndex(); | |
| 492 |
2/2✓ Branch 1 taken 46 times.
✓ Branch 2 taken 25760 times.
|
25806 | if /*Config.acceptMetaModelicaGrammar() or*/ Flags.isSet(Flags.GEN_DEBUG_SYMBOLS) then |
| 493 | 46 | Tpl.textFileConvertLines(Tpl.tplCallWithFailErrorNoArg(func), file); | |
| 494 | else | ||
| 495 | 25760 | nErr := Error.getNumErrorMessages(); | |
| 496 | 25760 | Tpl.closeFile(Tpl.tplCallWithFailErrorNoArg(func,Tpl.redirectToFile(Tpl.emptyTxt, file))); | |
| 497 | 25760 | Tpl.failIfTrue(Error.getNumErrorMessages() > nErr); | |
| 498 | end if; | ||
| 499 | 25806 | res := (true,SimCodeUtil.getFunctionIndex()); | |
| 500 | else | ||
| 501 | end try; | ||
| 502 | end runTplWriteFile; | ||
| 503 | |||
| 504 | function runTpl | ||
| 505 | extends PartialRunTpl; | ||
| 506 | input FuncText func; | ||
| 507 | algorithm | ||
| 508 | res := (false,{}); | ||
| 509 | try | ||
| 510 | 2244 | SimCodeCodegenUtil.resetFunctionIndex(); | |
| 511 | 2244 | SimCodeFunctionUtil.codegenResetTryThrowIndex(); | |
| 512 | 2244 | Tpl.tplCallWithFailErrorNoArg(func); | |
| 513 | 2244 | res := (true,SimCodeUtil.getFunctionIndex()); | |
| 514 | else | ||
| 515 | end try; | ||
| 516 | end runTpl; | ||
| 517 | |||
| 518 | // TODO: use another switch ... later make it first class option like -target or so | ||
| 519 | protected function callTargetTemplates " | ||
| 520 | Generate target code by passing the SimCode data structure to templates." | ||
| 521 | input SimCode.SimCode simCode; | ||
| 522 | input String target; | ||
| 523 | protected | ||
| 524 | partial function Func | ||
| 525 | input Tpl.Text txt; | ||
| 526 | input SimCode.SimCode a_simCode; | ||
| 527 | output Tpl.Text out_txt; | ||
| 528 | end Func; | ||
| 529 | partial function FuncText | ||
| 530 | input Tpl.Text txt; | ||
| 531 | output Tpl.Text out_txt; | ||
| 532 | end FuncText; | ||
| 533 | partial function BoolFunc | ||
| 534 | input Tpl.Text txt; | ||
| 535 | input SimCode.SimCode a_simCode; | ||
| 536 | output Tpl.Text out_txt; | ||
| 537 | end BoolFunc; | ||
| 538 | Func func; | ||
| 539 | Tpl.Text txt; | ||
| 540 | |||
| 541 | function runToStr | ||
| 542 | extends PartialRunTpl; | ||
| 543 | input Func func; | ||
| 544 | partial function Func | ||
| 545 | output String str; | ||
| 546 | end Func; | ||
| 547 | algorithm | ||
| 548 | res := (false,{}); | ||
| 549 | try | ||
| 550 | 2245 | SimCodeCodegenUtil.resetFunctionIndex(); | |
| 551 | 2245 | SimCodeFunctionUtil.codegenResetTryThrowIndex(); | |
| 552 |
1/2✓ Branch 0 taken 2245 times.
✗ Branch 1 not taken.
|
2245 | func(); |
| 553 | 2245 | res := (true,SimCodeUtil.getFunctionIndex()); | |
| 554 | else | ||
| 555 | end try; | ||
| 556 | end runToStr; | ||
| 557 | |||
| 558 | function runCodegenFunc | ||
| 559 | input PartialRunTpl func; | ||
| 560 | output tuple<Boolean,list<String>> res; | ||
| 561 | protected | ||
| 562 | Boolean b; | ||
| 563 | algorithm | ||
| 564 |
1/2✓ Branch 0 taken 31417 times.
✗ Branch 1 not taken.
|
31417 | res as (b,_) := func(); |
| 565 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31417 times.
|
31417 | if not b then |
| 566 | ✗ | Error.addInternalError(System.dladdr(func) + " failed\n", sourceInfo()); | |
| 567 | end if; | ||
| 568 |
1/2✓ Branch 1 taken 31417 times.
✗ Branch 2 not taken.
|
31417 | if ErrorExt.getNumMessages() > 0 then |
| 569 | ✗ | ErrorExt.moveMessagesToParentThread(); | |
| 570 | end if; | ||
| 571 | end runCodegenFunc; | ||
| 572 | |||
| 573 | function runToBoolean | ||
| 574 | input Func func; | ||
| 575 | output tuple<Boolean,list<String>> res; | ||
| 576 | protected | ||
| 577 | partial function Func | ||
| 578 | output Boolean b; | ||
| 579 | end Func; | ||
| 580 | algorithm | ||
| 581 |
1/2✓ Branch 0 taken 1122 times.
✗ Branch 1 not taken.
|
1122 | res := (func(),{}); |
| 582 | end runToBoolean; | ||
| 583 | |||
| 584 | |||
| 585 | AvlSetString.Tree generatedObjects=AvlSetString.EMPTY(); | ||
| 586 | algorithm | ||
| 587 | 1166 | setGlobalRoot(Global.optionSimCode, SOME(simCode)); | |
| 588 | 1166 | SimCodeFunctionUtil.setTrivialRecords(simCode.recordDecls); | |
| 589 | () := match target | ||
| 590 | local | ||
| 591 | String str, guid; | ||
| 592 | list<PartialRunTpl> codegenFuncs; | ||
| 593 | Integer numThreads, n; | ||
| 594 | list<tuple<Boolean,list<String>>> res = {}; | ||
| 595 | list<String> strs, tmp, matches; | ||
| 596 | String esp32Dir; | ||
| 597 | |||
| 598 | case "Cpp" | ||
| 599 | algorithm | ||
| 600 | 40 | callTargetTemplatesCPP(simCode); | |
| 601 |
2/2✓ Branch 0 taken 80 times.
✓ Branch 1 taken 40 times.
|
120 | for str in {"CalcHelperMain.o\n",".so\n"} loop |
| 602 | 80 | generatedObjects := AvlSetString.add(generatedObjects, "OMCpp" + simCode.fileNamePrefix + str); | |
| 603 | end for; | ||
| 604 | then (); | ||
| 605 | |||
| 606 | case "C" | ||
| 607 | algorithm | ||
| 608 | 1122 | guid := System.getUUIDStr(); | |
| 609 | |||
| 610 | 1122 | System.realtimeTick(ClockIndexes.RT_PROFILER0); | |
| 611 | codegenFuncs := {}; | ||
| 612 | 1122 | codegenFuncs := (function runToBoolean(func=function SerializeInitXML.simulationInitFileReturnBool(simCode=simCode, guid=guid))) :: codegenFuncs; | |
| 613 | 1122 | codegenFuncs := (function runTpl(func=function CodegenC.translateModel(in_a_simCode=simCode))) :: codegenFuncs; | |
| 614 |
2/2✓ Branch 21 taken 23562 times.
✓ Branch 22 taken 1122 times.
|
25806 | for f in { |
| 615 | // external objects | ||
| 616 | (CodegenC.simulationFile_exo, "_01exo.c"), | ||
| 617 | // non-linear systems | ||
| 618 | (CodegenC.simulationFile_nls, "_02nls.c"), | ||
| 619 | (CodegenC.simulationFile_lsy, "_03lsy.c"), | ||
| 620 | (CodegenC.simulationFile_set, "_04set.c"), | ||
| 621 | (CodegenC.simulationFile_evt, "_05evt.c"), | ||
| 622 | (CodegenC.simulationFile_inz, "_06inz.c"), | ||
| 623 | (CodegenC.simulationFile_dly, "_07dly.c"), | ||
| 624 | (CodegenC.simulationFile_bnd, "_08bnd.c"), | ||
| 625 | (CodegenC.simulationFile_alg, "_09alg.c"), | ||
| 626 | (CodegenC.simulationFile_asr, "_10asr.c"), | ||
| 627 | (CodegenC.simulationFile_jac, "_12jac.c"), | ||
| 628 | (CodegenC.simulationFile_jac_header, "_12jac.h"), | ||
| 629 | (CodegenC.simulationFile_opt, "_13opt.c"), | ||
| 630 | (CodegenC.simulationFile_opt_header, "_13opt.h"), | ||
| 631 | (CodegenC.simulationFile_lnz, "_14lnz.c"), | ||
| 632 | (CodegenC.simulationFile_syn, "_15syn.c"), | ||
| 633 | (CodegenC.simulationFile_dae, "_16dae.c"), | ||
| 634 | (CodegenC.simulationFile_dae_header, "_16dae.h"), | ||
| 635 | (CodegenC.simulationFile_inl, "_17inl.c"), | ||
| 636 | (CodegenC.simulationFile_spd, "_18spd.c"), | ||
| 637 | (CodegenC.simulationHeaderFile, "_model.h") | ||
| 638 | } loop | ||
| 639 | 23562 | (func,str) := f; | |
| 640 | 23562 | codegenFuncs := (function runTplWriteFile(func=function func(a_simCode=simCode), file=simCode.fileNamePrefix + str)) :: codegenFuncs; | |
| 641 | 23562 | (n,matches) := System.regex(str, "\\(.*\\)[.]c$", 2, false, false); | |
| 642 |
2/2✓ Branch 0 taken 19074 times.
✓ Branch 1 taken 4488 times.
|
23562 | if n==2 then |
| 643 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 19074 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19074 times.
|
19074 | _::str::_ := matches; |
| 644 | 19074 | generatedObjects := AvlSetString.add(generatedObjects, simCode.fileNamePrefix + str + ".o\n"); | |
| 645 | end if; | ||
| 646 | end for; | ||
| 647 |
2/2✓ Branch 0 taken 4488 times.
✓ Branch 1 taken 1122 times.
|
5610 | for str in {"_11mix.o\n","_functions.o\n","_info.json\n","_init.xml\n"} loop |
| 648 | 4488 | generatedObjects := AvlSetString.add(generatedObjects, simCode.fileNamePrefix + str); | |
| 649 | end for; | ||
| 650 | 1122 | codegenFuncs := (function runTpl(func=function CodegenC.simulationFile_mixAndHeader(a_simCode=simCode, a_modelNamePrefix=simCode.fileNamePrefix))) :: codegenFuncs; | |
| 651 | 1122 | codegenFuncs := (function runTplWriteFile(func=function CodegenC.simulationFile(in_a_simCode=simCode, in_a_guid=guid, in_a_isModelExchangeFMU=""), file=simCode.fileNamePrefix + ".c")) :: codegenFuncs; | |
| 652 | 1122 | codegenFuncs := (function runTplWriteFile(func=function CodegenC.simulationFunctionsFile(a_filePrefix=simCode.fileNamePrefix, a_functions=simCode.modelInfo.functions, a_genericCalls=simCode.generic_loop_calls), file=simCode.fileNamePrefix + "_functions.c")) :: codegenFuncs; | |
| 653 | |||
| 654 | 1122 | codegenFuncs := (function runToStr(func=function SerializeSparsityPattern.serialize(code=simCode))) :: codegenFuncs; | |
| 655 |
2/2✓ Branch 1 taken 1121 times.
✓ Branch 2 taken 1 time.
|
2243 | codegenFuncs := (function runToStr(func=function SerializeModelInfo.serialize(code=simCode, withOperations=Flags.isSet(Flags.INFO_XML_OPERATIONS)))) :: codegenFuncs; |
| 656 | |||
| 657 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1121 times.
|
1122 | if Flags.getConfigBool(Flags.PARMODAUTO) then |
| 658 |
1/2✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
|
2 | codegenFuncs := (function runToStr(func=function SerializeTaskSystemInfo.serializeParMod(code=simCode, withOperations=Flags.isSet(Flags.INFO_XML_OPERATIONS)))) :: codegenFuncs; |
| 659 | 1 | generatedObjects := AvlSetString.add(generatedObjects, simCode.fileNamePrefix + "_ode.json\n"); | |
| 660 | end if; | ||
| 661 | |||
| 662 | if Autoconf.os == "Windows_NT" then | ||
| 663 | codegenFuncs := (function runToStr(func=function SimCodeUtil.generateRunnerBatScript(code=simCode))) :: codegenFuncs; | ||
| 664 | end if; | ||
| 665 | |||
| 666 | // Test the parallel code generator in the test suite. Should give decent results given that the task is disk-intensive. | ||
| 667 |
1/2✓ Branch 1 taken 1122 times.
✗ Branch 2 not taken.
|
1122 | numThreads := max(1, if Testsuite.isRunning() then min(2, System.numProcessors()) else Config.noProc()); |
| 668 |
2/4✓ Branch 1 taken 1122 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1122 times.
|
1122 | if (not Flags.isSet(Flags.PARALLEL_CODEGEN)) or numThreads==1 then |
| 669 | ✗ | res := list(codegen_func() for codegen_func in codegenFuncs); | |
| 670 | else | ||
| 671 | 1122 | res := System.launchParallelTasks(numThreads, codegenFuncs, runCodegenFunc); | |
| 672 | end if; | ||
| 673 | strs := {}; | ||
| 674 |
2/2✓ Branch 0 taken 31417 times.
✓ Branch 1 taken 1122 times.
|
32539 | for tpl in res loop |
| 675 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31417 times.
|
31417 | (true,tmp) := tpl; |
| 676 | 31417 | strs := List.append_reverse(tmp, strs); | |
| 677 | end for; | ||
| 678 | 1122 | strs := listReverse(strs); | |
| 679 | // Some files are only sometimes generated, like initialization when it has >2000 equations | ||
| 680 |
2/2✓ Branch 0 taken 311 times.
✓ Branch 1 taken 1122 times.
|
1433 | for str in strs loop |
| 681 | 311 | (n,matches) := System.regex(str, "\\(.*\\)[.]c$", 2, false, false); | |
| 682 |
1/2✓ Branch 0 taken 311 times.
✗ Branch 1 not taken.
|
311 | if n==2 then |
| 683 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 311 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 311 times.
|
311 | _::str::_ := matches; |
| 684 | 311 | generatedObjects := AvlSetString.add(generatedObjects, simCode.fileNamePrefix + str + ".o\n"); | |
| 685 | end if; | ||
| 686 | end for; | ||
| 687 | // write the makefile last! | ||
| 688 | 1122 | Tpl.closeFile(Tpl.tplCallWithFailError3(CodegenC.simulationMakefile,Config.simulationCodeTarget(),simCode,strs,txt=Tpl.redirectToFile(Tpl.emptyTxt, simCode.fileNamePrefix+".makefile"))); | |
| 689 | then (); | ||
| 690 | |||
| 691 | case "ExperimentalEmbeddedC" | ||
| 692 | algorithm | ||
| 693 | ✗ | System.realtimeTick(ClockIndexes.RT_PROFILER0); | |
| 694 | codegenFuncs := {}; | ||
| 695 | ✗ | for f in { | |
| 696 | (CodegenEmbeddedC.mainFile, "_main.c") | ||
| 697 | } loop | ||
| 698 | ✗ | (func,str) := f; | |
| 699 | ✗ | codegenFuncs := (function runTplWriteFile(func=function func(a_simCode=simCode), file=simCode.fileNamePrefix + str)) :: codegenFuncs; | |
| 700 | end for; | ||
| 701 | |||
| 702 | // Test the parallel code generator in the test suite. Should give decent results given that the task is disk-intensive. | ||
| 703 | ✗ | numThreads := max(1, if Testsuite.isRunning() then min(2, System.numProcessors()) else Config.noProc()); | |
| 704 | ✗ | if (not Flags.isSet(Flags.PARALLEL_CODEGEN)) or numThreads==1 then | |
| 705 | ✗ | res := list(func() for func in codegenFuncs); | |
| 706 | else | ||
| 707 | ✗ | res := System.launchParallelTasks(numThreads, codegenFuncs, runCodegenFunc); | |
| 708 | end if; | ||
| 709 | strs := {}; | ||
| 710 | ✗ | for tpl in res loop | |
| 711 | ✗ | (true,tmp) := tpl; | |
| 712 | ✗ | strs := List.append_reverse(tmp, strs); | |
| 713 | end for; | ||
| 714 | ✗ | strs := listReverse(strs); | |
| 715 | // write the makefile last! | ||
| 716 | then (); | ||
| 717 | |||
| 718 | case "ESP32" | ||
| 719 | algorithm | ||
| 720 | ✗ | System.realtimeTick(ClockIndexes.RT_PROFILER0); | |
| 721 | // The generated project is an ESP-IDF one, which has to be laid out as | ||
| 722 | // a project directory holding a main component. | ||
| 723 | ✗ | esp32Dir := simCode.fileNamePrefix + "_esp32"; | |
| 724 | ✗ | if not Util.createDirectoryTree(esp32Dir + "/main") then | |
| 725 | ✗ | Error.addInternalError("Failed to create directory " + esp32Dir + "/main", sourceInfo()); | |
| 726 | ✗ | fail(); | |
| 727 | end if; | ||
| 728 | ✗ | runTplWriteFile(func = function CodegenESP32.projectCMakeFile(a_simCode=simCode), | |
| 729 | file = esp32Dir + "/CMakeLists.txt"); | ||
| 730 | ✗ | runTplWriteFile(func = function CodegenESP32.componentCMakeFile(a_simCode=simCode), | |
| 731 | file = esp32Dir + "/main/CMakeLists.txt"); | ||
| 732 | ✗ | runTplWriteFile(func = function CodegenESP32.sdkconfigFile(a_simCode=simCode), | |
| 733 | file = esp32Dir + "/sdkconfig.defaults"); | ||
| 734 | ✗ | runTplWriteFile(func = function CodegenESP32.modelHeaderFile(a_simCode=simCode), | |
| 735 | file = esp32Dir + "/main/" + simCode.fileNamePrefix + "_model.h"); | ||
| 736 | ✗ | runTplWriteFile(func = function CodegenESP32.modelSourceFile(a_simCode=simCode), | |
| 737 | file = esp32Dir + "/main/" + simCode.fileNamePrefix + "_model.c"); | ||
| 738 | ✗ | runTplWriteFile(func = function CodegenESP32.appMainFile(a_simCode=simCode), | |
| 739 | file = esp32Dir + "/main/main.c"); | ||
| 740 | ✗ | runTplWriteFile(func = function CodegenESP32.readmeFile(a_simCode=simCode), | |
| 741 | file = esp32Dir + "/README.md"); | ||
| 742 | then (); | ||
| 743 | |||
| 744 | case "JavaScript" algorithm | ||
| 745 | ✗ | guid := System.getUUIDStr(); | |
| 746 | ✗ | Tpl.tplNoret(CodegenC.translateModel, simCode); | |
| 747 | ✗ | SerializeInitXML.simulationInitFile(simCode, guid); | |
| 748 | ✗ | System.covertTextFileToCLiteral(simCode.fileNamePrefix+"_init.xml",simCode.fileNamePrefix+"_init.c", Config.simulationCodeTarget()); | |
| 749 | ✗ | SerializeSparsityPattern.serialize(simCode); | |
| 750 | ✗ | SerializeModelInfo.serialize(simCode, Flags.isSet(Flags.INFO_XML_OPERATIONS)); | |
| 751 | ✗ | Tpl.tplNoret(CodegenJS.markdownFile, simCode); | |
| 752 | then (); | ||
| 753 | |||
| 754 | case "XML" algorithm | ||
| 755 | 4 | Tpl.tplNoret(CodegenXML.translateModel, simCode); | |
| 756 | then (); | ||
| 757 | |||
| 758 | case "wasm-jit" algorithm | ||
| 759 | // translateModel records the lowering error and fails, so the message | ||
| 760 | // surfaces (getErrorString / OMEdit) instead of a later "no prepared model". | ||
| 761 | ✗ | CodegenWasmJit.translateModel(simCode); | |
| 762 | // The wasm module carries its own metadata; *_init.xml is only read by | ||
| 763 | // OMEdit's variable browser and *_info.json only for debugging. | ||
| 764 | ✗ | if Flags.isSet(Flags.OMEDIT) then | |
| 765 | ✗ | guid := System.getUUIDStr(); | |
| 766 | ✗ | SerializeInitXML.simulationInitFile(simCode, guid); | |
| 767 | end if; | ||
| 768 | ✗ | if Flags.isSet(Flags.INFO_XML_OPERATIONS) then | |
| 769 | ✗ | SerializeModelInfo.serialize(simCode, true); | |
| 770 | end if; | ||
| 771 | then (); | ||
| 772 | |||
| 773 | case "wasm" algorithm | ||
| 774 | // Standalone WASI command module (merged model + runtime); run on the | ||
| 775 | // desktop with wasmtime. The module carries its own metadata, so no | ||
| 776 | // _init.xml is needed for a CLI simulate. emitStandalone records the error | ||
| 777 | // and fails if lowering fails, instead of emitting a silent empty module. | ||
| 778 | ✗ | CodegenWasmJit.emitStandalone(simCode); | |
| 779 | then (); | ||
| 780 | |||
| 781 | case "None" | ||
| 782 | then (); | ||
| 783 | |||
| 784 | else algorithm | ||
| 785 | ✗ | str := "Unknown template target: " + target; | |
| 786 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {str}); | |
| 787 | ✗ | then fail(); | |
| 788 | end match; | ||
| 789 |
1/2✓ Branch 1 taken 1166 times.
✗ Branch 2 not taken.
|
1166 | if Testsuite.isRunning() then |
| 790 | 1166 | System.appendFile(Testsuite.getTempFilesFile(), stringAppendList(AvlSetString.listKeys(generatedObjects))); | |
| 791 | end if; | ||
| 792 | 1166 | setGlobalRoot(Global.optionSimCode, NONE()); | |
| 793 | end callTargetTemplates; | ||
| 794 | |||
| 795 | protected function callTargetTemplatesCPP | ||
| 796 | input SimCode.SimCode iSimCode; | ||
| 797 | algorithm | ||
| 798 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 39 times.
|
40 | if(Flags.isSet(Flags.HPCOM)) then |
| 799 | 1 | Tpl.tplNoret(CodegenCppHpcom.translateModel, iSimCode); | |
| 800 | else | ||
| 801 | 39 | Tpl.tplNoret(CodegenCpp.translateModel, iSimCode); | |
| 802 | end if; | ||
| 803 | end callTargetTemplatesCPP; | ||
| 804 | |||
| 805 | protected function visualizationCadFiles | ||
| 806 | "Absolute paths of the CAD files referenced as shape types in the visxml, so the | ||
| 807 | exported FMU can embed them (a portable FMU cannot resolve the modelica:// URIs | ||
| 808 | on another machine)." | ||
| 809 | input String visualXmlFile; | ||
| 810 | output list<String> paths = {}; | ||
| 811 | protected | ||
| 812 | Absyn.Program program; | ||
| 813 | Boolean prevType = false; | ||
| 814 | String low, path; | ||
| 815 | algorithm | ||
| 816 | ✗ | if not System.regularFileExists(visualXmlFile) then | |
| 817 | ✗ | return; | |
| 818 | end if; | ||
| 819 | ✗ | program := SymbolTable.getAbsyn(); | |
| 820 | // strtok on "<>" yields the type text as the token right after each "type". | ||
| 821 | ✗ | for tok in System.strtok(System.readFile(visualXmlFile), "<>") loop | |
| 822 | ✗ | if prevType then | |
| 823 | ✗ | low := System.tolower(tok); | |
| 824 | ✗ | if StringUtil.endsWith(low, ".dxf") or StringUtil.endsWith(low, ".stl") or | |
| 825 | StringUtil.endsWith(low, ".obj") or StringUtil.endsWith(low, ".3ds") then | ||
| 826 | ✗ | if System.stringFind(tok, "modelica://") == 0 then | |
| 827 | ✗ | path := ProgramUtil.getFullPathFromUri(program, tok, true); | |
| 828 | elseif System.stringFind(tok, "file://") == 0 then | ||
| 829 | ✗ | path := substring(tok, 8, stringLength(tok)); | |
| 830 | else | ||
| 831 | path := tok; | ||
| 832 | end if; | ||
| 833 | ✗ | if not listMember(path, paths) then | |
| 834 | paths := path :: paths; | ||
| 835 | end if; | ||
| 836 | end if; | ||
| 837 | end if; | ||
| 838 | ✗ | prevType := stringEqual(tok, "type"); | |
| 839 | end for; | ||
| 840 | ✗ | paths := listReverse(paths); | |
| 841 | end visualizationCadFiles; | ||
| 842 | |||
| 843 | protected function fmuTranslationKind | ||
| 844 | "What of `fmuType` a translation depends on. `introduceOutputRealDerivatives` is | ||
| 845 | added for a pure Co-Simulation export and nothing else, so me and me_cs share | ||
| 846 | one translation." | ||
| 847 | input String FMUType; | ||
| 848 | output String kind = if FMI.isFMICSType(FMUType) and not FMI.isFMIMEType(FMUType) then "cs" else "other"; | ||
| 849 | end fmuTranslationKind; | ||
| 850 | |||
| 851 | protected function fmuTranslationFlags | ||
| 852 | "The flag state a translation depends on, copied because Flags hands out the | ||
| 853 | live arrays. | ||
| 854 | |||
| 855 | BUILDING_MODEL is normalised out: `translateModelCallBackendOB` sets it on | ||
| 856 | itself and the scripting wrapper's `saveFlags` clears it again, so a snapshot | ||
| 857 | taken during a translation never matches one taken before the next. Another | ||
| 858 | flag a translation sets on itself would only make the export miss, which is | ||
| 859 | the safe direction." | ||
| 860 | output array<Boolean> debugFlags; | ||
| 861 | output array<Flags.FlagData> configFlags; | ||
| 862 | protected | ||
| 863 | Integer buildingModel; | ||
| 864 | algorithm | ||
| 865 | ✗ | Flags.FLAGS(debugFlags = debugFlags, configFlags = configFlags) := Flags.getFlags(); | |
| 866 | ✗ | debugFlags := arrayCopy(debugFlags); | |
| 867 | ✗ | configFlags := arrayCopy(configFlags); | |
| 868 | Flags.CONFIG_FLAG(index = buildingModel) := Flags.BUILDING_MODEL; | ||
| 869 | ✗ | arrayUpdate(configFlags, buildingModel, Flags.BOOL_FLAG(false)); | |
| 870 | end fmuTranslationFlags; | ||
| 871 | |||
| 872 | public function keepFmuTranslation | ||
| 873 | "Remember this translation for the buildModelFMU that follows. The Absyn program | ||
| 874 | is kept by reference, since its identity is exactly the staleness test." | ||
| 875 | input SimCode.SimCode simCode; | ||
| 876 | input String FMUVersion; | ||
| 877 | input String FMUType; | ||
| 878 | input Absyn.Path className; | ||
| 879 | protected | ||
| 880 | array<Boolean> debugFlags; | ||
| 881 | array<Flags.FlagData> configFlags; | ||
| 882 | algorithm | ||
| 883 | ✗ | (debugFlags, configFlags) := fmuTranslationFlags(); | |
| 884 | ✗ | setGlobalRoot(Global.fmuTranslation, SOME(FMU_TRANSLATION(simCode, FMUVersion, fmuTranslationKind(FMUType), | |
| 885 | className, SymbolTable.getAbsyn(), debugFlags, configFlags))); | ||
| 886 | end keepFmuTranslation; | ||
| 887 | |||
| 888 | protected function sameFmuSettings | ||
| 889 | "Whether two SimulationSettings describe the same translation. `method` is left | ||
| 890 | out: it reaches the model kernel and not modelDescription.xml, and the emitter | ||
| 891 | re-lowers the kernel for a method the kept one was not built with — so choosing | ||
| 892 | a Co-Simulation solver at export costs a lowering rather than a translation." | ||
| 893 | input Option<SimCode.SimulationSettings> a; | ||
| 894 | input Option<SimCode.SimulationSettings> b; | ||
| 895 | output Boolean same; | ||
| 896 | protected | ||
| 897 | SimCode.SimulationSettings x, y; | ||
| 898 | algorithm | ||
| 899 | same := match (a, b) | ||
| 900 | case (SOME(x), SOME(y)) | ||
| 901 | algorithm | ||
| 902 | ✗ | x.method := ""; | |
| 903 | ✗ | y.method := ""; | |
| 904 | ✗ | then valueEq(x, y); | |
| 905 | case (NONE(), NONE()) then true; | ||
| 906 | else false; | ||
| 907 | end match; | ||
| 908 | end sameFmuSettings; | ||
| 909 | |||
| 910 | public function fmuTranslationFor | ||
| 911 | "The kept translation when it is the one this export needs: same class, same FMI | ||
| 912 | version, same kind of interface and the same experiment, translated from the | ||
| 913 | program still loaded and under the flags still set. NONE() otherwise, and then | ||
| 914 | the caller translates." | ||
| 915 | input String FMUVersion; | ||
| 916 | input String FMUType; | ||
| 917 | input Absyn.Path className; | ||
| 918 | input Option<SimCode.SimulationSettings> settings; | ||
| 919 | output Option<SimCode.SimCode> simCode = NONE(); | ||
| 920 | protected | ||
| 921 | array<Boolean> debugFlags; | ||
| 922 | array<Flags.FlagData> configFlags; | ||
| 923 | Option<FmuTranslation> kept = getGlobalRoot(Global.fmuTranslation) "getGlobalRoot is untyped: the match subject needs a declared type"; | ||
| 924 | algorithm | ||
| 925 | () := match kept | ||
| 926 | local | ||
| 927 | FmuTranslation t; | ||
| 928 | case SOME(t as FMU_TRANSLATION()) | ||
| 929 | guard AbsynUtil.pathEqual(t.className, className) | ||
| 930 | and t.FMUVersion == FMUVersion | ||
| 931 | and t.kind == fmuTranslationKind(FMUType) | ||
| 932 | and referenceEq(t.ast, SymbolTable.getAbsyn()) | ||
| 933 | and sameFmuSettings(t.simCode.simulationSettingsOpt, settings) | ||
| 934 | algorithm | ||
| 935 | ✗ | (debugFlags, configFlags) := fmuTranslationFlags(); | |
| 936 | ✗ | if Array.isEqual(t.debugFlags, debugFlags) and Array.isEqual(t.configFlags, configFlags) then | |
| 937 | ✗ | simCode := SOME(t.simCode); | |
| 938 | end if; | ||
| 939 | then (); | ||
| 940 | else (); | ||
| 941 | end match; | ||
| 942 | end fmuTranslationFor; | ||
| 943 | |||
| 944 | public function wasmFMUSimulationFlagsJson | ||
| 945 | "The resources/<prefix>_flags.json a wasm FMU ships, the same one the C export | ||
| 946 | does. The emitter applies its `s` flag at export, since the component is linked | ||
| 947 | against one solver and cannot pick another at run time." | ||
| 948 | input SimCode.SimCode simCode; | ||
| 949 | output String json; | ||
| 950 | algorithm | ||
| 951 | json := match simCode.fmiSimulationFlags | ||
| 952 | local | ||
| 953 | SimCode.FmiSimulationFlags flags; | ||
| 954 | String path; | ||
| 955 | ✗ | case SOME(SimCode.FMI_SIMULATION_FLAGS_FILE(path=path)) then System.readFile(path); | |
| 956 | ✗ | case SOME(flags as SimCode.FMI_SIMULATION_FLAGS()) then | |
| 957 | Tpl.textString(CodegenFMUCommon.fmuSimulationFlagsFile(Tpl.emptyTxt, flags)); | ||
| 958 | else ""; | ||
| 959 | end match; | ||
| 960 | end wasmFMUSimulationFlagsJson; | ||
| 961 | |||
| 962 | public function emitWasmFMU | ||
| 963 | "Render the FMI metadata and write the self-contained <name>.fmu: link the model | ||
| 964 | kernel with the adapter into an fmi-ls-wasm component (host-free, all in Rust — | ||
| 965 | no external wasm-merge/zip). Called either straight after the translation or, | ||
| 966 | when translateModelFMU already did that translation, against the SimCode it | ||
| 967 | kept — so the export is a link and an XML render rather than a second | ||
| 968 | translation." | ||
| 969 | input SimCode.SimCode simCode; | ||
| 970 | input String FMUVersion; | ||
| 971 | input String FMUType; | ||
| 972 | input Absyn.Program program; | ||
| 973 | protected | ||
| 974 | String guid, modelDescriptionStr, simulationFlagsJson; | ||
| 975 | String lsDaeManifestStr = ""; | ||
| 976 | String fmutmp = "", terminalsDir = "", documentationDir = ""; | ||
| 977 | list<SimCode.FmiTerminal> terminals; | ||
| 978 | // `--fmuDirectory`: an export for an OpenModelica importer, which reads the | ||
| 979 | // model description and the artifact and nothing else. | ||
| 980 | Boolean bareExport = Flags.getConfigBool(Flags.FMU_DIRECTORY); | ||
| 981 | algorithm | ||
| 982 | // The templates look the SimCode up through getSimCode(); an export off a kept | ||
| 983 | // translation has no callTargetTemplatesFMU around it to have set it. | ||
| 984 | ✗ | setGlobalRoot(Global.optionSimCode, SOME(simCode)); | |
| 985 | ✗ | SimCodeFunctionUtil.setTrivialRecords(simCode.recordDecls); | |
| 986 | ✗ | guid := System.getUUIDStr(); | |
| 987 | ✗ | if not bareExport then | |
| 988 | // The C export's scratch directory, which terminalsAndIcons/ and documentation/ | ||
| 989 | // are staged in; old contents would be shipped verbatim. | ||
| 990 | ✗ | fmutmp := Util.hashFileNamePrefix(simCode.fileNamePrefix) + ".fmutmp"; | |
| 991 | ✗ | if System.directoryExists(fmutmp) and not System.removeDirectory(fmutmp) then | |
| 992 | ✗ | Error.addInternalError("Failed to remove directory: " + fmutmp, sourceInfo()); | |
| 993 | ✗ | fail(); | |
| 994 | end if; | ||
| 995 | end if; | ||
| 996 | // The same templates the C target uses, so the XML is byte-identical to it. No | ||
| 997 | // sourceFiles: a wasm FMU carries no C. The XML declaration comes from | ||
| 998 | // fmuModelDescriptionFile, which the C target reaches these through. | ||
| 999 | ✗ | System.realtimeTick(ClockIndexes.RT_CLOCK_FMU_TEMPLATES); | |
| 1000 | ✗ | if FMI.isFMIVersion20(FMUVersion) then | |
| 1001 | ✗ | modelDescriptionStr := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + Tpl.textString( | |
| 1002 | CodegenFMU2.fmiModelDescription(Tpl.emptyTxt, simCode, guid, FMUType, {})); | ||
| 1003 | else | ||
| 1004 | ✗ | modelDescriptionStr := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + Tpl.textString( | |
| 1005 | CodegenFMU3.fmiModelDescription(Tpl.emptyTxt, simCode, guid, FMUType, {})); | ||
| 1006 | ✗ | ExecStat.execStat("FMU modelDescription.xml"); | |
| 1007 | ✗ | if isSome(simCode.daeModeData) and FMI.isFMIMEType(FMUType) then | |
| 1008 | ✗ | lsDaeManifestStr := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + Tpl.textString( | |
| 1009 | CodegenFMU3.fmiLsDaeManifest(Tpl.emptyTxt, simCode)); | ||
| 1010 | ✗ | Error.addMessage(Error.FMU_EXPORT_FMI_LS_DAE_DRAFT, | |
| 1011 | {SimCodeCodegenUtil.FMI_LS_DAE_VERSION, SimCodeUtil.FMI_LS_DAE_DRAFT_DATE, SimCodeUtil.FMI_LS_DAE_DRAFT_COMMIT}); | ||
| 1012 | ✗ | ExecStat.execStat("FMU fmi-ls-manifest.xml"); | |
| 1013 | end if; | ||
| 1014 | // terminalsAndIcons/ by the C target's route: SimCode writes the XML, then the | ||
| 1015 | // OMGraphics renderer adds the <GraphicalRepresentation> and the icons beside it. | ||
| 1016 | ✗ | if not bareExport then | |
| 1017 | ✗ | terminalsDir := fmutmp + "/terminalsAndIcons/"; | |
| 1018 | ✗ | terminals := SimCodeCodegenUtil.getFMI3Terminals(simCode); | |
| 1019 | ✗ | if not listEmpty(terminals) then | |
| 1020 | ✗ | Util.createDirectoryTree(terminalsDir); | |
| 1021 | ✗ | System.writeFile(terminalsDir + "terminalsAndIcons.xml", | |
| 1022 | Tpl.textString(CodegenFMU3.fmiTerminalsAndIcons(Tpl.emptyTxt, terminals))); | ||
| 1023 | end if; | ||
| 1024 | ✗ | CevalScriptBackend.generateFMI3GraphicalRepresentation(simCode.modelInfo.name, fmutmp, simCode.fileNamePrefix); | |
| 1025 | ✗ | if not System.directoryExists(terminalsDir) then | |
| 1026 | terminalsDir := ""; | ||
| 1027 | end if; | ||
| 1028 | ✗ | ExecStat.execStat("FMU terminalsAndIcons.xml"); | |
| 1029 | end if; | ||
| 1030 | end if; | ||
| 1031 | // After the icons, so the page references the one terminalsAndIcons/ holds. | ||
| 1032 | ✗ | if not bareExport and writeFMUDocumentation(program, simCode, FMUVersion, fmutmp) then | |
| 1033 | ✗ | documentationDir := fmutmp + "/documentation/"; | |
| 1034 | ✗ | ExecStat.execStat("FMU documentation"); | |
| 1035 | end if; | ||
| 1036 | ✗ | System.realtimeAccumulate(ClockIndexes.RT_CLOCK_FMU_TEMPLATES); | |
| 1037 | ✗ | simulationFlagsJson := wasmFMUSimulationFlagsJson(simCode); | |
| 1038 | ✗ | if FMI.isFMIMEType(FMUType) and FMI.isFMICSType(FMUType) then | |
| 1039 | ✗ | CodegenWasmJit.emitMeCsFmu(simCode, simCode.fmuTargetName + ".fmu", guid, modelDescriptionStr, lsDaeManifestStr, documentationDir, terminalsDir, simulationFlagsJson); | |
| 1040 | elseif FMI.isFMICSType(FMUType) then | ||
| 1041 | ✗ | CodegenWasmJit.emitCsFmu(simCode, simCode.fmuTargetName + ".fmu", guid, modelDescriptionStr, lsDaeManifestStr, documentationDir, terminalsDir, simulationFlagsJson); | |
| 1042 | else | ||
| 1043 | ✗ | CodegenWasmJit.emitMeFmu(simCode, simCode.fmuTargetName + ".fmu", guid, modelDescriptionStr, lsDaeManifestStr, documentationDir, terminalsDir, simulationFlagsJson); | |
| 1044 | end if; | ||
| 1045 | ✗ | setGlobalRoot(Global.optionSimCode, NONE()); | |
| 1046 | end emitWasmFMU; | ||
| 1047 | |||
| 1048 | protected function callTargetTemplatesFMU | ||
| 1049 | "Generate target code by passing the SimCode data structure to templates." | ||
| 1050 | input SimCode.SimCode inSimCode; | ||
| 1051 | input String target; | ||
| 1052 | input String FMUVersion; | ||
| 1053 | input String FMUType; | ||
| 1054 | input Absyn.Program program; | ||
| 1055 | input Boolean translateOnly = false "keep the translation in memory instead of writing the FMU"; | ||
| 1056 | protected | ||
| 1057 | SimCode.SimCode simCode = SimCodeUtil.addFMI3Figures(inSimCode, FMUVersion); | ||
| 1058 | // "wasm" is the standalone simulation target and has no FMU export of its own; | ||
| 1059 | // an FMU built under it is the same fmi-ls-wasm component "wasm-jit" emits. | ||
| 1060 | String fmuTarget = if target == "wasm" then "wasm-jit" else target; | ||
| 1061 | algorithm | ||
| 1062 | |||
| 1063 | 91 | setGlobalRoot(Global.optionSimCode, SOME(simCode)); | |
| 1064 | 91 | SimCodeFunctionUtil.setTrivialRecords(simCode.recordDecls); | |
| 1065 | () := match (simCode,fmuTarget) | ||
| 1066 | local | ||
| 1067 | String str, newdir, newpath, resourcesDir, dirname, fileName; | ||
| 1068 | String fmutmp; | ||
| 1069 | String guid; | ||
| 1070 | list<SimCode.FmiTerminal> terminals; | ||
| 1071 | Boolean b; | ||
| 1072 | Boolean needSundials = false; | ||
| 1073 | String fileprefix, fileNamePrefixHash; | ||
| 1074 | String install_include_omc_dir, install_include_omc_c_dir, install_share_buildproject_dir, install_fmu_sources_dir, fmu_tmp_sources_dir; | ||
| 1075 | String cmakelistsStr, needCvode, cvodeDirectory; | ||
| 1076 | String modelDefinesHeaderStr; | ||
| 1077 | String fmu_dummy_include_defines; | ||
| 1078 | String needModelicaExternalC, cmakeCode; | ||
| 1079 | list<String> model_desc_src_files, fmi2HeaderFiles, modelica_standard_table_sources; | ||
| 1080 | list<String> dgesv_sources, cminpack_sources, simrt_c_sundials_sources, simrt_linear_solver_sources, simrt_non_linear_solver_sources; | ||
| 1081 | list<String> simrt_mixed_solver_sources, fmi_export_files, model_gen_files, model_all_gen_files, shared_source_files; | ||
| 1082 | list<String> rust_crate_roots; | ||
| 1083 | list<String> simrt_c_sources; | ||
| 1084 | Boolean rustRuntime, rustFmi; | ||
| 1085 | SimCode.VarInfo varInfo; | ||
| 1086 | case (SimCode.SIMCODE(),"wasm-jit") | ||
| 1087 | algorithm | ||
| 1088 | // Keep the translation either way: the export that follows — a second one | ||
| 1089 | // of this model, or the first after a translateModelFMU — then links an | ||
| 1090 | // adapter onto this kernel instead of translating again. | ||
| 1091 | ✗ | if translateOnly then | |
| 1092 | ✗ | CodegenWasmJit.translateFmu(simCode, FMUType, wasmFMUSimulationFlagsJson(simCode)); | |
| 1093 | else | ||
| 1094 | ✗ | emitWasmFMU(simCode, FMUVersion, FMUType, program); | |
| 1095 | end if; | ||
| 1096 | ✗ | keepFmuTranslation(simCode, FMUVersion, FMUType, simCode.modelInfo.name); | |
| 1097 | then (); | ||
| 1098 | |||
| 1099 | case (SimCode.SIMCODE(),"C") | ||
| 1100 | algorithm | ||
| 1101 | 82 | fileNamePrefixHash := Util.hashFileNamePrefix(simCode.fileNamePrefix); | |
| 1102 | 82 | fmutmp := fileNamePrefixHash + ".fmutmp"; | |
| 1103 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
|
82 | if System.directoryExists(fmutmp) then |
| 1104 | ✗ | if not System.removeDirectory(fmutmp) then | |
| 1105 | ✗ | Error.addInternalError("Failed to remove directory: " + fmutmp, sourceInfo()); | |
| 1106 | ✗ | fail(); | |
| 1107 | end if; | ||
| 1108 | end if; | ||
| 1109 | 82 | Util.createDirectoryTree(fmutmp + "/sources/include/"); | |
| 1110 | 82 | resourcesDir := fmutmp + "/resources/"; | |
| 1111 | 82 | Util.createDirectoryTree(resourcesDir); | |
| 1112 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | for path in simCode.modelInfo.resourcePaths loop |
| 1113 | ✗ | dirname := System.dirname(path); | |
| 1114 | newpath := path; | ||
| 1115 | // on windows, remove ":" from the path! | ||
| 1116 | if Autoconf.os == "Windows_NT" then | ||
| 1117 | dirname := System.stringReplace(dirname, ":", ""); | ||
| 1118 | newpath := System.stringReplace(newpath, ":", ""); | ||
| 1119 | end if; | ||
| 1120 | ✗ | newdir := resourcesDir + dirname; | |
| 1121 | ✗ | newpath := resourcesDir + newpath; | |
| 1122 | ✗ | if System.regularFileExists(newpath) or System.directoryExists(newpath) then | |
| 1123 | /* Already copied. Maybe one resource loaded a library and this one only a file in the directory */ | ||
| 1124 | ✗ | continue; | |
| 1125 | end if; | ||
| 1126 | ✗ | Util.createDirectoryTree(newdir); | |
| 1127 | // copy the file or directory | ||
| 1128 | ✗ | if not System.copyPath(path, newpath) then | |
| 1129 | ✗ | Error.addInternalError("Failed to copy path " + path + " to " + resourcesDir + dirname, sourceInfo()); | |
| 1130 | end if; | ||
| 1131 | end for; | ||
| 1132 | |||
| 1133 | // Add optional _flags.json to resources | ||
| 1134 | () := match simCode.fmiSimulationFlags | ||
| 1135 | local | ||
| 1136 | String pathToFlagsJson; | ||
| 1137 | case SOME(SimCode.FMI_SIMULATION_FLAGS_FILE(path=pathToFlagsJson)) | ||
| 1138 | algorithm | ||
| 1139 | needSundials := true; | ||
| 1140 | ✗ | if not System.copyFile(pathToFlagsJson, resourcesDir + simCode.fileNamePrefix + "_flags.json") then | |
| 1141 | ✗ | Error.addInternalError("Failed to copy " + pathToFlagsJson + " to " + resourcesDir + simCode.fileNamePrefix + "_flags.json", sourceInfo()); | |
| 1142 | end if; | ||
| 1143 | then(); | ||
| 1144 | else | ||
| 1145 | then(); | ||
| 1146 | end match; | ||
| 1147 | |||
| 1148 | // -d=visxml scene as a resource, referenced by the <Visualization> | ||
| 1149 | // annotation, plus the CAD files it references (a portable FMU cannot rely | ||
| 1150 | // on the importer having the libraries the modelica:// URIs point at). | ||
| 1151 |
1/4✗ Branch 2 not taken.
✓ Branch 3 taken 82 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
82 | if Flags.isSet(Flags.VISUAL_XML) and System.regularFileExists(simCode.fileNamePrefix + "_visual.xml") then |
| 1152 | ✗ | if not System.copyFile(simCode.fileNamePrefix + "_visual.xml", resourcesDir + simCode.fileNamePrefix + "_visual.xml") then | |
| 1153 | ✗ | Error.addInternalError("Failed to copy " + simCode.fileNamePrefix + "_visual.xml to " + resourcesDir, sourceInfo()); | |
| 1154 | end if; | ||
| 1155 | ✗ | for cad in visualizationCadFiles(simCode.fileNamePrefix + "_visual.xml") loop | |
| 1156 | ✗ | if System.regularFileExists(cad) and | |
| 1157 | not System.copyFile(cad, resourcesDir + System.basename(cad)) then | ||
| 1158 | ✗ | Error.addInternalError("Failed to copy CAD file " + cad + " to " + resourcesDir, sourceInfo()); | |
| 1159 | end if; | ||
| 1160 | end for; | ||
| 1161 | end if; | ||
| 1162 | |||
| 1163 | 82 | SerializeSparsityPattern.serialize(simCode); | |
| 1164 |
2/2✓ Branch 0 taken 634 times.
✓ Branch 1 taken 82 times.
|
716 | for jac in simCode.jacobianMatrices loop |
| 1165 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 529 times.
|
634 | if not listEmpty(jac.sparsity) then |
| 1166 | 105 | fileName := simCode.fileNamePrefix + "_Jac" + jac.matrixName + ".bin"; | |
| 1167 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 105 times.
|
105 | if not System.rename(fileName, resourcesDir + fileName) then |
| 1168 | ✗ | Error.addInternalError("Failed to move " + simCode.fileNamePrefix + "_Jac" + jac.matrixName + ".bin file", sourceInfo()); | |
| 1169 | end if; | ||
| 1170 | end if; | ||
| 1171 | end for; | ||
| 1172 | |||
| 1173 | 82 | SerializeModelInfo.serialize(simCode, Flags.isSet(Flags.INFO_XML_OPERATIONS)); | |
| 1174 | 82 | str := fmutmp + "/sources/" + simCode.fileNamePrefix; | |
| 1175 |
3/4✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 80 times.
|
82 | if FMUVersion == "1.0" then |
| 1176 | 2 | b := System.covertTextFileToCLiteral(simCode.fileNamePrefix + "_info.json", str + "_info.c", Flags.getConfigString(Flags.TARGET)); | |
| 1177 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if not b then |
| 1178 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {"System.covertTextFileToCLiteral failed. Could not write " + str + "_info.c\n"}); | |
| 1179 | ✗ | fail(); | |
| 1180 | end if; | ||
| 1181 | else | ||
| 1182 | // Add _info.json file to resources/ directory if neither --fmiFilter=blackBox nor --fmiFilter=protected are used | ||
| 1183 |
4/4✓ Branch 1 taken 79 times.
✓ Branch 2 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 78 times.
|
80 | if Flags.getConfigEnum(Flags.FMI_FILTER) <> Flags.FMI_BLACKBOX and Flags.getConfigEnum(Flags.FMI_FILTER) <> Flags.FMI_PROTECTED then |
| 1184 | 1 | fileName := simCode.fileNamePrefix + "_info.json"; | |
| 1185 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
|
1 | if not System.rename(fileName, resourcesDir + fileName) then |
| 1186 | ✗ | Error.addInternalError("Failed to move " + simCode.fileNamePrefix + "_info.json file", sourceInfo()); | |
| 1187 | end if; | ||
| 1188 | end if; | ||
| 1189 | end if; | ||
| 1190 | |||
| 1191 | 82 | SimCodeCodegenUtil.resetFunctionIndex(); | |
| 1192 | 82 | varInfo := simCode.modelInfo.varInfo; | |
| 1193 | |||
| 1194 | |||
| 1195 | 82 | install_include_omc_dir := Settings.getInstallationDirectoryPath() + "/include/omc/"; | |
| 1196 | 82 | install_include_omc_c_dir := install_include_omc_dir + "c/"; | |
| 1197 | 82 | install_share_buildproject_dir := Settings.getInstallationDirectoryPath() + "/share/omc/runtime/c/fmi/buildproject/"; | |
| 1198 | 82 | install_fmu_sources_dir := Settings.getInstallationDirectoryPath() + RuntimeSources.fmu_sources_dir; | |
| 1199 | 82 | fmu_tmp_sources_dir := fmutmp + "/sources/"; | |
| 1200 | |||
| 1201 | // --simCodeTarget=C: libSimulationRuntimeRust replaces everything | ||
| 1202 | // libSimulationRuntimeC provided, the solvers included, so the FMU only | ||
| 1203 | // compiles the libOpenModelicaRuntimeC half from C. The headers are the | ||
| 1204 | // same either way -- the generated code and the FMI interface include | ||
| 1205 | // them whichever runtime is behind them. | ||
| 1206 | 82 | rustRuntime := Config.simCodeRustRuntime(); | |
| 1207 | // libSimulationRuntimeRust also serves the FMI 2.0 and 3.0 C API, with its | ||
| 1208 | // own Co-Simulation solvers, in place of fmi-export/*.c and cvode_solver.c. | ||
| 1209 | rustFmi := rustRuntime; | ||
| 1210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | simrt_c_sources := if rustRuntime then RuntimeSources.simrt_c_runtime_sources else RuntimeSources.simrt_c_sources; |
| 1211 | // Only worth carrying if the sources survive into the archive; this build | ||
| 1212 | // links the installed archive either way. | ||
| 1213 |
4/6✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 75 times.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
|
82 | if rustRuntime and Flags.getConfigEnum(Flags.FMI_SOURCES) <> Flags.FMI_SOURCES_NONE |
| 1214 | and Flags.getConfigEnum(Flags.FMI_FILTER) <> Flags.FMI_BLACKBOX then | ||
| 1215 | 7 | copyFmuRustSources(fmutmp); | |
| 1216 | 7 | rust_crate_roots := fmuRustCrateRoots(fmutmp + "/sources/", "rust"); | |
| 1217 | else | ||
| 1218 | rust_crate_roots := {}; | ||
| 1219 | end if; | ||
| 1220 | |||
| 1221 | // The simrt c headers are in the include/omc/c directory. | ||
| 1222 | 82 | copyFiles(RuntimeSources.simrt_c_headers, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1223 | // The simrt C source files are installed to the folder specified by RuntimeSources.fmu_sources_dir. Copy them from there. | ||
| 1224 | 82 | copyFiles(simrt_c_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1225 | |||
| 1226 | /* | ||
| 1227 | * fix issue https://github.com/OpenModelica/OpenModelica/issues/13719 | ||
| 1228 | * copy the fmu runtime external solver sources to support source code cross compilation | ||
| 1229 | */ | ||
| 1230 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | if rustRuntime then |
| 1231 | // dgesv and CMinpack are the Rust runtime's own openmodelica_lapack and | ||
| 1232 | // minpack crates there. | ||
| 1233 | dgesv_sources := {}; | ||
| 1234 | cminpack_sources := {}; | ||
| 1235 | else | ||
| 1236 | // The dgesv headers are in the RuntimeSources.fmu_sources_dir for now since they are not properly installed in the include folder | ||
| 1237 | ✗ | copyFiles(RuntimeSources.dgesv_headers, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1238 | ✗ | copyFiles(RuntimeSources.dgesv_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1239 | dgesv_sources := RuntimeSources.dgesv_sources; | ||
| 1240 | |||
| 1241 | // Add CMinpack sources to FMU | ||
| 1242 | ✗ | copyFiles(RuntimeSources.cminpack_headers, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1243 | ✗ | copyFiles(RuntimeSources.cminpack_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1244 | cminpack_sources := RuntimeSources.cminpack_sources; | ||
| 1245 | end if; | ||
| 1246 | |||
| 1247 | // Check if the sundials files are needed. | ||
| 1248 | // The in-FMU CVODE integrator (s:cvode) is a Co-Simulation feature: cvode_solver_fmi_step() | ||
| 1249 | // drives the fmi2 ModelInstance / fmi2DoStep API and cvode_solver.h includes | ||
| 1250 | // fmu2_model_interface.h. OpenModelica only exports FMI 1.0 as Model Exchange (CS export | ||
| 1251 | // requires FMI 2.0, see FMI.canExportFMU); a model-exchange FMU is integrated by the | ||
| 1252 | // importer, and the FMI 1.0 export only ships fmu1_model_interface.h, so copying the | ||
| 1253 | // sundials sources there breaks compilation (fatal error: | ||
| 1254 | // 'fmi-export/fmu2_model_interface.h' file not found). See issue #15838. | ||
| 1255 |
3/4✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 80 times.
|
82 | if FMUVersion == "1.0" then |
| 1256 | // The in-FMU CVODE integrator does not apply to a model-exchange FMU; warn that the | ||
| 1257 | // 's:cvode' flag is ignored and do not pull in the sundials sources (they would not | ||
| 1258 | // compile for FMI 1.0). | ||
| 1259 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | if SimCodeUtil.cvodeFmiFlagIsSet(simCode.fmiSimulationFlags) then |
| 1260 | 1 | Error.addCompilerWarning("OpenModelica exports FMI 1.0 as Model Exchange only (Co-Simulation export requires FMI 2.0). A model-exchange FMU is integrated by the importer, so the in-FMU CVODE integrator does not apply. The 's:cvode' simulation flag is ignored for this FMI 1.0 export."); | |
| 1261 | end if; | ||
| 1262 | simrt_c_sundials_sources := {}; | ||
| 1263 | elseif not rustFmi and SimCodeUtil.cvodeFmiFlagIsSet(simCode.fmiSimulationFlags) then | ||
| 1264 | // The sundials headers are in the include directory. | ||
| 1265 | ✗ | copyFiles(RuntimeSources.sundials_headers, source=install_include_omc_dir, destination=fmu_tmp_sources_dir); | |
| 1266 | ✗ | copyFiles(RuntimeSources.simrt_c_sundials_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1267 | ✗ | simrt_c_sundials_sources := RuntimeSources.simrt_c_sundials_sources; | |
| 1268 | else | ||
| 1269 | simrt_c_sundials_sources := {}; | ||
| 1270 | end if; | ||
| 1271 | |||
| 1272 | |||
| 1273 |
3/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
82 | simrt_linear_solver_sources := if varInfo.numLinearSystems > 0 and not rustRuntime then RuntimeSources.simrt_linear_solver_sources else {}; |
| 1274 | 82 | copyFiles(simrt_linear_solver_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1275 | |||
| 1276 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
82 | simrt_non_linear_solver_sources := if varInfo.numNonLinearSystems > 0 and not rustRuntime then RuntimeSources.simrt_non_linear_solver_sources else {}; |
| 1277 | 82 | copyFiles(simrt_non_linear_solver_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1278 | |||
| 1279 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
82 | simrt_mixed_solver_sources := if varInfo.numMixedSystems > 0 and not rustRuntime then RuntimeSources.simrt_mixed_solver_sources else {}; |
| 1280 | 82 | copyFiles(simrt_mixed_solver_sources, source=install_fmu_sources_dir, destination=fmu_tmp_sources_dir); | |
| 1281 | |||
| 1282 | // This fmu export files of OMC are located in a very unexpected place. Right now they are in SimulationRuntime/fmi/export/openmodelica | ||
| 1283 | // and then then they are installed to include/omc/c/fmi-export for some reason. The source, install, and source fmu location | ||
| 1284 | // for these files should be made consistent. For now to avoid modifing things a lot they are left as they are and copied here. | ||
| 1285 |
4/6✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 80 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
82 | if FMUVersion == "1.0" and rustFmi then |
| 1286 | 2 | copyFiles(RuntimeSources.fmi1_rust_headers, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1287 | 2 | fmi_export_files := {}; | |
| 1288 | elseif FMUVersion == "1.0" then | ||
| 1289 | ✗ | copyFiles(RuntimeSources.fmi1Files, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1290 | ✗ | fmi_export_files := RuntimeSources.fmi1Files; | |
| 1291 | elseif FMUVersion == "3.0" then | ||
| 1292 | // FMI 3.0 export. fmu3_model_interface.c is built on top of the FMI 2.0 | ||
| 1293 | // ModelInstance and the generated per-base-type get/set helpers, so the | ||
| 1294 | // FMI 2.0 header (but not the FMI 2.0 interface .c) is required as well. | ||
| 1295 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if not rustFmi then |
| 1296 | ✗ | copyFiles(RuntimeSources.fmi3_sources, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1297 | end if; | ||
| 1298 | 15 | copyFiles(RuntimeSources.fmi3_headers, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | fmi_export_files := if rustFmi then {} else RuntimeSources.fmi3_sources; |
| 1300 | elseif rustFmi then | ||
| 1301 | 65 | copyFiles(RuntimeSources.fmi2_headers, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1302 | fmi_export_files := {}; | ||
| 1303 | else | ||
| 1304 | ✗ | copyFiles(RuntimeSources.fmi2_sources, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1305 | ✗ | copyFiles(RuntimeSources.fmi2_headers, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1306 | fmi_export_files := RuntimeSources.fmi2_sources; | ||
| 1307 | end if; | ||
| 1308 | |||
| 1309 | /* | ||
| 1310 | * fix issue https://github.com/OpenModelica/OpenModelica/issues/13213 | ||
| 1311 | * copy fmu reference headers directly to FMU sources, to support source code compilation | ||
| 1312 | */ | ||
| 1313 | fmi2HeaderFiles := {"fmi/fmi2Functions.h","fmi/fmi2FunctionTypes.h", "fmi/fmi2TypesPlatform.h", "fmi/fmiModelFunctions.h", "fmi/fmiModelTypes.h"}; | ||
| 1314 | 82 | copyFiles(fmi2HeaderFiles, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1315 | |||
| 1316 | // For FMI 3.0 also copy the FMI 3.0 reference headers into the FMU sources. | ||
| 1317 |
3/4✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 67 times.
|
82 | if FMUVersion == "3.0" then |
| 1318 | 15 | copyFiles({"fmi/fmi3Functions.h","fmi/fmi3FunctionTypes.h","fmi/fmi3PlatformTypes.h"}, source=install_include_omc_c_dir, destination=fmu_tmp_sources_dir); | |
| 1319 | // FMI 3.0 Terminals: create the terminalsAndIcons/ directory (the | ||
| 1320 | // CodegenFMU3 template writes terminalsAndIcons.xml into it) when the | ||
| 1321 | // model has connector-derived terminals. | ||
| 1322 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 7 times.
|
15 | if not listEmpty(SimCodeCodegenUtil.getFMI3Terminals(simCode)) then |
| 1323 | 8 | Util.createDirectoryTree(fmutmp + "/terminalsAndIcons/"); | |
| 1324 | end if; | ||
| 1325 | end if; | ||
| 1326 | |||
| 1327 | /* | ||
| 1328 | * fix issue fhttps://github.com/OpenModelica/OpenModelica/issues/13260 | ||
| 1329 | * Check if modelicaStandardTables source files are needed | ||
| 1330 | * this is not clear as of now, may be we should copy all the external C sources by default | ||
| 1331 | */ | ||
| 1332 | 82 | copyFiles(RuntimeSources.modelica_external_c_sources, source=install_include_omc_dir, destination=fmu_tmp_sources_dir); | |
| 1333 | 82 | copyFiles(RuntimeSources.modelica_external_c_headers, source=install_include_omc_dir, destination=fmu_tmp_sources_dir); | |
| 1334 | modelica_standard_table_sources := RuntimeSources.modelica_external_c_sources; | ||
| 1335 | |||
| 1336 |
6/8✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 80 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 80 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 65 times.
✓ Branch 10 taken 15 times.
|
147 | System.writeFile(fmutmp+"/sources/isfmi" + (if FMUVersion=="1.0" then "1" elseif FMUVersion=="3.0" then "3" else "2"), ""); |
| 1337 | |||
| 1338 |
4/4✓ Branch 0 taken 1886 times.
✓ Branch 1 taken 82 times.
✓ Branch 2 taken 1886 times.
✓ Branch 3 taken 82 times.
|
1968 | model_gen_files := list(simCode.fileNamePrefix + f for f in RuntimeSources.defaultFileSuffixes); |
| 1339 | |||
| 1340 | // I need to see some tests failing or something not working to make sense of what to add here | ||
| 1341 | 82 | shared_source_files := List.flatten({fmi_export_files, | |
| 1342 | simrt_c_sources, | ||
| 1343 | simrt_linear_solver_sources, | ||
| 1344 | simrt_non_linear_solver_sources, | ||
| 1345 | simrt_mixed_solver_sources | ||
| 1346 | }); | ||
| 1347 | |||
| 1348 | // check for fmiSources=false or --fmiFilter=blackBox | ||
| 1349 |
3/4✓ Branch 1 taken 7 times.
✓ Branch 2 taken 75 times.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
|
82 | if Flags.getConfigEnum(Flags.FMI_SOURCES) == Flags.FMI_SOURCES_NONE or Flags.getConfigEnum(Flags.FMI_FILTER) == Flags.FMI_BLACKBOX then |
| 1350 | model_desc_src_files := {}; // set the sourceFiles to empty, to remove the sources in modeldescription.xml | ||
| 1351 | else | ||
| 1352 | 49 | model_desc_src_files := List.flatten({List.sort(model_gen_files, Util.strcmpNoCaseBool), // order matters | |
| 1353 | List.sort(shared_source_files, Util.strcmpNoCaseBool), | ||
| 1354 | List.sort(dgesv_sources, Util.strcmpNoCaseBool), | ||
| 1355 | List.sort(cminpack_sources, Util.strcmpNoCaseBool), | ||
| 1356 | List.sort(simrt_c_sundials_sources, Util.strcmpNoCaseBool), | ||
| 1357 | List.sort(modelica_standard_table_sources, Util.strcmpNoCaseBool), | ||
| 1358 | rust_crate_roots | ||
| 1359 | }); | ||
| 1360 | end if; | ||
| 1361 | |||
| 1362 | 82 | Tpl.tplNoret(function CodegenFMU.translateModel(in_a_FMUVersion=FMUVersion, in_a_FMUType=FMUType, in_a_sourceFiles=model_desc_src_files), simCode); | |
| 1363 | |||
| 1364 | // FMI 3.0 graphical user annotations (issue #15686 task 9): the template | ||
| 1365 | // above wrote terminalsAndIcons.xml, this adds the icons beside it. Before | ||
| 1366 | // the documentation, which shows the icon it produced. | ||
| 1367 |
3/4✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 67 times.
|
82 | if FMUVersion == "3.0" then |
| 1368 | 15 | CevalScriptBackend.generateFMI3GraphicalRepresentation(simCode.modelInfo.name, fmutmp, simCode.fileNamePrefix); | |
| 1369 | end if; | ||
| 1370 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 74 times.
|
82 | if writeFMUDocumentation(program, simCode, FMUVersion, fmutmp) then |
| 1371 | 8 | ExecStat.execStat("FMU documentation"); | |
| 1372 | end if; | ||
| 1373 | |||
| 1374 | // Add the _part*.c files to the list of source files. We do not know how many of them there are until | ||
| 1375 | // we have called CodegenFMU.translateModel. Which means the list of source files passed to | ||
| 1376 | // CodegenFMU.translateModel above does not include them. Which means they are not listed in the | ||
| 1377 | // modelDescription.xml file. The way to fix that is to separate the generation of modelDescrition.xml | ||
| 1378 | // from CodegenFMU.translateModel. However, modelDescrition.xml wants to use the same GUID as the model code. | ||
| 1379 | // Which means the transateModel call should make its created GUID available outside of it. We can not simply | ||
| 1380 | // return the GUID from it (?) so there needs to be some more restructure needed. However, modelDescrition.xml | ||
| 1381 | // at the moment does does not list all the extra files anyway. So for now we leave it like this and make sure | ||
| 1382 | // the makefile gets them properly at least. | ||
| 1383 | 82 | model_all_gen_files := listAppend(model_gen_files, SimCodeUtil.getFunctionIndex()); | |
| 1384 | |||
| 1385 | // Copy CMakeLists.txt.in and replace @FMU_NAME_IN@ with fmu name | ||
| 1386 | 82 | System.copyFile(source = install_share_buildproject_dir + "CMakeLists.txt.in", | |
| 1387 | destination = fmu_tmp_sources_dir + "CMakeLists.txt"); | ||
| 1388 | 82 | cmakelistsStr := System.readFile(fmu_tmp_sources_dir + "CMakeLists.txt"); | |
| 1389 | /* | ||
| 1390 | https://github.com/OpenModelica/OpenModelica/issues/12916 | ||
| 1391 | use hashed fmu Strings for project Name in cmake to avoid long path issues | ||
| 1392 | */ | ||
| 1393 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMU_NAME_HASH_IN@", fileNamePrefixHash); | |
| 1394 | |||
| 1395 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMU_NAME_IN@", simCode.fileNamePrefix); // Name with underscored instead of dots | |
| 1396 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMU_TARGET_NAME@", simCode.fmuTargetName); // Name with dots | |
| 1397 | |||
| 1398 | // Include debugging symbols? | ||
| 1399 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
|
82 | if Flags.isSet(Flags.GEN_DEBUG_SYMBOLS) then |
| 1400 | ✗ | cmakelistsStr := System.stringReplace(cmakelistsStr, "@CMAKE_BUILD_TYPE@", "Debug"); | |
| 1401 | else | ||
| 1402 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@CMAKE_BUILD_TYPE@", "Release"); | |
| 1403 | end if; | ||
| 1404 | // Set CMake runtime dependencies level | ||
| 1405 | () := match Flags.getConfigString(Flags.FMU_RUNTIME_DEPENDS) | ||
| 1406 | local | ||
| 1407 | SemanticVersion.Version cmakeVersion; | ||
| 1408 | SemanticVersion.Version minimumVersion; | ||
| 1409 | case "default" algorithm | ||
| 1410 | 82 | cmakeVersion := SimCodeUtil.getCMakeVersion(); | |
| 1411 | minimumVersion := SemanticVersion.SEMVER(3, 21, 0, {}, {}); // v3.21.0 | ||
| 1412 |
1/2✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
|
82 | if SemanticVersion.compare(minimumVersion, cmakeVersion) <= 0 /* minimumVersion <= cmakeVersion */ then |
| 1413 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "\"modelica\""); | |
| 1414 | else | ||
| 1415 | ✗ | cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "\"none\""); | |
| 1416 | end if; | ||
| 1417 | then(); | ||
| 1418 | case "none" algorithm | ||
| 1419 | ✗ | cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "\"none\""); | |
| 1420 | then(); | ||
| 1421 | case "modelica" algorithm | ||
| 1422 | ✗ | cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "\"modelica\""); | |
| 1423 | then(); | ||
| 1424 | case "all" algorithm | ||
| 1425 | ✗ | cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUNTIME_DEPENDENCIES_LEVEL@", "\"all\""); | |
| 1426 | then(); | ||
| 1427 | else algorithm | ||
| 1428 | ✗ | Error.addCompilerError("Unsupported value " + Flags.getConfigString(Flags.FMU_RUNTIME_DEPENDS) + "for compiler flag 'fmuRuntimeDepends'."); | |
| 1429 | then(); | ||
| 1430 | end match; | ||
| 1431 | |||
| 1432 | // Add external libraries and includes | ||
| 1433 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMI_INTERFACE_HEADER_FILES_DIRECTORY@", "\"" + Settings.getInstallationDirectoryPath() + "/include/omc/c/fmi" + "\""); | |
| 1434 | // FMI 1.0 (Model Exchange only) never links CVODE into the FMU (see note above where the | ||
| 1435 | // sundials sources are skipped for FMI 1.0); keep NEED_CVODE=OFF so cvode_solver.c and | ||
| 1436 | // sundials_error.c are not compiled. See issue #15838. | ||
| 1437 |
4/6✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 80 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 80 times.
✗ Branch 6 not taken.
|
82 | if FMUVersion == "1.0" or rustFmi then |
| 1438 | 82 | (needCvode, cvodeDirectory) := ("OFF", "\"\""); | |
| 1439 | else | ||
| 1440 | ✗ | (needCvode, cvodeDirectory) := SimCodeUtil.getCmakeSundialsLinkCode(simCode.fmiSimulationFlags); | |
| 1441 | end if; | ||
| 1442 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@NEED_CVODE@", needCvode); | |
| 1443 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@CVODE_DIRECTORY@", cvodeDirectory); | |
| 1444 | // --simCodeTarget=C: name the installed archive, which is what this | ||
| 1445 | // installation builds the FMU against. Rebuilding the FMU elsewhere finds | ||
| 1446 | // no such file and falls back to the crates under sources/rust. | ||
| 1447 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
|
82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@OMC_RUST_SIMULATION_RUNTIME@", if rustRuntime then "ON" else "OFF"); |
| 1448 |
1/2✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
|
82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@RUST_SIM_RUNTIME_LIBRARY@", |
| 1449 | if rustRuntime | ||
| 1450 | then "\"${DOCKER_VOL_DIR}" + Settings.getInstallationDirectoryPath() + "/lib/${OM_LIBRARY_ARCH}/omc/libSimulationRuntimeRust.a\"" | ||
| 1451 | else "\"\""); | ||
| 1452 | 82 | (needModelicaExternalC, cmakeCode) := SimCodeUtil.getCmakeLinkLibrariesCode(simCode.makefileParams.libs); | |
| 1453 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@COMPILE_MODELICA_EXTERNAL_C@", needModelicaExternalC); | |
| 1454 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMU_ADDITIONAL_LIBS@", cmakeCode); | |
| 1455 | 82 | cmakelistsStr := System.stringReplace(cmakelistsStr, "@FMU_ADDITIONAL_INCLUDES@", SimCodeUtil.make2CMakeInclude(simCode.makefileParams.includes)); | |
| 1456 | |||
| 1457 | 82 | System.writeFile(fmu_tmp_sources_dir + "CMakeLists.txt", cmakelistsStr); | |
| 1458 | |||
| 1459 | // Note the external include directories of the model. Cross compiling in a | ||
| 1460 | // container needs them next to the external libraries inside the container. | ||
| 1461 | // Written as a dot file so it doesn't end up in the FMU archive. | ||
| 1462 | // See https://github.com/OpenModelica/OpenModelica/issues/9509 | ||
| 1463 |
4/4✓ Branch 1 taken 1 time.
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 82 times.
|
83 | System.writeFile(fmutmp + "/.external_include_dirs", |
| 1464 | stringDelimitList(list(SimCodeUtil.stripIncludeFlag(i) for i in simCode.makefileParams.includes), "\n")); | ||
| 1465 | |||
| 1466 | // Set model define include in the FMI model interface source. The | ||
| 1467 | // interface source includes a version-specific placeholder header | ||
| 1468 | // ("fmu2_dummy_model_defines.h" / "fmu3_dummy_model_defines.h") that is | ||
| 1469 | // replaced here with the generated model-specific "<model>_FMU.h". | ||
| 1470 | // Only for FMI 2.0+. FMI 1.0 includes fmu1_model_interface.c.inc directly into | ||
| 1471 | // the generated <model>_FMU.c (which already has the model defines), so there is | ||
| 1472 | // no standalone fmu2_model_interface.c to patch. See issue #15838. | ||
| 1473 |
4/6✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 80 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 80 times.
|
82 | if FMUVersion <> "1.0" and not rustFmi then |
| 1474 | ✗ | fmu_dummy_include_defines := (if FMUVersion == "2.0" then "fmu2" else "fmu3") + "_dummy_model_defines.h"; | |
| 1475 | ✗ | for fmuModelInterfaceFile in ( | |
| 1476 | if FMUVersion == "3.0" | ||
| 1477 | then {"fmi-export/fmu3_model_interface.c"} | ||
| 1478 | else {"fmi-export/fmu2_model_interface.c"}) loop | ||
| 1479 | ✗ | modelDefinesHeaderStr := System.readFile(fmu_tmp_sources_dir + fmuModelInterfaceFile); | |
| 1480 | ✗ | modelDefinesHeaderStr := System.stringReplace(modelDefinesHeaderStr, fmu_dummy_include_defines, "../" + simCode.fileNamePrefix + "_FMU.h"); | |
| 1481 | ✗ | System.writeFile(fmu_tmp_sources_dir + fmuModelInterfaceFile, modelDefinesHeaderStr); | |
| 1482 | end for; | ||
| 1483 | end if; | ||
| 1484 | |||
| 1485 | 82 | Tpl.closeFile(Tpl.tplCallWithFailError( | |
| 1486 | CodegenFMU.settingsfile, | ||
| 1487 | simCode, | ||
| 1488 | txt=Tpl.redirectToFile(Tpl.emptyTxt, fmutmp+"/sources/omc_simulation_settings.h"))); | ||
| 1489 | then (); | ||
| 1490 | case (_,"Cpp") | ||
| 1491 | algorithm | ||
| 1492 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if(Flags.isSet(Flags.HPCOM)) then |
| 1493 | ✗ | Tpl.tplNoret3(CodegenFMUCppHpcom.translateModel, simCode, FMUVersion, FMUType); | |
| 1494 | else | ||
| 1495 | 9 | Tpl.tplNoret(function CodegenFMUCpp.translateModel(in_a_FMUVersion=FMUVersion, in_a_FMUType=FMUType, in_a_sourceFiles={}), simCode); | |
| 1496 | // dump modelInstance.json so the FMU makefile can filter out the | ||
| 1497 | // requested extra annotations (see flag --fmiExtraAnnotations) | ||
| 1498 |
2/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
|
9 | if Flags.getConfigString(Flags.FMI_EXTRA_ANNOTATIONS) <> "" then |
| 1499 | ✗ | System.writeFile(simCode.fileNamePrefix + "_modelInstance.json", ValuesUtil.extractValueString(NFApi.getModelInstance(simCode.modelInfo.name, simCode.modelInfo.name, "", true))); | |
| 1500 | end if; | ||
| 1501 | end if; | ||
| 1502 | then (); | ||
| 1503 | else | ||
| 1504 | algorithm | ||
| 1505 | ✗ | str := "Unknown FMU template target: " + target; | |
| 1506 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {str}); | |
| 1507 | ✗ | then fail(); | |
| 1508 | end match; | ||
| 1509 | 91 | setGlobalRoot(Global.optionSimCode, NONE()); | |
| 1510 | end callTargetTemplatesFMU; | ||
| 1511 | |||
| 1512 | // `svgiconhead` is sized as the generated library documentation sizes it: a | ||
| 1513 | // little larger than the class name it sits beside, not a banner. | ||
| 1514 | protected constant String FMU_DOCUMENTATION_CSS = " | ||
| 1515 | :root { color-scheme: light dark; } | ||
| 1516 | body { margin: 0 auto; padding: 1.5rem; max-width: 50rem; line-height: 1.5; | ||
| 1517 | font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; } | ||
| 1518 | h1 { font-size: 1.5rem; margin: 0 0 .2rem; } | ||
| 1519 | .svgiconhead { height: 32px; width: 32px; object-fit: contain; vertical-align: middle; } | ||
| 1520 | .fmu-desc { margin: 0 0 1.5rem; font-style: italic; opacity: .8; } | ||
| 1521 | img { max-width: 100%; height: auto; } | ||
| 1522 | table { border-collapse: collapse; } | ||
| 1523 | td, th { border: 1px solid; border-color: color-mix(in srgb, currentColor 30%, transparent); | ||
| 1524 | padding: .2rem .5rem; } | ||
| 1525 | pre, code { font-family: ui-monospace, Menlo, Consolas, monospace; } | ||
| 1526 | "; | ||
| 1527 | |||
| 1528 | protected function writeFMUDocumentation | ||
| 1529 | "Write the FMU's documentation/ directory under `fmutmp`: a standalone HTML page | ||
| 1530 | built from the model's Documentation annotation | ||
| 1531 | (e.g) annotation(Documentation(info=\"<html> </html>\", | ||
| 1532 | revisions=\"<html> </html>\", | ||
| 1533 | __OpenModelica_infoHeader = \"<html> </html>\")) | ||
| 1534 | plus the modelica:// images it references, so the page needs nothing outside the | ||
| 1535 | FMU. FMI 3.0 pages also show the icon rendered to terminalsAndIcons/. | ||
| 1536 | False when the model carries no such annotation and nothing was written." | ||
| 1537 | input Absyn.Program program; | ||
| 1538 | input SimCode.SimCode simCode; | ||
| 1539 | input String FMUVersion; | ||
| 1540 | input String fmutmp; | ||
| 1541 | output Boolean written = false; | ||
| 1542 | protected | ||
| 1543 | String info, revisions, infoHeader, docDir, name, page, icon; | ||
| 1544 | algorithm | ||
| 1545 | 82 | (info, revisions, infoHeader) := ProgramUtil.getNamedAnnotationExp(simCode.modelInfo.name, program, Absyn.IDENT("Documentation"), SOME(("","","")),Interactive.getDocumentationAnnotationString); | |
| 1546 | |||
| 1547 |
4/6✓ Branch 0 taken 74 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 74 times.
✗ Branch 5 not taken.
|
82 | if (stringEmpty(info) and stringEmpty(revisions) and stringEmpty(infoHeader)) then |
| 1548 | 74 | return; | |
| 1549 | end if; | ||
| 1550 | |||
| 1551 | 8 | name := Util.escapeModelicaStringToXmlString(AbsynUtil.pathString(simCode.modelInfo.name)); | |
| 1552 | // terminalsAndIcons/ is FMI 3.0 only, and holds an icon only for a model whose | ||
| 1553 | // icon layer had something to draw. | ||
| 1554 |
4/6✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
|
8 | icon := if FMUVersion == "3.0" and System.regularFileExists(fmutmp + "/terminalsAndIcons/icon.svg") |
| 1555 | then "<img class=\"svgiconhead\" src=\"../terminalsAndIcons/icon.svg\" alt=\"\"> " else ""; | ||
| 1556 | |||
| 1557 |
1/2✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
|
8 | page := "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n" |
| 1558 | + "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n" | ||
| 1559 | + "<title>" + name + "</title>\n" | ||
| 1560 | + "<style>" + FMU_DOCUMENTATION_CSS + "</style>\n" | ||
| 1561 | + "</head>\n<body>\n" | ||
| 1562 | + "<h1>" + icon + name + "</h1>\n" | ||
| 1563 | + (if stringEmpty(simCode.modelInfo.description) then "" | ||
| 1564 | else "<p class=\"fmu-desc\">" + Util.escapeModelicaStringToXmlString(simCode.modelInfo.description) + "</p>\n") | ||
| 1565 | + fmuDocumentationSection("", infoHeader) | ||
| 1566 | + fmuDocumentationSection("Information", info) | ||
| 1567 | + fmuDocumentationSection("Revisions", revisions) | ||
| 1568 | + "</body>\n</html>\n"; | ||
| 1569 | |||
| 1570 | 8 | docDir := fmutmp + "/documentation/"; | |
| 1571 | 8 | Util.createDirectoryTree(docDir); | |
| 1572 | 8 | page := copyFMUDocumentationResources(program, page, docDir); | |
| 1573 |
2/4✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
|
16 | System.writeFile(docDir + (if FMUVersion == "1.0" then "_main.html" else "index.html"), page); |
| 1574 | written := true; | ||
| 1575 | end writeFMUDocumentation; | ||
| 1576 | |||
| 1577 | protected function fmuDocumentationSection | ||
| 1578 | "One section of the documentation page: nothing at all when the annotation | ||
| 1579 | string is empty, so a model documenting only `info` gets no dangling Revisions | ||
| 1580 | heading. The strings are whole HTML documents in Modelica; the page they go | ||
| 1581 | into already has its own <html>, so the wrapper is dropped rather than nested." | ||
| 1582 | input String heading "empty for an unheaded section"; | ||
| 1583 | input String html; | ||
| 1584 | output String section = ""; | ||
| 1585 | protected | ||
| 1586 | String body; | ||
| 1587 | Integer len; | ||
| 1588 | algorithm | ||
| 1589 | 24 | body := System.trim(html); | |
| 1590 | // Modelica writes these as \"<html> ... </html>\"; some models leave the tags off, | ||
| 1591 | // and \"<html></html>\" documents nothing at all. | ||
| 1592 |
2/2✓ Branch 2 taken 11 times.
✓ Branch 3 taken 13 times.
|
24 | if StringUtil.startsWith(System.tolower(body), "<html>") then |
| 1593 | 11 | len := stringLength(body); | |
| 1594 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | body := if len > 6 then substring(body, 7, len) else ""; |
| 1595 | 11 | len := stringLength(body); | |
| 1596 |
1/2✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
11 | if StringUtil.endsWith(System.tolower(body), "</html>") then |
| 1597 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | body := if len > 7 then substring(body, 1, len - 7) else ""; |
| 1598 | end if; | ||
| 1599 | 11 | body := System.trim(body); | |
| 1600 | end if; | ||
| 1601 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 11 times.
|
24 | if stringEmpty(body) then |
| 1602 | 13 | return; | |
| 1603 | end if; | ||
| 1604 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | section := (if stringEmpty(heading) then "" else "<h2>" + heading + "</h2>\n") + body + "\n"; |
| 1605 | end fmuDocumentationSection; | ||
| 1606 | |||
| 1607 | protected function copyFMUDocumentationResources | ||
| 1608 | "Copy every modelica:// resource the documentation references into `docDir` and | ||
| 1609 | point the page at the copy, so it displays without the library it came from. The | ||
| 1610 | URI keeps its own path (modelica://Modelica/Resources/Images/x.png becomes | ||
| 1611 | documentation/Modelica/Resources/Images/x.png), which keeps two same-named images | ||
| 1612 | in different libraries apart. Only URIs naming a file are touched, so a | ||
| 1613 | modelica://Some.Class link is left alone." | ||
| 1614 | input Absyn.Program program; | ||
| 1615 | input output String page; | ||
| 1616 | input String docDir; | ||
| 1617 | protected | ||
| 1618 | list<String> uris; | ||
| 1619 | String source, classname, relative, target; | ||
| 1620 | algorithm | ||
| 1621 | // A URI cannot contain any of these unencoded, so tokenizing on them lifts the | ||
| 1622 | // URIs out of the markup without needing to parse it. | ||
| 1623 |
6/6✓ Branch 2 taken 1986 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 1991 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 8 times.
|
1999 | uris := List.uniqueOnTrue(list(u for u guard StringUtil.startsWith(u, "modelica://") |
| 1624 | in System.strtok(page, "\"'<> \t\n\r")), stringEq); | ||
| 1625 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
|
13 | for uri in uris loop |
| 1626 | try | ||
| 1627 | 5 | (_, classname, relative) := System.uriToClassAndPath(uri); | |
| 1628 | 5 | source := ProgramUtil.getFullPathFromUri(program, uri, false); | |
| 1629 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 time.
|
5 | if System.regularFileExists(source) then |
| 1630 | 4 | target := classname + relative; | |
| 1631 | 4 | Util.createDirectoryTree(docDir + System.dirname(target)); | |
| 1632 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | if System.copyFile(source, docDir + target) then |
| 1633 | 4 | page := System.stringReplace(page, uri, target); | |
| 1634 | end if; | ||
| 1635 | end if; | ||
| 1636 | else | ||
| 1637 | // A broken documentation link is not worth failing the export over. | ||
| 1638 | end try; | ||
| 1639 | end for; | ||
| 1640 | end copyFMUDocumentationResources; | ||
| 1641 | |||
| 1642 | protected function callTargetTemplatesXML | ||
| 1643 | "Generate target code by passing the SimCode data structure to templates." | ||
| 1644 | input SimCode.SimCode simCode; | ||
| 1645 | input String target; | ||
| 1646 | algorithm | ||
| 1647 | ✗ | Tpl.tplNoret(CodegenXML.translateModel, simCode); | |
| 1648 | end callTargetTemplatesXML; | ||
| 1649 | |||
| 1650 | public function translateModel " | ||
| 1651 | Entry point to translate a Modelica model for simulation / FMU / XML. | ||
| 1652 | Called from other places in the compiler." | ||
| 1653 | output Boolean success; | ||
| 1654 | input TranslateModelKind kind; | ||
| 1655 | input output FCore.Cache cache; | ||
| 1656 | input FCore.Graph inEnv; | ||
| 1657 | input Absyn.Path className "path for the model"; | ||
| 1658 | input String inFileNamePrefix; | ||
| 1659 | input Boolean runBackend "if true, run the backend as well. This will run SimCode and Codegen as well."; | ||
| 1660 | input Boolean useDAEMode "if true, run the backend in DAEMode."; | ||
| 1661 | input Boolean runSilent "if true, flat modelica code will not be dumped to out stream"; | ||
| 1662 | input Option<SimCode.SimulationSettings> inSimSettingsOpt; | ||
| 1663 | input Absyn.FunctionArgs args=Absyn.emptyFunctionArgs "labels for remove terms"; | ||
| 1664 | output list<String> outLibs; | ||
| 1665 | output String outFileDir; | ||
| 1666 | output list<tuple<String, Values.Value>> resultValues; | ||
| 1667 | protected | ||
| 1668 | FCore.Cache inCache = cache; | ||
| 1669 | Real timeFrontend=0.0; | ||
| 1670 | DAE.DAElist dae; | ||
| 1671 | FCore.Graph env; | ||
| 1672 | Option<DAE.DAElist> odae; | ||
| 1673 | AvlTreePathFunction.Tree funcs; | ||
| 1674 | list<Option<Integer>> allRoots; | ||
| 1675 | FlatModel flatModel; | ||
| 1676 | FunctionTree funcTree; | ||
| 1677 | UnorderedMap<Absyn.Path, NFFunction.Function> funcMap; | ||
| 1678 | Boolean dumpValidFlatModelicaNF; | ||
| 1679 | String flatString = "", NFFlatString = ""; | ||
| 1680 | Boolean restart; | ||
| 1681 | list<String> structuralParameters; | ||
| 1682 | |||
| 1683 | algorithm | ||
| 1684 | // BUILD_MODEL starts only once the translation is through, so clear it too: | ||
| 1685 | // a failing translation would otherwise read an earlier command's tick. | ||
| 1686 | 2537 | List.map_0({ClockIndexes.RT_CLOCK_FRONTEND,ClockIndexes.RT_CLOCK_BACKEND, | |
| 1687 | ClockIndexes.RT_CLOCK_SIMCODE,ClockIndexes.RT_CLOCK_TEMPLATES, | ||
| 1688 | ClockIndexes.RT_CLOCK_BUILD_MODEL,ClockIndexes.RT_CLOCK_FMU_BACKEND, | ||
| 1689 | ClockIndexes.RT_CLOCK_FMU_SIMCODE,ClockIndexes.RT_CLOCK_FMU_TEMPLATES},System.realtimeClear); | ||
| 1690 | 2537 | FlagsUtil.setConfigBool(Flags.BUILDING_MODEL, true); | |
| 1691 | |||
| 1692 | 2537 | outLibs := {}; | |
| 1693 | 2537 | outFileDir := ""; | |
| 1694 | 2537 | resultValues := {}; | |
| 1695 | |||
| 1696 |
4/4✓ Branch 0 taken 1262 times.
✓ Branch 1 taken 1275 times.
✓ Branch 3 taken 1237 times.
✓ Branch 4 taken 25 times.
|
2537 | dumpValidFlatModelicaNF := not runSilent and Config.flatModelica(); |
| 1697 | |||
| 1698 | // new backend - also activates new frontend by default | ||
| 1699 |
2/2✓ Branch 1 taken 204 times.
✓ Branch 2 taken 2333 times.
|
2537 | if Flags.getConfigBool(Flags.NEW_BACKEND) then |
| 1700 | // The C++ runtime keeps arrays un-expanded (StatArrayDim members) and cannot | ||
| 1701 | // use the scalarized var layout; the C runtime does not yet support | ||
| 1702 | // non-scalarized arrays. Force simCodeScalarize=false for the C++ target only, | ||
| 1703 | // leaving the default (true) for the C target (issue #15496). Must happen | ||
| 1704 | // before any scalarize-dependent decision in the pipeline. | ||
| 1705 | // Scalarized sim code has one slot per element and cannot grow, so resizable | ||
| 1706 | // arrays are never scalarized. | ||
| 1707 |
5/6✓ Branch 1 taken 1 time.
✓ Branch 2 taken 203 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 8 taken 12 times.
✓ Branch 9 taken 191 times.
|
204 | if stringEqual(Config.simCodeTarget(), "Cpp") or Flags.getConfigBool(Flags.RESIZABLE_ARRAYS) then |
| 1708 | 13 | FlagsUtil.setConfigBool(Flags.SIM_CODE_SCALARIZE, false); | |
| 1709 | end if; | ||
| 1710 | // ToDo: set permanently matching -> SBGraphs | ||
| 1711 | 204 | System.realtimeTick(ClockIndexes.RT_CLOCK_FRONTEND); | |
| 1712 | 204 | ExecStat.execStatReset(); | |
| 1713 | |||
| 1714 | 204 | (flatModel, funcTree, NFFlatString) := CevalScriptBackend.runFrontEndWorkNF(className, false, dumpValidFlatModelicaNF); | |
| 1715 | 203 | timeFrontend := System.realtimeTock(ClockIndexes.RT_CLOCK_FRONTEND); | |
| 1716 | 203 | ExecStat.execStat("FrontEnd"); | |
| 1717 | |||
| 1718 |
2/2✓ Branch 0 taken 193 times.
✓ Branch 1 taken 10 times.
|
203 | if runBackend then |
| 1719 | 193 | funcMap := UnorderedMap.fromLists(FunctionTreeImpl.listKeys(funcTree), FunctionTreeImpl.listValues(funcTree), AbsynUtil.pathHash, AbsynUtil.pathEqual); | |
| 1720 | 193 | (outLibs, outFileDir, resultValues, funcs) := translateModelCallBackendNB(flatModel, funcMap, className, inFileNamePrefix, inSimSettingsOpt, kind); | |
| 1721 | else | ||
| 1722 | 10 | funcs := NFConvertDAE.convertFunctionTree(funcTree); | |
| 1723 | end if; | ||
| 1724 | |||
| 1725 | // This must be done after calling the backend since it uses the FlatModel, | ||
| 1726 | // and converting it to DAE is destructive. | ||
| 1727 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 196 times.
|
198 | if dumpValidFlatModelicaNF then |
| 1728 | 2 | flatString := NFFlatString; | |
| 1729 | elseif not runSilent then | ||
| 1730 | 8 | dae := NFConvertDAE.convertModel(flatModel); | |
| 1731 | 8 | flatString := DAEDump.dumpStr(dae, funcs); | |
| 1732 | end if; | ||
| 1733 | |||
| 1734 | // old backend | ||
| 1735 | else | ||
| 1736 | // The frontend runs again if the backend asks to evaluate parameters that | ||
| 1737 | // make a coefficient it solved an equation with zero. | ||
| 1738 | 2333 | setGlobalRoot(Global.structuralParameters, {}); | |
| 1739 | 2333 | ErrorExt.setCheckpoint(getInstanceName()); | |
| 1740 | try | ||
| 1741 | 2333 | restart := true; | |
| 1742 |
2/2✓ Branch 0 taken 2334 times.
✓ Branch 1 taken 2028 times.
|
4362 | while restart loop |
| 1743 | // calculate stuff that we need to create SimCode data structure | ||
| 1744 | 2334 | System.realtimeTick(ClockIndexes.RT_CLOCK_FRONTEND); | |
| 1745 | 2334 | ExecStat.execStatReset(); | |
| 1746 | 2334 | (cache, env, odae, NFFlatString) := CevalScriptBackend.runFrontEnd(inCache, inEnv, className, false, dumpValidFlatModelicaNF); | |
| 1747 | 2334 | ExecStat.execStat("FrontEnd"); | |
| 1748 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 2334 times.
✓ Branch 2 taken 294 times.
✓ Branch 3 taken 2040 times.
|
2334 | SOME(dae) := odae; |
| 1749 | |||
| 1750 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2017 times.
|
2040 | if dumpValidFlatModelicaNF then |
| 1751 | 23 | flatString := NFFlatString; | |
| 1752 | elseif not runSilent then | ||
| 1753 | 936 | funcs := FCore.getFunctionTree(cache); | |
| 1754 | 936 | flatString := DAEDump.dumpStr(dae, funcs); | |
| 1755 | end if; | ||
| 1756 | |||
| 1757 | |||
| 1758 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2040 times.
|
2040 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 1759 | allRoots := {}; | ||
| 1760 | ✗ | for i in 1:300 loop | |
| 1761 | try | ||
| 1762 | ✗ | allRoots := getGlobalRoot(i)::allRoots; | |
| 1763 | else | ||
| 1764 | end try; | ||
| 1765 | end for; | ||
| 1766 | ✗ | serializeNotify(allRoots, "All local+global roots (1:300)"); | |
| 1767 | ✗ | serializeNotify(dae, "FrontEnd DAE"); | |
| 1768 | ✗ | serializeNotify((env,inEnv,cache,inCache), "FCore.Graph + Cache + Old graph + Old cache"); | |
| 1769 | ✗ | serializeNotify((SymbolTable.get(),dae,env,inEnv,cache,inCache), "Symbol Table, DAE, Graph, OldGraph, Cache, OldCache"); | |
| 1770 | ✗ | ExecStat.execStat("Serialize FrontEnd"); | |
| 1771 | end if; | ||
| 1772 | |||
| 1773 | 2040 | timeFrontend := System.realtimeTock(ClockIndexes.RT_CLOCK_FRONTEND); | |
| 1774 |
2/2✓ Branch 0 taken 1081 times.
✓ Branch 1 taken 959 times.
|
2040 | if runBackend then |
| 1775 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1072 times.
|
1081 | if useDAEMode then |
| 1776 | 9 | (cache, outLibs, outFileDir, resultValues) := translateModelCallBackendOBDAEMode(cache, env, dae, className, inFileNamePrefix, inSimSettingsOpt, args, kind); | |
| 1777 | 9 | restart := false; | |
| 1778 | else | ||
| 1779 | 1072 | (cache, outLibs, outFileDir, resultValues, restart) := translateModelCallBackendOB(kind, cache, env, dae, className, inFileNamePrefix, inSimSettingsOpt, args); | |
| 1780 | end if; | ||
| 1781 | else | ||
| 1782 | 959 | restart := false; | |
| 1783 | end if; | ||
| 1784 | |||
| 1785 |
2/2✓ Branch 0 taken 2028 times.
✓ Branch 1 taken 1 time.
|
2029 | if restart then |
| 1786 | 1 | ErrorExt.rollBack(getInstanceName()); | |
| 1787 | 1 | ErrorExt.setCheckpoint(getInstanceName()); | |
| 1788 | end if; | ||
| 1789 | end while; | ||
| 1790 | else | ||
| 1791 | 305 | ErrorExt.delCheckpoint(getInstanceName()); | |
| 1792 | 305 | setGlobalRoot(Global.structuralParameters, {}); | |
| 1793 | 305 | fail(); | |
| 1794 | end try; | ||
| 1795 | 2028 | ErrorExt.delCheckpoint(getInstanceName()); | |
| 1796 | |||
| 1797 | 2028 | structuralParameters := getGlobalRoot(Global.structuralParameters); | |
| 1798 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2027 times.
|
2028 | if not listEmpty(structuralParameters) then |
| 1799 | 2 | Error.addMessage(Error.EVALUATED_ZERO_COEFFICIENT, {stringDelimitList(listReverse(structuralParameters), ", ")}); | |
| 1800 | 1 | setGlobalRoot(Global.structuralParameters, {}); | |
| 1801 | end if; | ||
| 1802 | end if; | ||
| 1803 | |||
| 1804 | 2226 | resultValues := List.appendElt(("timeFrontend", Values.REAL(timeFrontend)), resultValues); | |
| 1805 | 2226 | FlagsUtil.setConfigBool(Flags.BUILDING_MODEL, false); | |
| 1806 | |||
| 1807 |
3/4✓ Branch 0 taken 969 times.
✓ Branch 1 taken 1257 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 969 times.
|
2226 | if not stringEmpty(flatString) and runSilent then |
| 1808 | ✗ | Error.addInternalError("Flat model string generated but is not being dumped. Please make sure it is not generated if it is not shown." | |
| 1809 | , sourceInfo()); | ||
| 1810 | elseif stringEmpty(flatString) and not runSilent then | ||
| 1811 | ✗ | Error.addInternalError("Flat model string generated but is empty.", sourceInfo()); | |
| 1812 | else | ||
| 1813 | 2226 | print(flatString); | |
| 1814 | end if; | ||
| 1815 | |||
| 1816 | // The simCode clock ticks on every FMU export and on nothing else. | ||
| 1817 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 2226 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
2226 | if Flags.isSet(Flags.EXEC_STAT) and System.realtimeNtick(ClockIndexes.RT_CLOCK_FMU_SIMCODE) > 0 then |
| 1818 | ✗ | Error.addCompilerNotification("FMU-only work: backend " + fmuOverheadTime(ClockIndexes.RT_CLOCK_FMU_BACKEND) | |
| 1819 | + ", simCode " + fmuOverheadTime(ClockIndexes.RT_CLOCK_FMU_SIMCODE) | ||
| 1820 | + ", templates " + fmuOverheadTime(ClockIndexes.RT_CLOCK_FMU_TEMPLATES)); | ||
| 1821 | end if; | ||
| 1822 | |||
| 1823 | success := true; | ||
| 1824 | end translateModel; | ||
| 1825 | |||
| 1826 | protected function fmuOverheadTime | ||
| 1827 | "Seconds accumulated on one of the RT_CLOCK_FMU_* clocks, 0 if it never ran." | ||
| 1828 | input Integer clockIndex; | ||
| 1829 | output String str; | ||
| 1830 | algorithm | ||
| 1831 | ✗ | str := System.snprintff("%.4g", 20, if System.realtimeNtick(clockIndex) > 0 then System.realtimeAccumulated(clockIndex) else 0.0); | |
| 1832 | end fmuOverheadTime; | ||
| 1833 | |||
| 1834 | public function translateModelCallBackend | ||
| 1835 | input FlatModel flatModel; | ||
| 1836 | input FunctionTree functions; | ||
| 1837 | input Absyn.Path className; | ||
| 1838 | input String fileNamePrefix; | ||
| 1839 | input Boolean useDAEMode; | ||
| 1840 | input Option<SimCode.SimulationSettings> simSettings; | ||
| 1841 | output list<String> outLibs; | ||
| 1842 | output String outFileDir; | ||
| 1843 | output list<tuple<String, Values.Value>> resultValues; | ||
| 1844 | protected | ||
| 1845 | UnorderedMap<Absyn.Path, NFFunction.Function> func_map; | ||
| 1846 | DAE.DAElist dae; | ||
| 1847 | AvlTreePathFunction.Tree dae_funcs; | ||
| 1848 | FCore.Graph env; | ||
| 1849 | FCore.Cache cache; | ||
| 1850 | String file_name_prefix; | ||
| 1851 | algorithm | ||
| 1852 | ✗ | file_name_prefix := if fileNamePrefix == "<default>" then AbsynUtil.pathString(className) else fileNamePrefix; | |
| 1853 | |||
| 1854 | ✗ | if Flags.getConfigBool(Flags.NEW_BACKEND) then | |
| 1855 | // The C++ runtime keeps arrays un-expanded (StatArrayDim members) and cannot | ||
| 1856 | // use the scalarized var layout; the C runtime, on the other hand, does not | ||
| 1857 | // yet support non-scalarized arrays. So force simCodeScalarize=false for the | ||
| 1858 | // C++ target only, leaving the default (true) for the C target (issue #15496). | ||
| 1859 | // Scalarized sim code has one slot per element and cannot grow, so resizable | ||
| 1860 | // arrays are never scalarized. | ||
| 1861 | ✗ | if stringEqual(Config.simCodeTarget(), "Cpp") or Flags.getConfigBool(Flags.RESIZABLE_ARRAYS) then | |
| 1862 | ✗ | FlagsUtil.setConfigBool(Flags.SIM_CODE_SCALARIZE, false); | |
| 1863 | end if; | ||
| 1864 | ✗ | func_map := UnorderedMap.fromLists(FunctionTree.listKeys(functions), FunctionTree.listValues(functions), AbsynUtil.pathHash, AbsynUtil.pathEqual); | |
| 1865 | ✗ | (outLibs, outFileDir, resultValues, _) := translateModelCallBackendNB(flatModel, func_map, className, file_name_prefix, simSettings); | |
| 1866 | else | ||
| 1867 | ✗ | dae := NFConvertDAE.convertModel(flatModel); | |
| 1868 | ✗ | dae_funcs := NFConvertDAE.convertFunctionTree(functions); | |
| 1869 | ✗ | env := FGraph.new("graph", FCore.dummyTopModel); | |
| 1870 | ✗ | cache := FCore.emptyCache(); | |
| 1871 | ✗ | FCore.setCachedFunctionTree(cache, dae_funcs); | |
| 1872 | |||
| 1873 | ✗ | if useDAEMode then | |
| 1874 | ✗ | (cache, outLibs, outFileDir, resultValues) := translateModelCallBackendOBDAEMode(cache, env, dae, className, file_name_prefix, simSettings, Absyn.emptyFunctionArgs); | |
| 1875 | else | ||
| 1876 | ✗ | (cache, outLibs, outFileDir, resultValues) := translateModelCallBackendOB(TranslateModelKind.NORMAL(), cache, env, dae, className, file_name_prefix, simSettings, Absyn.emptyFunctionArgs); | |
| 1877 | end if; | ||
| 1878 | end if; | ||
| 1879 | end translateModelCallBackend; | ||
| 1880 | |||
| 1881 | protected function simSettingsSimflags | ||
| 1882 | "Extracts the simulation flag string from the simulation settings, if present. | ||
| 1883 | BackendDAE.ExtraInfo stores only this String (not the SimCode.SimulationSettings | ||
| 1884 | record) so the backend datatype package does not depend on SimCode." | ||
| 1885 | input Option<SimCode.SimulationSettings> inSimSettingsOpt; | ||
| 1886 | output Option<String> simflags; | ||
| 1887 | algorithm | ||
| 1888 | simflags := match inSimSettingsOpt | ||
| 1889 | local String s; | ||
| 1890 | case SOME(SimCode.SIMULATION_SETTINGS(simflags = s)) then SOME(s); | ||
| 1891 | else NONE(); | ||
| 1892 | end match; | ||
| 1893 | end simSettingsSimflags; | ||
| 1894 | |||
| 1895 | protected function translateModelCallBackendOB | ||
| 1896 | input TranslateModelKind kind; | ||
| 1897 | input output FCore.Cache cache; | ||
| 1898 | input FCore.Graph inEnv; | ||
| 1899 | input DAE.DAElist inDae; | ||
| 1900 | input Absyn.Path className "path for the model"; | ||
| 1901 | input String inFileNamePrefix; | ||
| 1902 | input Option<SimCode.SimulationSettings> inSimSettingsOpt; | ||
| 1903 | input Absyn.FunctionArgs args = Absyn.emptyFunctionArgs "labels for remove terms"; | ||
| 1904 | output list<String> outLibs; | ||
| 1905 | output String outFileDir; | ||
| 1906 | output list<tuple<String, Values.Value>> resultValues; | ||
| 1907 | output Boolean restart = false "the backend added parameters that the frontend must evaluate"; | ||
| 1908 | protected | ||
| 1909 | Boolean generateFunctions = false; | ||
| 1910 | Real timeSimCode=0.0, timeTemplates=0.0, timeBackend=0.0; | ||
| 1911 | Integer numStructuralParameters = listLength(getGlobalRoot(Global.structuralParameters)); | ||
| 1912 | algorithm | ||
| 1913 | 1072 | FlagsUtil.setConfigBool(Flags.BUILDING_MODEL, true); | |
| 1914 | (outLibs, outFileDir) := match inEnv | ||
| 1915 | local | ||
| 1916 | String file_dir, description, fmuType; | ||
| 1917 | list<String> libs; | ||
| 1918 | DAE.DAElist dae; | ||
| 1919 | FCore.Graph graph; | ||
| 1920 | BackendDAE.BackendDAE dlow, initDAE; | ||
| 1921 | Option<BackendDAE.BackendDAE> initDAE_lambda0; | ||
| 1922 | Option<BackendDAE.InlineData> inlineData; | ||
| 1923 | list<BackendDAE.Equation> removedInitialEquationLst; | ||
| 1924 | Option<list<String>> strPreOptModules; | ||
| 1925 | Boolean isFMI2; | ||
| 1926 | BackendDAE.SymbolicJacobians fmiDer; | ||
| 1927 | AvlTreePathFunction.Tree funcs; | ||
| 1928 | |||
| 1929 | // old backend | ||
| 1930 | case graph algorithm | ||
| 1931 | 1072 | System.realtimeTick(ClockIndexes.RT_CLOCK_BACKEND); | |
| 1932 | 1072 | dae := DAEUtil.transformationsBeforeBackend(cache, graph, inDae, StateMachineFlatten.stateMachineToDataFlow); | |
| 1933 | 1072 | ExecStat.execStat("Transformations before backend"); | |
| 1934 | |||
| 1935 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1072 times.
|
1072 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 1936 | ✗ | serializeNotify(dae, "FrontEnd DAE after transformations"); | |
| 1937 | ✗ | serializeNotify((dae,inDae), "FrontEnd DAE before+after transformations"); | |
| 1938 | ✗ | ExecStat.execStat("Serialize DAE (2)"); | |
| 1939 | end if; | ||
| 1940 | 1072 | GCExt.free(inDae); | |
| 1941 | // inDae := DAE.emptyDae; | ||
| 1942 | |||
| 1943 | 1072 | generateFunctions := FlagsUtil.set(Flags.GEN, false); | |
| 1944 | // We should not need to lookup constants and classes in the backend, | ||
| 1945 | // so let's free up the old graph and just make it the initial environment. | ||
| 1946 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1072 times.
|
1072 | if not Flags.isSet(Flags.BACKEND_KEEP_ENV_GRAPH) then |
| 1947 | ✗ | (cache,graph) := Builtin.initialGraph(cache); | |
| 1948 | end if; | ||
| 1949 | |||
| 1950 | 1072 | description := DAEUtil.daeDescription(dae); | |
| 1951 | 1072 | dlow := BackendDAECreate.lower(dae, cache, graph, BackendDAE.EXTRA_INFO(description, inFileNamePrefix, simSettingsSimflags(inSimSettingsOpt))); | |
| 1952 | |||
| 1953 | 1066 | GCExt.free(dae); | |
| 1954 | |||
| 1955 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1066 times.
|
1066 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 1956 | ✗ | serializeNotify(dlow, "BackendDAECreate.lower"); | |
| 1957 | ✗ | ExecStat.execStat("Serialize dlow"); | |
| 1958 | end if; | ||
| 1959 | |||
| 1960 | // FMI 2.0 and 3.0 both provide a ModelStructure with dependencies and | ||
| 1961 | // partial derivatives, so they share the same backend preparation. | ||
| 1962 | (isFMI2,fmuType) := match kind | ||
| 1963 |
4/4✓ Branch 2 taken 16 times.
✓ Branch 3 taken 70 times.
✓ Branch 6 taken 13 times.
✓ Branch 7 taken 3 times.
|
86 | case TranslateModelKind.FMU(fmuType) then (FMI.isFMIVersion20() or FMI.isFMIVersion30(),fmuType); |
| 1964 | else (false,""); | ||
| 1965 | end match; | ||
| 1966 | // FMI 2.0/3.0: enable postOptModule to create alias variables for output states | ||
| 1967 |
2/2✓ Branch 0 taken 83 times.
✓ Branch 1 taken 3 times.
|
86 | strPreOptModules := if (isFMI2) then SOME("introduceOutputAliases"::BackendDAEUtil.getPreOptModulesString()) else NONE(); |
| 1968 | |||
| 1969 | // FMI 2.0/3.0: enable postOptModule "introduceOutputRealDerivatives" to set maxOutputDerivativeOrder = 1 | ||
| 1970 |
6/6✓ Branch 0 taken 83 times.
✓ Branch 1 taken 983 times.
✓ Branch 2 taken 75 times.
✓ Branch 3 taken 8 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 67 times.
|
1066 | if (isFMI2 and fmuType == "cs") then |
| 1971 | 8 | strPreOptModules := SOME("introduceOutputRealDerivatives":: Util.getOption(strPreOptModules)); | |
| 1972 | end if; | ||
| 1973 | |||
| 1974 | //BackendDump.printBackendDAE(dlow); | ||
| 1975 | 1066 | (dlow, initDAE, initDAE_lambda0, inlineData, removedInitialEquationLst) := BackendDAEUtil.getSolvedSystem(dlow,inFileNamePrefix,strPreOptModules=strPreOptModules); | |
| 1976 | 1061 | restart := listLength(getGlobalRoot(Global.structuralParameters)) > numStructuralParameters; | |
| 1977 | |||
| 1978 | // generate derivatives | ||
| 1979 |
4/4✓ Branch 0 taken 83 times.
✓ Branch 1 taken 978 times.
✓ Branch 3 taken 80 times.
✓ Branch 4 taken 3 times.
|
1061 | if (isFMI2) and not restart and not Flags.isSet(Flags.FMI20_DEPENDENCIES) then |
| 1980 | // activate symolic jacobains for fmi 2.0 | ||
| 1981 | // to provide dependence information and partial derivatives | ||
| 1982 | 80 | System.realtimeTick(ClockIndexes.RT_CLOCK_FMU_BACKEND); | |
| 1983 | 80 | (fmiDer, funcs) := SymbolicJacobian.createFMIModelDerivatives(dlow); | |
| 1984 | 80 | dlow := BackendDAEUtil.setFunctionTree(dlow, funcs); | |
| 1985 | 80 | System.realtimeAccumulate(ClockIndexes.RT_CLOCK_FMU_BACKEND); | |
| 1986 | else | ||
| 1987 | fmiDer := {}; | ||
| 1988 | end if; | ||
| 1989 | 1061 | timeBackend := System.realtimeTock(ClockIndexes.RT_CLOCK_BACKEND); | |
| 1990 | |||
| 1991 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1061 times.
|
1061 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 1992 | ✗ | serializeNotify(dlow, "BackendDAE (simulation)"); | |
| 1993 | ✗ | serializeNotify(initDAE, "BackendDAE (initialization)"); | |
| 1994 | ✗ | serializeNotify(initDAE_lambda0, "BackendDAE (lambda0)"); | |
| 1995 | ✗ | serializeNotify((dlow,initDAE,initDAE_lambda0,inlineData,removedInitialEquationLst), "BackendDAE (simulation+initialization+lambda0+inlineData+removedInitialEquationLst)"); | |
| 1996 | ✗ | ExecStat.execStat("Serialize solved system"); | |
| 1997 | end if; | ||
| 1998 | |||
| 1999 | (libs, file_dir, timeSimCode, timeTemplates) := match kind | ||
| 2000 | 1 | case _ guard restart then ({}, "", 0.0, 0.0); | |
| 2001 | case TranslateModelKind.NORMAL() | ||
| 2002 | algorithm | ||
| 2003 | 974 | (libs, file_dir, timeSimCode, timeTemplates) := generateModelCode(dlow, initDAE, initDAE_lambda0, inlineData, removedInitialEquationLst, SymbolTable.getAbsyn(), className, inFileNamePrefix, inSimSettingsOpt, args,fmiDer); | |
| 2004 | then (libs, file_dir, timeSimCode, timeTemplates); | ||
| 2005 | case TranslateModelKind.FMU() | ||
| 2006 | algorithm | ||
| 2007 | |||
| 2008 | 86 | (libs,file_dir,timeSimCode,timeTemplates) := generateModelCodeFMU(dlow, initDAE, initDAE_lambda0, fmiDer, removedInitialEquationLst, SymbolTable.getAbsyn(), className, FMI.getFMIVersionString(), kind.kind, inFileNamePrefix, kind.targetName, inSimSettingsOpt, kind.translateOnly); | |
| 2009 | then (libs, file_dir, timeSimCode, timeTemplates); | ||
| 2010 | case TranslateModelKind.XML() | ||
| 2011 | algorithm | ||
| 2012 | ✗ | (libs, file_dir, timeSimCode, timeTemplates) := generateModelCodeXML(dlow, initDAE, initDAE_lambda0, removedInitialEquationLst, SymbolTable.getAbsyn(), className, inFileNamePrefix, inSimSettingsOpt); | |
| 2013 | then (libs, file_dir, timeSimCode, timeTemplates); | ||
| 2014 | else | ||
| 2015 | algorithm | ||
| 2016 | ✗ | Error.addInternalError("Unknown translateModel kind: " + anyString(kind), sourceInfo()); | |
| 2017 | ✗ | then fail(); | |
| 2018 | end match; | ||
| 2019 | then (libs, file_dir); | ||
| 2020 | |||
| 2021 | end match; | ||
| 2022 | |||
| 2023 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1060 times.
|
1061 | if generateFunctions then |
| 2024 | 1 | FlagsUtil.set(Flags.GEN, true); | |
| 2025 | end if; | ||
| 2026 | |||
| 2027 | 1061 | resultValues := {("timeTemplates", Values.REAL(timeTemplates)), | |
| 2028 | ("timeSimCode", Values.REAL(timeSimCode)), | ||
| 2029 | ("timeBackend", Values.REAL(timeBackend))}; | ||
| 2030 | end translateModelCallBackendOB; | ||
| 2031 | |||
| 2032 | public function translateModelCallBackendOBDAEMode | ||
| 2033 | " Entry point to translate a Modelica model for simulation in DAE mode | ||
| 2034 | Called from CevalScriptBackend" | ||
| 2035 | input output FCore.Cache cache; | ||
| 2036 | input FCore.Graph inEnv; | ||
| 2037 | input DAE.DAElist inDae; | ||
| 2038 | input Absyn.Path className "path for the model"; | ||
| 2039 | input String inFileNamePrefix; | ||
| 2040 | input Option<SimCode.SimulationSettings> inSimSettingsOpt; | ||
| 2041 | input Absyn.FunctionArgs args "labels for remove terms"; | ||
| 2042 | input TranslateModelKind kind = TranslateModelKind.NORMAL() "FMU() to generate an FMU instead of a simulation"; | ||
| 2043 | output list<String> outLibs; | ||
| 2044 | output String outFileDir; | ||
| 2045 | output list<tuple<String, Values.Value>> resultValues; | ||
| 2046 | protected | ||
| 2047 | Boolean generateFunctions = false; | ||
| 2048 | Real timeSimCode=0.0, timeTemplates=0.0, timeBackend=0.0; | ||
| 2049 | algorithm | ||
| 2050 | (outLibs, outFileDir) := | ||
| 2051 | matchcontinue inEnv | ||
| 2052 | local | ||
| 2053 | String file_dir, resstr, description; | ||
| 2054 | list<String> libs; | ||
| 2055 | DAE.DAElist dae; | ||
| 2056 | FCore.Graph graph; | ||
| 2057 | BackendDAE.BackendDAE dlow, initDAE; | ||
| 2058 | Option<BackendDAE.BackendDAE> initDAE_lambda0_option; | ||
| 2059 | list<BackendDAE.Equation> removedInitialEquationLst; | ||
| 2060 | Option<list<String>> strPreOptModules; | ||
| 2061 | Boolean isFMI; | ||
| 2062 | |||
| 2063 | case graph algorithm | ||
| 2064 | 9 | System.realtimeTick(ClockIndexes.RT_CLOCK_BACKEND); | |
| 2065 | 9 | dae := DAEUtil.transformationsBeforeBackend(cache, graph, inDae, StateMachineFlatten.stateMachineToDataFlow); | |
| 2066 | 9 | ExecStat.execStat("Transformations before backend"); | |
| 2067 | |||
| 2068 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 2069 | ✗ | serializeNotify(dae, "dae2"); | |
| 2070 | ✗ | ExecStat.execStat("Serialize DAE (2)"); | |
| 2071 | end if; | ||
| 2072 | 9 | GCExt.free(inDae); | |
| 2073 | |||
| 2074 | 9 | generateFunctions := FlagsUtil.set(Flags.GEN, false); | |
| 2075 | // We should not need to lookup constants and classes in the backend, | ||
| 2076 | // so let's free up the old graph and just make it the initial environment. | ||
| 2077 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if not Flags.isSet(Flags.BACKEND_KEEP_ENV_GRAPH) then |
| 2078 | ✗ | (cache,graph) := Builtin.initialGraph(cache); | |
| 2079 | end if; | ||
| 2080 | |||
| 2081 | 9 | description := DAEUtil.daeDescription(dae); | |
| 2082 | 9 | dlow := BackendDAECreate.lower(dae, cache, graph, BackendDAE.EXTRA_INFO(description,inFileNamePrefix,simSettingsSimflags(inSimSettingsOpt))); | |
| 2083 | |||
| 2084 | 9 | GCExt.free(dae); | |
| 2085 | |||
| 2086 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 2087 | ✗ | serializeNotify(dlow, "dlow"); | |
| 2088 | ✗ | ExecStat.execStat("Serialize dlow"); | |
| 2089 | end if; | ||
| 2090 | |||
| 2091 | // The alias variables FMI 2.0/3.0 report top-level outputs off. Not its | ||
| 2092 | // counterpart introduceOutputRealDerivatives: that makes every Real output a | ||
| 2093 | // state, which in DAE mode changes the residual system. | ||
| 2094 | isFMI := match kind | ||
| 2095 | ✗ | case TranslateModelKind.FMU() then FMI.isFMIVersion20() or FMI.isFMIVersion30(); | |
| 2096 | else false; | ||
| 2097 | end match; | ||
| 2098 | ✗ | strPreOptModules := if isFMI then SOME("introduceOutputAliases"::BackendDAEUtil.getPreOptModulesString()) else NONE(); | |
| 2099 | |||
| 2100 | //BackendDump.printBackendDAE(dlow); | ||
| 2101 | 9 | (dlow, initDAE, initDAE_lambda0_option, removedInitialEquationLst) := DAEMode.getEqSystemDAEmode(dlow, inFileNamePrefix, strPreOptModules=strPreOptModules); | |
| 2102 | 9 | ExecStat.execStat("Backend"); | |
| 2103 | |||
| 2104 | 9 | timeBackend := System.realtimeTock(ClockIndexes.RT_CLOCK_BACKEND); | |
| 2105 | |||
| 2106 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 2107 | ✗ | serializeNotify(dlow, "simDAE"); | |
| 2108 | ✗ | serializeNotify(initDAE, "initDAE"); | |
| 2109 | ✗ | serializeNotify(removedInitialEquationLst, "removedInitialEquationLst"); | |
| 2110 | ✗ | ExecStat.execStat("Serialize solved system"); | |
| 2111 | end if; | ||
| 2112 | |||
| 2113 | 9 | (libs, file_dir, timeSimCode, timeTemplates) := generateModelCodeDAE(dlow, initDAE, initDAE_lambda0_option, removedInitialEquationLst, SymbolTable.getAbsyn(), className, inFileNamePrefix, inSimSettingsOpt, args, kind); | |
| 2114 | 9 | timeSimCode := System.realtimeTock(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 2115 | 9 | timeTemplates := System.realtimeTock(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 2116 | |||
| 2117 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | then (libs, file_dir); |
| 2118 | |||
| 2119 | else algorithm | ||
| 2120 | ✗ | resstr := AbsynUtil.pathStringNoQual(className); | |
| 2121 | ✗ | resstr := stringAppendList({"SimCode DAEmode: The model ", resstr, " could not be translated"}); | |
| 2122 | ✗ | Error.addMessage(Error.INTERNAL_ERROR, {resstr}); | |
| 2123 | ✗ | then fail(); | |
| 2124 | |||
| 2125 | end matchcontinue; | ||
| 2126 | |||
| 2127 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if generateFunctions then |
| 2128 | ✗ | FlagsUtil.set(Flags.GEN, true); | |
| 2129 | end if; | ||
| 2130 | |||
| 2131 | 9 | resultValues := {("timeTemplates", Values.REAL(timeTemplates)), | |
| 2132 | ("timeSimCode", Values.REAL(timeSimCode)), | ||
| 2133 | ("timeBackend", Values.REAL(timeBackend))}; | ||
| 2134 | |||
| 2135 | end translateModelCallBackendOBDAEMode; | ||
| 2136 | |||
| 2137 | protected function translateModelCallBackendNB | ||
| 2138 | input FlatModel inFlatModel; | ||
| 2139 | input UnorderedMap<Absyn.Path, NFFunction.Function> funcMap; | ||
| 2140 | input Absyn.Path inClassName "path for the model"; | ||
| 2141 | input String inFileNamePrefix; | ||
| 2142 | input Option<SimCode.SimulationSettings> inSimSettingsOpt; | ||
| 2143 | input TranslateModelKind kind = TranslateModelKind.NORMAL() "FMU() to generate an FMU instead of a simulation"; | ||
| 2144 | output list<String> outLibs; | ||
| 2145 | output String outFileDir; | ||
| 2146 | output list<tuple<String, Values.Value>> resultValues; | ||
| 2147 | output AvlTreePathFunction.Tree oldFunctionTree; | ||
| 2148 | protected | ||
| 2149 | Real timeSimCode=0.0, timeTemplates=0.0, timeBackend=0.0; | ||
| 2150 | NBackendDAE bdae; | ||
| 2151 | Boolean nf_api; | ||
| 2152 | algorithm | ||
| 2153 | 193 | FlagsUtil.setConfigBool(Flags.BUILDING_MODEL, true); | |
| 2154 | 193 | nf_api := FlagsUtil.set(Flags.NF_API, false); | |
| 2155 | |||
| 2156 | 193 | System.realtimeTick(ClockIndexes.RT_CLOCK_BACKEND); | |
| 2157 | 193 | bdae := NBackendDAE.lower(inFlatModel, funcMap); | |
| 2158 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 193 times.
|
193 | if Flags.isSet(Flags.OPT_DAE_DUMP) then |
| 2159 | ✗ | print(NBackendDAE.toString(bdae, "(After Lowering)")); | |
| 2160 | end if; | ||
| 2161 | 193 | bdae := NBackendDAE.main(bdae); | |
| 2162 | 188 | timeBackend := System.realtimeTock(ClockIndexes.RT_CLOCK_BACKEND); | |
| 2163 | 188 | ExecStat.execStat("backend"); | |
| 2164 | 188 | FlagsUtil.set(Flags.NF_API, nf_api); | |
| 2165 | |||
| 2166 | 188 | (outLibs, outFileDir, timeSimCode, timeTemplates, oldFunctionTree) := generateModelCodeNewBackend(bdae, inClassName, inFileNamePrefix, inSimSettingsOpt, kind); | |
| 2167 | |||
| 2168 | 188 | resultValues := {("timeTemplates", Values.REAL(timeTemplates)), | |
| 2169 | ("timeSimCode", Values.REAL(timeSimCode)), | ||
| 2170 | ("timeBackend", Values.REAL(timeBackend))}; | ||
| 2171 | |||
| 2172 | end translateModelCallBackendNB; | ||
| 2173 | |||
| 2174 | protected function generateModelCodeDAE | ||
| 2175 | " Generates code for a model by creating a SimCode structure for the DAEmode | ||
| 2176 | and call the template target generator. " | ||
| 2177 | input BackendDAE.BackendDAE inBackendDAE; | ||
| 2178 | input BackendDAE.BackendDAE inInitDAE; | ||
| 2179 | input Option<BackendDAE.BackendDAE> initDAE_lambda0_option; | ||
| 2180 | input list<BackendDAE.Equation> inRemovedInitialEquationLst; | ||
| 2181 | input Absyn.Program p; | ||
| 2182 | input Absyn.Path className; | ||
| 2183 | input String filenamePrefix; | ||
| 2184 | input Option<SimCode.SimulationSettings> simSettingsOpt; | ||
| 2185 | input Absyn.FunctionArgs args; | ||
| 2186 | input TranslateModelKind kind = TranslateModelKind.NORMAL() "FMU() to generate an FMU instead of a simulation"; | ||
| 2187 | output list<String> libs; | ||
| 2188 | output String fileDir; | ||
| 2189 | output Real timeSimCode; | ||
| 2190 | output Real timeTemplates; | ||
| 2191 | protected | ||
| 2192 | constant Boolean debug = false; | ||
| 2193 | list<String> includes, includeDirs,libPaths; | ||
| 2194 | list<SimCodeFunction.Function> functions; | ||
| 2195 | SimCode.SimCode simCode; | ||
| 2196 | list<SimCodeFunction.RecordDeclaration> recordDecls; | ||
| 2197 | Absyn.ComponentRef a_cref; | ||
| 2198 | tuple<Integer, UnorderedMap<DAE.Exp, Integer>, list<DAE.Exp>> literals; | ||
| 2199 | list<DAE.Exp> lits; | ||
| 2200 | Integer numCheckpoints; | ||
| 2201 | list<SimCodeVar.SimVar> tempVars = {}; | ||
| 2202 | BackendDAE.BackendDAE emptyBDAE; | ||
| 2203 | BackendDAE.BackendDAE initDAE_lambda0; | ||
| 2204 | |||
| 2205 | SimCode.ModelInfo modelInfo; | ||
| 2206 | SimCode.ExtObjInfo extObjInfo; | ||
| 2207 | SimCode.HashTableCrefToSimVar crefToSimVarHT; | ||
| 2208 | SimCodeFunction.MakefileParams makefileParams; | ||
| 2209 | SimCode.SpatialDistributionInfo spatialInfo; | ||
| 2210 | list<tuple<Integer, tuple<DAE.Exp, DAE.Exp, DAE.Exp>>> delayedExps; | ||
| 2211 | Integer maxDelayedExpIndex; | ||
| 2212 | Integer uniqueEqIndex = 1; | ||
| 2213 | Boolean tmpB; | ||
| 2214 | |||
| 2215 | HashTableCrIListArray.HashTable varToArrayIndexMapping "maps each array-variable to a array of positions"; | ||
| 2216 | HashTableCrILst.HashTable varToIndexMapping "maps each variable to an array position"; | ||
| 2217 | HashTable.HashTable crefToClockIndexHT; | ||
| 2218 | |||
| 2219 | list<DAE.ComponentRef> discreteModelVars; | ||
| 2220 | list<BackendDAE.TimeEvent> timeEvents; | ||
| 2221 | BackendDAE.ZeroCrossingSet zeroCrossingsSet, sampleZCSet; | ||
| 2222 | BackendDAE.ZeroCrossingSet de_relations; | ||
| 2223 | list<BackendDAE.ZeroCrossing> zeroCrossings, sampleZC, relations; | ||
| 2224 | |||
| 2225 | BackendDAE.Variables daeVars, resVars, algStateVars, auxVars; | ||
| 2226 | list<BackendDAE.Var> varsLst; | ||
| 2227 | Option<SimCode.JacobianMatrix> daeModeSP; | ||
| 2228 | Option<SimCode.DaeModeData> daeModeData; | ||
| 2229 | SimCode.DaeModeConfig daeModeConf; | ||
| 2230 | list<String> matrixnames; | ||
| 2231 | list<list<SimCode.SimEqSystem>> daeEquations; | ||
| 2232 | list<SimCodeVar.SimVar> residualVars, algebraicStateVars, auxiliaryVars; | ||
| 2233 | |||
| 2234 | tuple<Option<BackendDAE.SymbolicJacobian>, BackendDAE.SparsePattern, BackendDAE.SparseColoring, BackendDAE.NonlinearPattern> daeModeJacobian; | ||
| 2235 | Option<BackendDAE.SymbolicJacobian> daeModeJac; | ||
| 2236 | Option<BackendDAE.Jacobian> jacH; | ||
| 2237 | BackendDAE.SparsePattern daeModeSparsity; | ||
| 2238 | BackendDAE.SparseColoring daeModeColoring; | ||
| 2239 | BackendDAE.NonlinearPattern nonlinearPattern; | ||
| 2240 | |||
| 2241 | SimCode.JacobianMatrix symDAESparsPattern; | ||
| 2242 | list<SimCode.JacobianMatrix> symJacs, SymbolicJacs, SymbolicJacsNLS, SymbolicJacsTemp; | ||
| 2243 | list<SimCode.SimEqSystem> initialEquations; | ||
| 2244 | list<SimCode.SimEqSystem> initialEquations_lambda0; | ||
| 2245 | list<SimCode.SimEqSystem> removedInitialEquations; | ||
| 2246 | list<SimCodeVar.SimVar> jacobianSimvars, seedVars; | ||
| 2247 | list<SimCode.SimEqSystem> startValueEquations = {}; // --> updateBoundStartValues | ||
| 2248 | list<SimCode.SimEqSystem> maxValueEquations = {}; // --> updateBoundMaxValues | ||
| 2249 | list<SimCode.SimEqSystem> minValueEquations = {}; // --> updateBoundMinValues | ||
| 2250 | list<SimCode.SimEqSystem> nominalValueEquations = {}; // --> updateBoundNominalValues | ||
| 2251 | list<SimCode.SimEqSystem> parameterEquations = {}; // --> updateBoundParameters | ||
| 2252 | list<SimCode.SimEqSystem> jacobianEquations = {}; | ||
| 2253 | |||
| 2254 | Boolean isFMU = false, translateOnly = false; | ||
| 2255 | String FMUVersion = "", FMUType = "", fmuTargetName = "", fullPathPrefix = ""; | ||
| 2256 | Option<SimCode.FmiModelStructure> modelStructure = NONE(); | ||
| 2257 | Option<SimCode.FmiSimulationFlags> fmiSimulationFlags = NONE(); | ||
| 2258 | algorithm | ||
| 2259 | () := match kind | ||
| 2260 | case TranslateModelKind.FMU() | ||
| 2261 | algorithm | ||
| 2262 | ✗ | isFMU := true; | |
| 2263 | ✗ | FMUVersion := FMI.getFMIVersionString(); | |
| 2264 | ✗ | FMUType := kind.kind; | |
| 2265 | ✗ | fmuTargetName := kind.targetName; | |
| 2266 | ✗ | translateOnly := kind.translateOnly; | |
| 2267 | // Where the C target writes the generated sources. | ||
| 2268 | ✗ | fullPathPrefix := Util.hashFileNamePrefix(filenamePrefix) + ".fmutmp/sources/"; | |
| 2269 | then (); | ||
| 2270 | else (); | ||
| 2271 | end match; | ||
| 2272 | |||
| 2273 | 9 | numCheckpoints:=ErrorExt.getNumCheckpoints(); | |
| 2274 | try | ||
| 2275 | 9 | StackOverflow.clearStacktraceMessages(); | |
| 2276 | 9 | System.realtimeTick(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 2277 | |||
| 2278 | // +++ create SimCode stuff +++ | ||
| 2279 | // create SimCode functions | ||
| 2280 | 9 | a_cref := AbsynUtil.pathToCref(className); | |
| 2281 | 9 | fileDir := ProgramUtil.getFileDir(a_cref, p); | |
| 2282 | 9 | (libs, libPaths, includes, includeDirs, recordDecls, functions, literals) := SimCodeUtilShared.createFunctions(p, inBackendDAE.shared.functionTree); | |
| 2283 | |||
| 2284 | // create external objects | ||
| 2285 | 9 | extObjInfo := SimCodeUtil.createExtObjInfo(inBackendDAE.shared); | |
| 2286 | // create make file params | ||
| 2287 | 9 | makefileParams := SimCodeFunctionUtil.createMakefileParams(includeDirs, libs, libPaths, false, false); | |
| 2288 | //create delay exps | ||
| 2289 | 9 | (delayedExps, maxDelayedExpIndex) := SimCodeUtil.extractDelayedExpressions(inBackendDAE); | |
| 2290 | 9 | spatialInfo := SimCodeUtil.extractSpatialDistributionInfo(inBackendDAE); | |
| 2291 | |||
| 2292 | // created event suff e.g. zeroCrossings, samples, ... | ||
| 2293 | 9 | timeEvents := inBackendDAE.shared.eventInfo.timeEvents; | |
| 2294 | (zeroCrossings,relations,sampleZC) := match inBackendDAE.shared.eventInfo | ||
| 2295 | case BackendDAE.EVENT_INFO(zeroCrossings=zeroCrossingsSet, relations=de_relations, samples=sampleZCSet) | ||
| 2296 | 9 | then (ZeroCrossings.toList(zeroCrossingsSet), ZeroCrossings.toList(de_relations), ZeroCrossings.toList(sampleZCSet)); | |
| 2297 | end match; | ||
| 2298 | |||
| 2299 | // initialization stuff | ||
| 2300 | // ******************** | ||
| 2301 | |||
| 2302 | // generate equations for initDAE | ||
| 2303 | 9 | (initialEquations, uniqueEqIndex, tempVars) := SimCodeUtil.createInitialEquations(inInitDAE, uniqueEqIndex, tempVars); | |
| 2304 | |||
| 2305 | // generate equations for initDAE_lambda0 | ||
| 2306 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 7 times.
|
9 | if isSome(initDAE_lambda0_option) then |
| 2307 | 2 | SOME(initDAE_lambda0) := initDAE_lambda0_option; | |
| 2308 | 2 | (initialEquations_lambda0, uniqueEqIndex, tempVars) := SimCodeUtil.createInitialEquations_lambda0(initDAE_lambda0, uniqueEqIndex, tempVars); | |
| 2309 | else | ||
| 2310 | initialEquations_lambda0 := {}; | ||
| 2311 | end if; | ||
| 2312 | |||
| 2313 | // generate equations for removed initial equations | ||
| 2314 | 9 | (removedInitialEquations, (uniqueEqIndex, _), tempVars) := SimCodeUtil.createNonlinearResidualEquations(inRemovedInitialEquationLst, (uniqueEqIndex, 0), tempVars, inBackendDAE.shared.functionTree); | |
| 2315 | //removedInitialEquations := listReverse(removedInitialEquations); | ||
| 2316 | |||
| 2317 | 9 | ExecStat.execStat("simCode: created initialization part"); | |
| 2318 | |||
| 2319 | // create parameter equations | ||
| 2320 | 9 | (uniqueEqIndex, startValueEquations, _) := BackendDAEUtil.foldEqSystem(inInitDAE, SimCodeUtil.createStartValueEquations, (uniqueEqIndex, {}, inBackendDAE.shared.globalKnownVars)); | |
| 2321 | if debug then ExecStat.execStat("simCode: createStartValueEquations"); end if; | ||
| 2322 | |||
| 2323 | 9 | (uniqueEqIndex, nominalValueEquations) := SimCodeUtil.createValueEquationsShared(inBackendDAE.shared, SimCodeUtil.createInitialAssignmentsFromNominal, (uniqueEqIndex, nominalValueEquations)); | |
| 2324 | if debug then ExecStat.execStat("simCode: createNominalValueEquationsShared"); end if; | ||
| 2325 | 9 | (uniqueEqIndex, nominalValueEquations) := BackendDAEUtil.foldEqSystem(inBackendDAE, SimCodeUtil.createNominalValueEquations, (uniqueEqIndex, nominalValueEquations)); | |
| 2326 | if debug then ExecStat.execStat("simCode: createNominalValueEquations"); end if; | ||
| 2327 | |||
| 2328 | 9 | (uniqueEqIndex, minValueEquations) := SimCodeUtil.createValueEquationsShared(inBackendDAE.shared, SimCodeUtil.createInitialAssignmentsFromMin, (uniqueEqIndex, minValueEquations)); | |
| 2329 | if debug then ExecStat.execStat("simCode: createMinValueEquationsShared"); end if; | ||
| 2330 | 9 | (uniqueEqIndex, minValueEquations) := BackendDAEUtil.foldEqSystem(inBackendDAE, SimCodeUtil.createMinValueEquations, (uniqueEqIndex, minValueEquations)); | |
| 2331 | if debug then ExecStat.execStat("simCode: createMinValueEquations"); end if; | ||
| 2332 | |||
| 2333 | 9 | (uniqueEqIndex, maxValueEquations) := SimCodeUtil.createValueEquationsShared(inBackendDAE.shared, SimCodeUtil.createInitialAssignmentsFromMax, (uniqueEqIndex, maxValueEquations)); | |
| 2334 | if debug then ExecStat.execStat("simCode: createMaxValueEquationsShared"); end if; | ||
| 2335 | 9 | (uniqueEqIndex, maxValueEquations) := BackendDAEUtil.foldEqSystem(inBackendDAE, SimCodeUtil.createMaxValueEquations, (uniqueEqIndex, maxValueEquations)); | |
| 2336 | if debug then ExecStat.execStat("simCode: createMaxValueEquations"); end if; | ||
| 2337 | |||
| 2338 | 9 | (uniqueEqIndex, parameterEquations, _) := SimCodeUtil.createParameterEquations(uniqueEqIndex, parameterEquations, inBackendDAE.shared.globalKnownVars); | |
| 2339 | if debug then ExecStat.execStat("simCode: createParameterEquations"); end if; | ||
| 2340 | |||
| 2341 | 9 | discreteModelVars := BackendDAEUtil.foldEqSystem(inBackendDAE, SimCodeUtil.extractDiscreteModelVars, {}); | |
| 2342 | |||
| 2343 | // create DAEmode equations | ||
| 2344 | 9 | (daeEquations, uniqueEqIndex, tempVars) := SimCodeUtil.createEquationsfromBackendDAE(inBackendDAE, uniqueEqIndex, tempVars, true, true); | |
| 2345 | |||
| 2346 | // create model info | ||
| 2347 | // create dummy system where all original variables are created | ||
| 2348 | 18 | emptyBDAE := BackendDAE.DAE(BackendDAEUtil.createEqSystem( | |
| 2349 | Util.getOption(inBackendDAE.shared.daeModeData.modelVars))::{}, | ||
| 2350 | inBackendDAE.shared); | ||
| 2351 | // disable start value calculation, it's only helpful in case of algebraic loops | ||
| 2352 | // and they are not present in DAEmode | ||
| 2353 | |||
| 2354 | // create DAE mode Sparse pattern and TODO: Jacobians | ||
| 2355 | // sparsity pattern generation | ||
| 2356 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
9 | if Flags.getConfigString(Flags.GENERATE_DYNAMIC_JACOBIAN) == "symbolic" then |
| 2357 | // create symbolic jacobian (like nls systems!) | ||
| 2358 | ✗ | (daeModeJac, daeModeSparsity, daeModeColoring, nonlinearPattern) := listGet(inBackendDAE.shared.symjacs, BackendDAE.SymbolicJacobianAIndex); | |
| 2359 | ✗ | if isSome(inBackendDAE.shared.dataReconciliationData) then | |
| 2360 | ✗ | BackendDAE.DATA_RECON(_, _, _, _, jacH) := Util.getOption(inBackendDAE.shared.dataReconciliationData); | |
| 2361 | ✗ | if isSome(jacH) then | |
| 2362 | matrixnames := {"B", "C", "D", "ADJ"}; | ||
| 2363 | else | ||
| 2364 | matrixnames := {"B", "C", "D", "H", "ADJ"}; | ||
| 2365 | end if; | ||
| 2366 | else | ||
| 2367 | matrixnames := {"B", "C", "D", "F", "H", "ADJ"}; | ||
| 2368 | end if; | ||
| 2369 | ✗ | (daeModeSP, uniqueEqIndex, tempVars) := SimCodeUtil.createSymbolicSimulationJacobian( | |
| 2370 | inJacobian = BackendDAE.GENERIC_JACOBIAN(daeModeJac, daeModeSparsity, daeModeColoring, nonlinearPattern), | ||
| 2371 | iuniqueEqIndex = uniqueEqIndex, | ||
| 2372 | itempvars = tempVars); | ||
| 2373 | ✗ | tmpB := FlagsUtil.set(Flags.NO_START_CALC, true); | |
| 2374 | ✗ | modelInfo := SimCodeUtil.createModelInfo(className, p, emptyBDAE, inInitDAE, functions, {}, 0, spatialInfo.maxIndex, fileDir, 0, tempVars); | |
| 2375 | ✗ | FlagsUtil.set(Flags.NO_START_CALC, tmpB); | |
| 2376 | //create hash table | ||
| 2377 | ✗ | crefToSimVarHT := SimCodeCodegenUtil.createCrefToSimVarHT(modelInfo); | |
| 2378 | ✗ | (symJacs, uniqueEqIndex) := SimCodeUtil.createSymbolicJacobianssSimCode({}, crefToSimVarHT, uniqueEqIndex, matrixnames, {}); | |
| 2379 | ✗ | symJacs := listReverse(Util.getOption(daeModeSP) :: symJacs); | |
| 2380 | else | ||
| 2381 | 9 | tmpB := FlagsUtil.set(Flags.NO_START_CALC, true); | |
| 2382 | 9 | modelInfo := SimCodeUtil.createModelInfo(className, p, emptyBDAE, inInitDAE, functions, {}, 0, spatialInfo.maxIndex, fileDir, 0, tempVars); | |
| 2383 | 9 | FlagsUtil.set(Flags.NO_START_CALC, tmpB); | |
| 2384 | 9 | crefToSimVarHT := SimCodeCodegenUtil.createCrefToSimVarHT(modelInfo); | |
| 2385 | |||
| 2386 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
9 | if isSome(inBackendDAE.shared.dataReconciliationData) then |
| 2387 | ✗ | BackendDAE.DATA_RECON(_, _, _, _, jacH) := Util.getOption(inBackendDAE.shared.dataReconciliationData); | |
| 2388 | ✗ | if isSome(jacH) then | |
| 2389 | matrixnames := {"A", "B", "C", "D", "ADJ"}; | ||
| 2390 | else | ||
| 2391 | matrixnames := {"A", "B", "C", "D", "H", "ADJ"}; | ||
| 2392 | end if; | ||
| 2393 | else | ||
| 2394 | matrixnames := {"A", "B", "C", "D", "F", "H", "ADJ"}; | ||
| 2395 | end if; | ||
| 2396 | 9 | (symJacs, uniqueEqIndex) := SimCodeUtil.createSymbolicJacobianssSimCode({}, crefToSimVarHT, uniqueEqIndex, matrixnames, {}); | |
| 2397 | end if; | ||
| 2398 | |||
| 2399 | // collect symbolic jacobians in initialization loops of the overall jacobians | ||
| 2400 | SymbolicJacsNLS := {}; | ||
| 2401 | 9 | (initialEquations, modelInfo, SymbolicJacsTemp) := SimCodeUtil.addAlgebraicLoopsModelInfo(initialEquations, modelInfo); | |
| 2402 | 9 | SymbolicJacsNLS := listAppend(SymbolicJacsTemp, SymbolicJacsNLS); | |
| 2403 | 9 | (initialEquations_lambda0, modelInfo, SymbolicJacsTemp) := SimCodeUtil.addAlgebraicLoopsModelInfo(initialEquations_lambda0, modelInfo); | |
| 2404 | 9 | SymbolicJacsNLS := listAppend(SymbolicJacsTemp, SymbolicJacsNLS); | |
| 2405 | 9 | (parameterEquations, modelInfo, SymbolicJacsTemp) := SimCodeUtil.addAlgebraicLoopsModelInfo(parameterEquations, modelInfo); | |
| 2406 | 9 | SymbolicJacsNLS := listAppend(SymbolicJacsTemp, SymbolicJacsNLS); | |
| 2407 | // check for datareconciliation is present and pass the matrixnames | ||
| 2408 | |||
| 2409 | //(_, modelInfo, symJacs) := SimCodeUtil.addAlgebraicLoopsModelInfoSymJacs(inBackendDAE.shared.symjacs, modelInfo); | ||
| 2410 | 9 | (SymbolicJacs, modelInfo, SymbolicJacsTemp) := SimCodeUtil.addAlgebraicLoopsModelInfoSymJacs(symJacs, modelInfo); | |
| 2411 | |||
| 2412 | // collect jacobian equation only for equantion info file | ||
| 2413 | 9 | jacobianEquations := SimCodeUtil.collectAllJacobianEquations(SymbolicJacs); | |
| 2414 | if debug then ExecStat.execStat("simCode: create Jacobian linear code"); end if; | ||
| 2415 | |||
| 2416 | 9 | SymbolicJacs := listAppend(listReverse(SymbolicJacsNLS), listAppend(SymbolicJacs, SymbolicJacsTemp)); | |
| 2417 | 9 | jacobianSimvars := SimCodeUtil.collectAllJacobianVars(SymbolicJacs); | |
| 2418 | 9 | modelInfo := SimCodeUtil.setJacobianVars(jacobianSimvars, modelInfo); | |
| 2419 | 9 | crefToSimVarHT:= List.fold(jacobianSimvars, HashTableCrefSimVar.addSimVarToHashTable, crefToSimVarHT); | |
| 2420 | 9 | seedVars := SimCodeUtil.collectAllSeedVars(SymbolicJacs); | |
| 2421 | 9 | modelInfo := SimCodeUtil.setSeedVars(seedVars, modelInfo); | |
| 2422 | 9 | crefToSimVarHT := List.fold(seedVars, HashTableCrefSimVar.addSimVarToHashTable, crefToSimVarHT); | |
| 2423 | |||
| 2424 | // create dae SimVars: residual and algebraic | ||
| 2425 | 9 | varsLst := BackendVariable.equationSystemsVarsLst(inBackendDAE.eqs); | |
| 2426 | 9 | daeVars := BackendVariable.listVar(varsLst); | |
| 2427 | |||
| 2428 | // create residual variables, set index and push them SimCode HashTable | ||
| 2429 | 9 | (_, resVars) := BackendVariable.traverseBackendDAEVars(daeVars, BackendVariable.collectVarKindVarinVariables, (BackendVariable.isDAEmodeResVar, BackendVariable.emptyVars())); | |
| 2430 | 9 | (residualVars, _) := BackendVariable.traverseBackendDAEVars(resVars, SimCodeUtil.traversingdlowvarToSimvar, ({}, BackendVariable.emptyVars())); | |
| 2431 | 9 | residualVars := SimCodeUtil.rewriteIndex(residualVars, 0); | |
| 2432 | 9 | (residualVars, _) := SimCodeCodegenUtil.setVariableIndexHelper(residualVars, 0, 0); | |
| 2433 | 9 | crefToSimVarHT:= List.fold(residualVars,HashTableCrefSimVar.addSimVarToHashTable,crefToSimVarHT); | |
| 2434 | |||
| 2435 | // create auxiliary variables, set index and push them SimCode Hash Table | ||
| 2436 | 9 | (_, auxVars) := BackendVariable.traverseBackendDAEVars(daeVars, BackendVariable.collectVarKindVarinVariables, (BackendVariable.isDAEmodeAuxVar, BackendVariable.emptyVars())); | |
| 2437 | 9 | (auxiliaryVars, _) := BackendVariable.traverseBackendDAEVars(auxVars, SimCodeUtil.traversingdlowvarToSimvar, ({}, BackendVariable.emptyVars())); | |
| 2438 | 9 | auxiliaryVars := List.sort(auxiliaryVars, SimCodeUtil.simVarCompareByCrefSubsAtEndlLexical); | |
| 2439 | 9 | auxiliaryVars := SimCodeUtil.rewriteIndex(auxiliaryVars, 0); | |
| 2440 | 9 | (auxiliaryVars, _) := SimCodeCodegenUtil.setVariableIndexHelper(auxiliaryVars, 0, 0); | |
| 2441 | 9 | crefToSimVarHT:= List.fold(auxiliaryVars,HashTableCrefSimVar.addSimVarToHashTable,crefToSimVarHT); | |
| 2442 | |||
| 2443 | // create SimCodeVars for algebraic states | ||
| 2444 | 9 | algStateVars := BackendVariable.listVar(inBackendDAE.shared.daeModeData.algStateVars); | |
| 2445 | 9 | (algebraicStateVars, _) := BackendVariable.traverseBackendDAEVars(algStateVars, SimCodeUtil.traversingdlowvarToSimvar, ({}, BackendVariable.emptyVars())); | |
| 2446 | |||
| 2447 | 9 | algebraicStateVars := SimCodeUtil.sortSimVarsAndWriteIndex(algebraicStateVars, crefToSimVarHT); | |
| 2448 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if isFMU then |
| 2449 | ✗ | modelInfo := SimCodeUtil.exportDaeAlgebraicStates(modelInfo, algebraicStateVars); | |
| 2450 | end if; | ||
| 2451 | |||
| 2452 | // only create sparsity pattern for dae mode data even if it is created with --generateDynamicJacobian=symbolic | ||
| 2453 | // the A matrix will be used symbolically | ||
| 2454 | // (also the A matrix sparsity pattern seems to be faulty so we use this one instead) | ||
| 2455 | 9 | daeModeJacobian := listGet(inBackendDAE.shared.symjacs, BackendDAE.SymbolicJacobianAIndex); | |
| 2456 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
|
9 | ({symDAESparsPattern}, uniqueEqIndex) := SimCodeUtil.createSymbolicJacobianssSimCode({daeModeJacobian}, crefToSimVarHT, uniqueEqIndex, {"daeMode"}, {}); |
| 2457 | daeModeSP := SOME(symDAESparsPattern); | ||
| 2458 | |||
| 2459 | // copy the sparsity pattern to the A jacobian | ||
| 2460 |
1/4✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
9 | if Flags.getConfigString(Flags.GENERATE_DYNAMIC_JACOBIAN) == "symbolic" then |
| 2461 | ✗ | SymbolicJacs := list(SimCodeUtil.syncDAEandSimJac(symjac, symDAESparsPattern) for symjac in SymbolicJacs); | |
| 2462 | end if; | ||
| 2463 | |||
| 2464 | daeModeConf := SimCode.ALL_EQUATIONS(); | ||
| 2465 | 9 | daeModeData := SOME(SimCode.DAEMODEDATA(daeEquations, daeModeSP, residualVars, algebraicStateVars, auxiliaryVars, daeModeConf)); | |
| 2466 | |||
| 2467 | /* This is a *much* better estimate than the guessed number of equations */ | ||
| 2468 | 9 | modelInfo := SimCodeUtil.addNumEqns(modelInfo, uniqueEqIndex - listLength(jacobianEquations)); | |
| 2469 | |||
| 2470 | // No FMIDER jacobian: it is an ODE right-hand-side derivative, which a | ||
| 2471 | // DAE-mode model has none of. The specification lets an importer assume a | ||
| 2472 | // dependency on every known instead. | ||
| 2473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if isFMU then |
| 2474 | ✗ | System.realtimeTick(ClockIndexes.RT_CLOCK_FMU_SIMCODE); | |
| 2475 | ✗ | if FMI.isFMIVersion20(FMUVersion) or FMI.isFMIVersion30(FMUVersion) then | |
| 2476 | ✗ | (_, modelStructure, modelInfo, _, uniqueEqIndex, _) := | |
| 2477 | SimCodeUtil.createFMIModelStructure({}, modelInfo, uniqueEqIndex, inInitDAE, inBackendDAE); | ||
| 2478 | end if; | ||
| 2479 | ✗ | fmiSimulationFlags := SimCodeUtil.createFMISimulationFlags(); | |
| 2480 | ✗ | System.realtimeAccumulate(ClockIndexes.RT_CLOCK_FMU_SIMCODE); | |
| 2481 | end if; | ||
| 2482 | |||
| 2483 | // update hash table | ||
| 2484 | // mahge: This creates a new crefToSimVarHT discarding everything added upto here | ||
| 2485 | // The updated variable 'numEquations' (by SimCodeUtil.addNumEqns) is not even used in createCrefToSimVarHT :/ | ||
| 2486 | // crefToSimVarHT := SimCodeUtil.createCrefToSimVarHT(modelInfo); | ||
| 2487 | |||
| 2488 |
3/4✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
9 | if stringEqual(Config.simCodeTarget(), "Cpp") then |
| 2489 | 2 | (varToArrayIndexMapping, varToIndexMapping) := SimCodeUtilShared.createVarToArrayIndexMapping(modelInfo); | |
| 2490 | 2 | (crefToClockIndexHT, _) := List.fold(listReverse(inBackendDAE.eqs), SimCodeUtil.collectClockedVars, (HashTable.emptyHashTable(), 1)); | |
| 2491 | else | ||
| 2492 | 7 | varToArrayIndexMapping := HashTableCrIListArray.emptyHashTable(); | |
| 2493 | 7 | varToIndexMapping := HashTableCrILst.emptyHashTable(); | |
| 2494 | 7 | crefToClockIndexHT := HashTable.emptyHashTable(); | |
| 2495 | end if; | ||
| 2496 | |||
| 2497 | 9 | setGlobalRoot(Global.timeIndependentVars, NONE()); | |
| 2498 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | simCode := SimCode.SIMCODE( |
| 2499 | modelInfo = modelInfo, | ||
| 2500 | literals = {}, // Set by the traversal below... | ||
| 2501 | recordDecls = recordDecls, | ||
| 2502 | externalFunctionIncludes = includes, | ||
| 2503 | generic_loop_calls = {}, // only used in new backend | ||
| 2504 | localKnownVars = {}, | ||
| 2505 | allEquations = {}, | ||
| 2506 | odeEquations = {}, | ||
| 2507 | algebraicEquations = {}, | ||
| 2508 | clockedPartitions = {}, | ||
| 2509 | initialEquations = initialEquations, | ||
| 2510 | initialEquations_lambda0 = initialEquations_lambda0, | ||
| 2511 | removedInitialEquations = removedInitialEquations, | ||
| 2512 | startValueEquations = startValueEquations, | ||
| 2513 | nominalValueEquations = nominalValueEquations, | ||
| 2514 | minValueEquations = minValueEquations, | ||
| 2515 | maxValueEquations = maxValueEquations, | ||
| 2516 | parameterEquations = parameterEquations, | ||
| 2517 | removedEquations = {}, | ||
| 2518 | algorithmAndEquationAsserts = {}, | ||
| 2519 | equationsForZeroCrossings = {}, | ||
| 2520 | jacobianEquations = jacobianEquations, | ||
| 2521 | stateSets = {}, | ||
| 2522 | constraints = {}, | ||
| 2523 | classAttributes = {}, | ||
| 2524 | zeroCrossings = FindZeroCrossings.setOperatorZeroCrossingIndices(ZeroCrossings.updateIndices(zeroCrossings)), | ||
| 2525 | relations = ZeroCrossings.updateIndices(relations), | ||
| 2526 | timeEvents = timeEvents, | ||
| 2527 | discreteModelVars = discreteModelVars, | ||
| 2528 | extObjInfo = extObjInfo, | ||
| 2529 | makefileParams = makefileParams, | ||
| 2530 | delayedExps = SimCode.DELAYED_EXPRESSIONS(delayedExps, maxDelayedExpIndex), | ||
| 2531 | spatialInfo = spatialInfo, | ||
| 2532 | jacobianMatrices = SymbolicJacs, | ||
| 2533 | simulationSettingsOpt = simSettingsOpt, | ||
| 2534 | fileNamePrefix = filenamePrefix, | ||
| 2535 | fullPathPrefix = fullPathPrefix, | ||
| 2536 | fmuTargetName = fmuTargetName, | ||
| 2537 | hpcomData = HpcOmSimCode.emptyHpcomData, | ||
| 2538 | valueReferences = if isFMU then SimCodeUtil.getValueReferenceMapping(modelInfo) else AvlTreeCRToInt.EMPTY(), | ||
| 2539 | varToArrayIndexMapping = varToArrayIndexMapping, | ||
| 2540 | varToIndexMapping = varToIndexMapping, | ||
| 2541 | crefToSimVarHT = crefToSimVarHT, | ||
| 2542 | crefToClockIndexHT = crefToClockIndexHT, | ||
| 2543 | backendMapping = NONE(), | ||
| 2544 | modelStructure = modelStructure, | ||
| 2545 | fmiSimulationFlags = fmiSimulationFlags, | ||
| 2546 | partitionData = SimCode.emptyPartitionData, | ||
| 2547 | daeModeData = daeModeData, | ||
| 2548 | inlineEquations = {}, | ||
| 2549 | scalarized = true, | ||
| 2550 | fmiFigures = {} | ||
| 2551 | ); | ||
| 2552 | |||
| 2553 | 9 | (simCode, lits) := SimCodeUtil.findSimCodeLiterals(simCode, literals); | |
| 2554 | 9 | simCode.literals := lits; | |
| 2555 | |||
| 2556 | 9 | timeSimCode := System.realtimeTock(ClockIndexes.RT_CLOCK_SIMCODE); | |
| 2557 | 9 | ExecStat.execStat("SimCode"); | |
| 2558 | |||
| 2559 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if Flags.isSet(Flags.SERIALIZED_SIZE) then |
| 2560 | ✗ | serializeNotify(simCode, "SimCode"); | |
| 2561 | ✗ | ExecStat.execStat("Serialize simCode"); | |
| 2562 | end if; | ||
| 2563 | |||
| 2564 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | if Flags.isSet(Flags.DUMP_SIMCODE) then |
| 2565 | ✗ | SimCodeUtil.dumpSimCodeDebug(simCode); | |
| 2566 | end if; | ||
| 2567 | |||
| 2568 | 9 | System.realtimeTick(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 2569 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if isFMU then |
| 2570 | ✗ | callTargetTemplatesFMU(simCode, Config.simCodeTarget(), FMUVersion, FMUType, p, translateOnly); | |
| 2571 | else | ||
| 2572 | 9 | callTargetTemplates(simCode, Config.simCodeTarget()); | |
| 2573 | end if; | ||
| 2574 | 9 | timeTemplates := System.realtimeTock(ClockIndexes.RT_CLOCK_TEMPLATES); | |
| 2575 | 9 | ExecStat.execStat("Templates"); | |
| 2576 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | return; |
| 2577 | else | ||
| 2578 | ✗ | setGlobalRoot(Global.stackoverFlowIndex, NONE()); | |
| 2579 | ✗ | ErrorExt.rollbackNumCheckpoints(ErrorExt.getNumCheckpoints()-numCheckpoints); | |
| 2580 | ✗ | Error.addInternalError("Stack overflow in "+getInstanceName()+"...\n"+stringDelimitList(StackOverflow.readableStacktraceMessages(), "\n"), sourceInfo()); | |
| 2581 | /* Do not fail or we can loop too much */ | ||
| 2582 | ✗ | StackOverflow.clearStacktraceMessages(); | |
| 2583 | end try annotation(__OpenModelica_stackOverflowCheckpoint=true); | ||
| 2584 | ✗ | fail(); | |
| 2585 | end generateModelCodeDAE; | ||
| 2586 | |||
| 2587 | protected function serializeNotify<T> | ||
| 2588 | input T data; | ||
| 2589 | input String name; | ||
| 2590 | protected | ||
| 2591 | Real sz,raw_sz,nonSharedStringSize; | ||
| 2592 | algorithm | ||
| 2593 | ✗ | (sz,raw_sz,nonSharedStringSize) := System.getSizeOfData(data); | |
| 2594 | ✗ | Error.addMessage(Error.SERIALIZED_SIZE, {name, StringUtil.bytesToReadableUnit(sz), StringUtil.bytesToReadableUnit(raw_sz), StringUtil.bytesToReadableUnit(nonSharedStringSize)}); | |
| 2595 | end serializeNotify; | ||
| 2596 | |||
| 2597 | protected function copyFiles | ||
| 2598 | input list<String> files; | ||
| 2599 | input String source, destination; | ||
| 2600 | protected | ||
| 2601 | String f2, d2; | ||
| 2602 | algorithm | ||
| 2603 |
2/2✓ Branch 0 taken 12492 times.
✓ Branch 1 taken 753 times.
|
13245 | for f in files loop |
| 2604 | 12492 | f2 := destination+"/"+f; | |
| 2605 | 12492 | d2 := System.dirname(f2); | |
| 2606 |
2/2✓ Branch 1 taken 984 times.
✓ Branch 2 taken 11508 times.
|
12492 | if not System.directoryExists(d2) then |
| 2607 | 984 | Error.assertion(Util.createDirectoryTree(d2), "Failed to create directory " + d2, sourceInfo()); | |
| 2608 | end if; | ||
| 2609 | 12492 | Error.assertion(System.copyFile(source + "/" + f, f2), "Failed to copy file " + f + " from " + source + " to " + destination, sourceInfo()); | |
| 2610 | end for; | ||
| 2611 | end copyFiles; | ||
| 2612 | |||
| 2613 | public function copyFmuRustSources | ||
| 2614 | "The Rust half of a `--simCodeTarget=C` source FMU: the crates | ||
| 2615 | libSimulationRuntimeRust is built from, so the FMU rebuilds where OpenModelica | ||
| 2616 | is not installed. | ||
| 2617 | |||
| 2618 | `--fmiSources=full` additionally vendors the crates.io dependencies beside them, | ||
| 2619 | which makes the rebuild work without network access. The vendored tree is ~90 MB | ||
| 2620 | and the same for every FMU, so it is built once into the user's cache and copied | ||
| 2621 | from there. It is deliberately not part of the OpenModelica installation." | ||
| 2622 | input String fmutmp; | ||
| 2623 | protected | ||
| 2624 | String rust_sources_dir, dest, manifest, vendor, cache, lock; | ||
| 2625 | algorithm | ||
| 2626 | 7 | rust_sources_dir := Settings.getInstallationDirectoryPath() + RuntimeSources.fmu_rust_sources_dir; | |
| 2627 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
|
7 | if not System.directoryExists(rust_sources_dir) then |
| 2628 | ✗ | Error.addCompilerWarning("--fmiSources asked for the sources of a --simCodeTarget=C FMU, but " | |
| 2629 | + rust_sources_dir + " does not exist. The FMU carries its C sources only and cannot be rebuilt."); | ||
| 2630 | ✗ | return; | |
| 2631 | end if; | ||
| 2632 | 7 | dest := fmutmp + "/sources/rust"; | |
| 2633 | 7 | Error.assertion(Util.createDirectoryTree(dest), "Failed to create directory " + dest, sourceInfo()); | |
| 2634 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
|
7 | if not System.copyPath(rust_sources_dir, dest) then |
| 2635 | ✗ | Error.addInternalError("Failed to copy the Rust runtime sources into " + dest, sourceInfo()); | |
| 2636 | ✗ | return; | |
| 2637 | end if; | ||
| 2638 | |||
| 2639 | 7 | manifest := dest + "/" + RuntimeSources.fmu_rust_manifest; | |
| 2640 | 7 | writeFmuRustWorkspace(manifest); | |
| 2641 | |||
| 2642 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
7 | if Flags.getConfigEnum(Flags.FMI_SOURCES) <> Flags.FMI_SOURCES_FULL then |
| 2643 | 7 | return; | |
| 2644 | end if; | ||
| 2645 | |||
| 2646 | ✗ | lock := System.dirname(manifest) + "/Cargo.lock"; | |
| 2647 | // Keyed by the lock file: a different dependency set is a different vendor tree. | ||
| 2648 | ✗ | cache := Settings.getHomeDir(runningTestsuite=Testsuite.isRunning()) + "/.openmodelica/fmu-rust-vendor/" | |
| 2649 | + intString(stringHashDjb2(if System.regularFileExists(lock) then System.readFile(lock) else "")); | ||
| 2650 | ✗ | if not System.directoryExists(cache) then | |
| 2651 | ✗ | Error.assertion(Util.createDirectoryTree(cache), "Failed to create directory " + cache, sourceInfo()); | |
| 2652 | ✗ | if 0 <> System.systemCall("cargo vendor --versioned-dirs --manifest-path \"" + manifest + "\" \"" + cache + "\"", | |
| 2653 | outFile=fmutmp + "/resources/cargo-vendor.log") then | ||
| 2654 | ✗ | System.removeDirectory(cache); | |
| 2655 | ✗ | Error.addCompilerError("--fmiSources=full needs `cargo vendor` to collect the Rust dependencies, and it failed. " | |
| 2656 | + "See " + fmutmp + "/resources/cargo-vendor.log. Use --fmiSources=slim for an FMU whose rebuild fetches them instead."); | ||
| 2657 | ✗ | return; | |
| 2658 | end if; | ||
| 2659 | end if; | ||
| 2660 | |||
| 2661 | ✗ | vendor := dest + "/vendor"; | |
| 2662 | ✗ | if not System.copyPath(cache, vendor) then | |
| 2663 | ✗ | Error.addInternalError("Failed to copy the vendored Rust dependencies into " + vendor, sourceInfo()); | |
| 2664 | ✗ | return; | |
| 2665 | end if; | ||
| 2666 | // Cargo finds this by walking up from its working directory, which the FMU's | ||
| 2667 | // CMakeLists sets to `sources/rust`; the path in it is relative to that same | ||
| 2668 | // directory, so the FMU stays movable. | ||
| 2669 | ✗ | Error.assertion(Util.createDirectoryTree(dest + "/.cargo"), "Failed to create directory " + dest + "/.cargo", sourceInfo()); | |
| 2670 | ✗ | System.writeFile(dest + "/.cargo/config.toml", | |
| 2671 | "[source.crates-io]\nreplace-with = \"vendored-sources\"\n\n[source.vendored-sources]\ndirectory = \"vendor\"\n"); | ||
| 2672 | end copyFmuRustSources; | ||
| 2673 | |||
| 2674 | protected function fmuRustCrateRoots | ||
| 2675 | "The Rust half of a source FMU's <SourceFiles>: the root of every crate under | ||
| 2676 | `dir` (relative to `sources`), the unit rustc compiles, as a .c file is for C. | ||
| 2677 | The vendored dependencies are left out, as a library a C FMU links would be." | ||
| 2678 | input String sources; | ||
| 2679 | input String dir; | ||
| 2680 | output list<String> roots = {}; | ||
| 2681 | algorithm | ||
| 2682 |
2/2✓ Branch 4 taken 266 times.
✓ Branch 5 taken 175 times.
|
441 | for d in listReverse(List.sort(System.subDirectories(sources + dir), Util.strcmpBool)) loop |
| 2683 |
2/2✓ Branch 1 taken 168 times.
✓ Branch 2 taken 98 times.
|
266 | if not listMember(d, {"vendor", "target", "src", ".cargo"}) then |
| 2684 | 168 | roots := listAppend(fmuRustCrateRoots(sources, dir + "/" + d), roots); | |
| 2685 | end if; | ||
| 2686 | end for; | ||
| 2687 |
3/4✓ Branch 5 taken 77 times.
✓ Branch 6 taken 98 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 98 times.
|
175 | if System.regularFileExists(sources + dir + "/Cargo.toml") and System.regularFileExists(sources + dir + "/src/lib.rs") then |
| 2688 | 98 | roots := (dir + "/src/lib.rs") :: roots; | |
| 2689 | end if; | ||
| 2690 | end fmuRustCrateRoots; | ||
| 2691 | |||
| 2692 | protected function writeFmuRustWorkspace | ||
| 2693 | "The FMU's own cargo workspace root, in place of the one the checkout uses. | ||
| 2694 | |||
| 2695 | That one reaches the result-file library and the browser module too, and those | ||
| 2696 | pull in crates an FMU has no use for and does not carry -- `cargo vendor` stops | ||
| 2697 | at the first one it cannot find. This root names `openmodelica_simulation_runtime` | ||
| 2698 | and nothing else, and excludes every crate beside it so cargo does not claim | ||
| 2699 | them as members of it." | ||
| 2700 | input String manifest; | ||
| 2701 | protected | ||
| 2702 | String dir, excludes; | ||
| 2703 | list<String> subdirs; | ||
| 2704 | algorithm | ||
| 2705 | 7 | dir := System.dirname(manifest); | |
| 2706 |
6/6✓ Branch 5 taken 7 times.
✓ Branch 6 taken 77 times.
✓ Branch 7 taken 84 times.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 77 times.
✓ Branch 10 taken 7 times.
|
91 | subdirs := List.sort(list(d for d guard System.regularFileExists(dir + "/" + d + "/Cargo.toml") |
| 2707 | in System.subDirectories(dir)), Util.strcmpBool); | ||
| 2708 |
4/4✓ Branch 0 taken 77 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 77 times.
✓ Branch 3 taken 7 times.
|
84 | excludes := stringDelimitList(list(" \"" + d + "\"," for d in subdirs), "\n"); |
| 2709 | 7 | System.writeFile(manifest, | |
| 2710 | "# Generated by OpenModelica: the workspace a source-code FMU builds its Rust\n" | ||
| 2711 | + "# simulation runtime from.\n" | ||
| 2712 | + "[package]\n" | ||
| 2713 | + "name = \"openmodelica_fmu_runtime\"\n" | ||
| 2714 | + "version = \"0.1.0\"\n" | ||
| 2715 | + "edition = \"2024\"\n" | ||
| 2716 | + "\n" | ||
| 2717 | + "[dependencies]\n" | ||
| 2718 | + "# default-features = false drops `standalone`, which an FMU has no use for:\n" | ||
| 2719 | + "# the executable entry points, the result file, --variableFilter and -iif.\n" | ||
| 2720 | + "openmodelica_simulation_runtime = { path = \"openmodelica_simulation_runtime\", default-features = false, features = [\"fmu-lapack\", \"fmu-runtime\"] }\n" | ||
| 2721 | + "\n" | ||
| 2722 | + "# Reached as plain path dependencies; a member would have to belong to this\n" | ||
| 2723 | + "# workspace, and each of them names another root.\n" | ||
| 2724 | + "[workspace]\n" | ||
| 2725 | + "exclude = [\n" + excludes + "\n]\n"); | ||
| 2726 | end writeFmuRustWorkspace; | ||
| 2727 | |||
| 2728 | protected function objectFilesOf | ||
| 2729 | "The object files a list of runtime sources compiles to, for the makefile of a | ||
| 2730 | source FMU. | ||
| 2731 | |||
| 2732 | Only .c becomes an object, that being what the generated makefile has a rule for. | ||
| 2733 | The lists also carry files that are shipped but never compiled - headers and the | ||
| 2734 | .inc the FMI 1.0 and 2.0 interfaces include - and those are simply left out. | ||
| 2735 | |||
| 2736 | A C++ source is left out too, but reported, because it is one that would have to | ||
| 2737 | be built and cannot be: the makefile has no rule for it, and the .c to .o | ||
| 2738 | replacement this used to do turned jacobian_colpack.cpp into | ||
| 2739 | jacobian_colpack.opp, an object nothing can build. That went unnoticed because it | ||
| 2740 | only breaks when somebody builds the FMU from its sources with the makefile." | ||
| 2741 | input list<String> sourceFiles; | ||
| 2742 | output list<String> objectFiles = {}; | ||
| 2743 | protected | ||
| 2744 | String ext; | ||
| 2745 | algorithm | ||
| 2746 | ✗ | for f in sourceFiles loop | |
| 2747 | ext := ""; | ||
| 2748 | ✗ | for part in System.strtok(f, ".") loop | |
| 2749 | ext := part; // the last one is the extension | ||
| 2750 | end for; | ||
| 2751 | |||
| 2752 | ✗ | if ext == "c" then | |
| 2753 | // drop the extension character and put the object one in its place | ||
| 2754 | ✗ | objectFiles := substring(f, 1, stringLength(f) - 1) + "o" :: objectFiles; | |
| 2755 | elseif ext == "cpp" or ext == "cc" or ext == "cxx" or ext == "c++" then | ||
| 2756 | ✗ | Error.addCompilerWarning("Leaving " + f + " out of the object files of the FMU makefile: " | |
| 2757 | + "the makefile has no rule for '." + ext + "'."); | ||
| 2758 | end if; | ||
| 2759 | end for; | ||
| 2760 | ✗ | objectFiles := listReverse(objectFiles); | |
| 2761 | end objectFilesOf; | ||
| 2762 | |||
| 2763 | annotation(__OpenModelica_Interface="backend_main"); | ||
| 2764 | end SimCodeMain; | ||
| 2765 |