Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 62.0% 500 / 0 / 807
Functions: -% 0 / 1 / 1
Branches: 48.8% 273 / 0 / 560

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