OMCompiler/Compiler/Template/CodegenFMUCpp.tpl
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of OpenModelica. | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), | ||
| 5 | * c/o Linköpings universitet, Department of Computer and Information Science, | ||
| 6 | * SE-58183 Linköping, Sweden. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR | ||
| 11 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. | ||
| 12 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES | ||
| 13 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL | ||
| 14 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 15 | * | ||
| 16 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) | ||
| 17 | * Public License (OSMC-PL) are obtained from OSMC, either from the above | ||
| 18 | * address, from the URLs: | ||
| 19 | * http://www.openmodelica.org or | ||
| 20 | * https://github.com/OpenModelica/ or | ||
| 21 | * http://www.ida.liu.se/projects/OpenModelica, | ||
| 22 | * and in the OpenModelica distribution. | ||
| 23 | * | ||
| 24 | * GNU AGPL version 3 is obtained from: | ||
| 25 | * https://www.gnu.org/licenses/licenses.html#GPL | ||
| 26 | * | ||
| 27 | * This program is distributed WITHOUT ANY WARRANTY; without | ||
| 28 | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 29 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH | ||
| 30 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. | ||
| 31 | * | ||
| 32 | * See the full OSMC Public License conditions for more details. | ||
| 33 | * | ||
| 34 | */ | ||
| 35 | |||
| 36 | // This file defines templates for transforming Modelica/MetaModelica code to FMU | ||
| 37 | // code. They are used in the code generator phase of the compiler to write | ||
| 38 | // target code. | ||
| 39 | // | ||
| 40 | // There are one root template intended to be called from the code generator: | ||
| 41 | // translateModel. These template do not return any | ||
| 42 | // result but instead write the result to files. All other templates return | ||
| 43 | // text and are used by the root templates (most of them indirectly). | ||
| 44 | // | ||
| 45 | // To future maintainers of this file: | ||
| 46 | // | ||
| 47 | // - A line like this | ||
| 48 | // # var = "" /*BUFD*/ | ||
| 49 | // declares a text buffer that you can later append text to. It can also be | ||
| 50 | // passed to other templates that in turn can append text to it. In the new | ||
| 51 | // version of Susan it should be written like this instead: | ||
| 52 | // let &var = buffer "" | ||
| 53 | // | ||
| 54 | // - A line like this | ||
| 55 | // ..., Text var /*BUFP*/, ... | ||
| 56 | // declares that a template takes a text buffer as input parameter. In the | ||
| 57 | // new version of Susan it should be written like this instead: | ||
| 58 | // ..., Text &var, ... | ||
| 59 | // | ||
| 60 | // - A line like this: | ||
| 61 | // ..., var /*BUFC*/, ... | ||
| 62 | // passes a text buffer to a template. In the new version of Susan it should | ||
| 63 | // be written like this instead: | ||
| 64 | // ..., &var, ... | ||
| 65 | // | ||
| 66 | // - Style guidelines: | ||
| 67 | // | ||
| 68 | // - Try (hard) to limit each row to 80 characters | ||
| 69 | // | ||
| 70 | // - Code for a template should be indented with 2 spaces | ||
| 71 | // | ||
| 72 | // - Exception to this rule is if you have only a single case, then that | ||
| 73 | // single case can be written using no indentation | ||
| 74 | // | ||
| 75 | // This single case can be seen as a clarification of the input to the | ||
| 76 | // template | ||
| 77 | // | ||
| 78 | // - Code after a case should be indented with 2 spaces if not written on the | ||
| 79 | // same line | ||
| 80 | |||
| 81 | package CodegenFMUCpp | ||
| 82 | |||
| 83 | import interface SimCodeTV; | ||
| 84 | import interface SimCodeBackendTV; | ||
| 85 | import CodegenUtil.*; | ||
| 86 | import CodegenCpp.*; //unqualified import, no need the CodegenC is optional when calling a template; or mandatory when the same named template exists in this package (name hiding) | ||
| 87 | import CodegenCppCommon.*; | ||
| 88 | import CodegenFMUModelDescription; | ||
| 89 | import CodegenCppInit; | ||
| 90 | import CodegenFMUCommon; | ||
| 91 | import CodegenFMU2; | ||
| 92 | |||
| 93 | 9 | template translateModel(SimCode simCode, String FMUVersion, String FMUType, list<String> sourceFiles) | |
| 94 | "Generates C++ code and Makefile for compiling an FMU of a Modelica model. | ||
| 95 | Calls CodegenCpp.translateModel for the actual model code." | ||
| 96 | ::= | ||
| 97 | match simCode | ||
| 98 | case SIMCODE(modelInfo=modelInfo as MODELINFO(__)) then | ||
| 99 | let guid = getUUIDStr() | ||
| 100 | let target = simulationCodeTarget() | ||
| 101 | let stateDerVectorName = "__zDot" | ||
| 102 | let &extraFuncs = buffer "" /*BUFD*/ | ||
| 103 | let &extraFuncsDecl = buffer "" /*BUFD*/ | ||
| 104 | let &complexStartExpressions = buffer "" | ||
| 105 | |||
| 106 | let numRealVars = numRealvars(modelInfo) | ||
| 107 | let numIntVars = numIntvars(modelInfo) | ||
| 108 | let numBoolVars = numBoolvars(modelInfo) | ||
| 109 | let numStringVars = numStringvars(modelInfo) | ||
| 110 | |||
| 111 | let extraAnnotations = Flags.getConfigString(FMI_EXTRA_ANNOTATIONS) | ||
| 112 | |||
| 113 | let _ = FlagsUtil.set(Flags.HARDCODED_START_VALUES, true) | ||
| 114 | let cpp = CodegenCpp.translateModel(simCode) | ||
| 115 | let()= textFile(fmuWriteOutputHeaderFile(simCode , &extraFuncs , &extraFuncsDecl, ""),'OMCpp<%fileNamePrefix%>WriteOutput.h') | ||
| 116 | let()= textFile(fmuModelHeaderFile(simCode, extraFuncs, extraFuncsDecl, "",guid, FMUVersion), 'OMCpp<%fileNamePrefix%>FMU.h') | ||
| 117 | let()= textFile(fmuModelCppFile(simCode, extraFuncs, extraFuncsDecl, "",guid, FMUVersion), 'OMCpp<%fileNamePrefix%>FMU.cpp') | ||
| 118 | let()= textFile((if isFMIVersion10(FMUVersion) then CodegenCppInit.modelInitXMLFile(simCode, numRealVars, numIntVars, numBoolVars, numStringVars, FMUVersion, FMUType, guid, true, "cpp-runtime", complexStartExpressions, stateDerVectorName) else | ||
| 119 | CodegenFMUModelDescription.fmuModelDescriptionFile(simCode, guid, FMUVersion, FMUType, sourceFiles)), 'modelDescription.xml') | ||
| 120 | let()= textFile(CodegenFMUCommon.fmudeffile(simCode, FMUVersion), '<%fileNamePrefix%>.def') | ||
| 121 | let()= textFile(fmuMakefile(target, simCode, extraFuncs, extraFuncsDecl, "", FMUVersion, "", "", "", "", extraAnnotations), '<%fileNamePrefix%>_FMU.makefile') | ||
| 122 | let()= textFile(fmuCalcHelperMainfile(simCode), 'OMCpp<%fileNamePrefix%>CalcHelperMain.cpp') | ||
| 123 | let _ = FlagsUtil.set(Flags.HARDCODED_START_VALUES, false) | ||
| 124 | |||
| 125 | "" | ||
| 126 | // Return empty result since result written to files directly | ||
| 127 | end translateModel; | ||
| 128 | |||
| 129 | 9 | template fmuCalcHelperMainfile(SimCode simCode) | |
| 130 | ::= | ||
| 131 | match simCode | ||
| 132 | case SIMCODE(modelInfo = MODELINFO(__)) then | ||
| 133 | << | ||
| 134 | /***************************************************************************** | ||
| 135 | * | ||
| 136 | * Helper file that includes all generated calculation files, except the alg loops. | ||
| 137 | * This file is generated by the OpenModelica Compiler and produced to speed-up the compile time. | ||
| 138 | * | ||
| 139 | *****************************************************************************/ | ||
| 140 | #include <Core/ModelicaDefine.h> | ||
| 141 | #include <Core/Modelica.h> | ||
| 142 | #include <Core/System/FactoryExport.h> | ||
| 143 | #include <Core/DataExchange/SimData.h> | ||
| 144 | #include <Core/System/SimVars.h> | ||
| 145 | #include <Core/System/DiscreteEvents.h> | ||
| 146 | #include <Core/System/EventHandling.h> | ||
| 147 | #include <Core/Utils/extension/logger.hpp> | ||
| 148 | |||
| 149 | #include "OMCpp<%fileNamePrefix%>Types.h" | ||
| 150 | #include "OMCpp<%fileNamePrefix%>Functions.h" | ||
| 151 | #include "OMCpp<%fileNamePrefix%>.h" | ||
| 152 | |||
| 153 | #include "OMCpp<%fileNamePrefix%>Jacobian.h" | ||
| 154 | #include "OMCpp<%fileNamePrefix%>Mixed.h" | ||
| 155 | #include "OMCpp<%fileNamePrefix%>StateSelection.h" | ||
| 156 | #include "OMCpp<%fileNamePrefix%>WriteOutput.h" | ||
| 157 | #include "OMCpp<%fileNamePrefix%>Initialize.h" | ||
| 158 | |||
| 159 | #include "OMCpp<%fileNamePrefix%>FMU.h" | ||
| 160 | |||
| 161 | #include "OMCpp<%fileNamePrefix%>AlgLoopMain.cpp" | ||
| 162 | #include "OMCpp<%fileNamePrefix%>Mixed.cpp" | ||
| 163 | #include "OMCpp<%fileNamePrefix%>Functions.cpp" | ||
| 164 | <%if(boolOr(Flags.isSet(Flags.HARDCODED_START_VALUES), Flags.isSet(Flags.GEN_DEBUG_SYMBOLS))) then | ||
| 165 | << | ||
| 166 | #include "OMCpp<%fileNamePrefix%>InitializeParameter.cpp" | ||
| 167 | #include "OMCpp<%fileNamePrefix%>InitializeAlgVars.cpp" | ||
| 168 | >> | ||
| 169 | %> | ||
| 170 | #include "OMCpp<%fileNamePrefix%>Initialize.cpp" | ||
| 171 | #include "OMCpp<%fileNamePrefix%>Jacobian.cpp" | ||
| 172 | #include "OMCpp<%fileNamePrefix%>StateSelection.cpp" | ||
| 173 | #include "OMCpp<%fileNamePrefix%>.cpp" | ||
| 174 | #include "OMCpp<%fileNamePrefix%>FMU.cpp" | ||
| 175 | >> | ||
| 176 | end fmuCalcHelperMainfile; | ||
| 177 | |||
| 178 | 9 | template fmuWriteOutputHeaderFile(SimCode simCode ,Text& extraFuncs,Text& extraFuncsDecl,Text extraFuncsNamespace) | |
| 179 | "Overrides code for writing simulation file. FMU does not write an output file" | ||
| 180 | ::= | ||
| 181 | match simCode | ||
| 182 | case SIMCODE(modelInfo=MODELINFO(__),simulationSettingsOpt = SOME(settings as SIMULATION_SETTINGS(__))) then | ||
| 183 | << | ||
| 184 | #pragma once | ||
| 185 | |||
| 186 | // Dummy code for FMU that writes no output file | ||
| 187 | class <%lastIdentOfPath(modelInfo.name)%>WriteOutput : public IWriteOutput,public <%lastIdentOfPath(modelInfo.name)%>StateSelection | ||
| 188 | { | ||
| 189 | public: | ||
| 190 | <%lastIdentOfPath(modelInfo.name)%>WriteOutput(IGlobalSettings* globalSettings, shared_ptr<ISimObjects> simObjects): <%lastIdentOfPath(modelInfo.name)%>StateSelection(globalSettings, simObjects) {} | ||
| 191 | virtual ~<%lastIdentOfPath(modelInfo.name)%>WriteOutput() {} | ||
| 192 | |||
| 193 | virtual void writeOutput(const IWriteOutput::OUTPUT command = IWriteOutput::UNDEF_OUTPUT) {} | ||
| 194 | virtual IHistory* getHistory() {return NULL;} | ||
| 195 | |||
| 196 | protected: | ||
| 197 | void initialize() {} | ||
| 198 | }; | ||
| 199 | >> | ||
| 200 | end fmuWriteOutputHeaderFile; | ||
| 201 | |||
| 202 | 9 | template fmuModelHeaderFile(SimCode simCode,Text& extraFuncs,Text& extraFuncsDecl,Text extraFuncsNamespace, String guid, String FMUVersion) | |
| 203 | "Generates declaration for FMU target." | ||
| 204 | ::= | ||
| 205 | match simCode | ||
| 206 | case SIMCODE(modelInfo=MODELINFO(__)) then | ||
| 207 | let modelShortName = lastIdentOfPath(modelInfo.name) | ||
| 208 | //let modelIdentifier = System.stringReplace(dotPath(modelInfo.name), ".", "_") | ||
| 209 | << | ||
| 210 | // declaration for Cpp FMU target | ||
| 211 | |||
| 212 | class <%modelShortName%>FMU: public <%modelShortName%>Initialize { | ||
| 213 | public: | ||
| 214 | // constructor | ||
| 215 | <%modelShortName%>FMU(IGlobalSettings* globalSettings, shared_ptr<ISimObjects> simObjects); | ||
| 216 | |||
| 217 | // initialization | ||
| 218 | virtual void initialize(); | ||
| 219 | |||
| 220 | // getters for given value references | ||
| 221 | virtual void getReal(const unsigned int vr[], size_t nvr, double value[]); | ||
| 222 | virtual void getInteger(const unsigned int vr[], size_t nvr, int value[]); | ||
| 223 | virtual void getBoolean(const unsigned int vr[], size_t nvr, int value[]); | ||
| 224 | virtual void getString(const unsigned int vr[], size_t nvr, string value[]); | ||
| 225 | |||
| 226 | // setters for given value references | ||
| 227 | virtual void setReal(const unsigned int vr[], size_t nvr, const double value[]); | ||
| 228 | virtual void setInteger(const unsigned int vr[], size_t nvr, const int value[]); | ||
| 229 | virtual void setBoolean(const unsigned int vr[], size_t nvr, const int value[]); | ||
| 230 | virtual void setString(const unsigned int vr[], size_t nvr, const string value[]); | ||
| 231 | |||
| 232 | // Jacobian | ||
| 233 | void getDirectionalDerivative(const unsigned int vrUnknown[], size_t nUnknown, | ||
| 234 | const unsigned int vrKnown[], size_t nKnown, | ||
| 235 | const double dvKnown[], double dvUnknown[]); | ||
| 236 | |||
| 237 | protected: | ||
| 238 | static unsigned int _inputRefs[]; ///< Value references of input variables | ||
| 239 | static unsigned int _outputRefs[]; ///< Value references of discrete states and outputs | ||
| 240 | }; | ||
| 241 | |||
| 242 | /// create instance of <%modelShortName%>FMU | ||
| 243 | static <%modelShortName%>FMU *createSystemFMU(IGlobalSettings *globalSettings); | ||
| 244 | >> | ||
| 245 | end fmuModelHeaderFile; | ||
| 246 | |||
| 247 | 9 | template fmuModelCppFile(SimCode simCode,Text& extraFuncs,Text& extraFuncsDecl,Text extraFuncsNamespace, String guid, String FMUVersion) | |
| 248 | "Generates code for FMU target." | ||
| 249 | ::= | ||
| 250 | match simCode | ||
| 251 | case SIMCODE(modelInfo=MODELINFO(vars=SIMVARS(inputVars=inputVars, algVars=algVars)), modelStructure=modelStructure) then | ||
| 252 | let modelName = CodegenUtil.dotPath(modelInfo.name) | ||
| 253 | let modelShortName = lastIdentOfPath(modelInfo.name) | ||
| 254 | let modelLongName = System.stringReplace(modelName, ".", "_") | ||
| 255 | let algloopfiles = (listAppend(listAppend(allEquations, initialEquations), getClockedEquations(getSubPartitions(clockedPartitions))) |> eqs => algloopMainfile2(eqs, simCode , &extraFuncs , &extraFuncsDecl, extraFuncsNamespace, modelShortName) ;separator="\n") | ||
| 256 | let solverFactory = match algloopfiles case "" then '' else | ||
| 257 | 'createStaticAlgLoopSolverFactory(globalSettings, PATH(""), PATH(""))' | ||
| 258 | << | ||
| 259 | // define model identifier and unique id | ||
| 260 | #define MODEL_IDENTIFIER <%modelLongName%> | ||
| 261 | #define MODEL_IDENTIFIER_SHORT <%modelShortName%> | ||
| 262 | #define MODEL_CLASS <%modelShortName%>FMU | ||
| 263 | #define MODEL_GUID "{<%guid%>}" | ||
| 264 | |||
| 265 | <%ModelDefineData(modelInfo)%> | ||
| 266 | #define NUMBER_OF_EVENT_INDICATORS <%CodegenFMUCommon.getNumberOfEventIndicators(simCode)%> | ||
| 267 | |||
| 268 | <%if isFMIVersion10(FMUVersion) then | ||
| 269 | '#include <FMU/FMUWrapper.h>' | ||
| 270 | else if isFMIVersion30(FMUVersion) then | ||
| 271 | '#include "FMU3/FMU3Wrapper.cpp"' | ||
| 272 | else | ||
| 273 | '#include "FMU2/FMU2Wrapper.cpp"'%> | ||
| 274 | <%if isFMIVersion30(FMUVersion) then | ||
| 275 | // FMI 3.0 uses its own FMU3Wrapper (included above); the FMI 3.0 C API is | ||
| 276 | // provided by FMU3Interface.cpp using the per-base-type value-reference | ||
| 277 | // offsets defined here (see SimCodeCodegenUtil.getFMI3TypeOffset). | ||
| 278 | (fmi3CppOffsetDefines(simCode) + "\n #include \"FMU3/FMU3Interface.cpp\"") | ||
| 279 | else if isFMIVersion10(FMUVersion) then | ||
| 280 | '#include <FMU/FMULibInterface.h>' | ||
| 281 | else | ||
| 282 | '#include "FMU2/FMU2Interface.cpp"'%> | ||
| 283 | |||
| 284 | // SimObjects for <%modelShortName%>FMU | ||
| 285 | shared_ptr<IAlgLoopSolverFactory> createStaticAlgLoopSolverFactory(IGlobalSettings*, PATH, PATH); | ||
| 286 | |||
| 287 | class <%modelShortName%>SimObjects : public ISimObjects { | ||
| 288 | public: | ||
| 289 | <%modelShortName%>SimObjects(IGlobalSettings *globalSettings) { | ||
| 290 | _algLoopSolverFactory = shared_ptr<IAlgLoopSolverFactory>(<%solverFactory%>); | ||
| 291 | } | ||
| 292 | <%modelShortName%>SimObjects(<%modelShortName%>SimObjects& instance) { | ||
| 293 | _algLoopSolverFactory = instance._algLoopSolverFactory; | ||
| 294 | } | ||
| 295 | weak_ptr<ISimData> LoadSimData(string modelKey) { | ||
| 296 | return shared_ptr<ISimData>(); | ||
| 297 | } | ||
| 298 | weak_ptr<ISimVars> LoadSimVars(string modelKey, size_t dim_real, size_t dim_int, size_t dim_bool, size_t dim_string, size_t dim_pre_vars, size_t dim_z, size_t z_i) { | ||
| 299 | _simVars = shared_ptr<ISimVars>(new SimVars(dim_real, dim_int, dim_bool, dim_string, dim_pre_vars, dim_z, z_i)); | ||
| 300 | return _simVars; | ||
| 301 | } | ||
| 302 | weak_ptr<IHistory> LoadWriter(size_t) { | ||
| 303 | return shared_ptr<IHistory>(); | ||
| 304 | } | ||
| 305 | shared_ptr<ISimData> getSimData(string modelKey) { | ||
| 306 | return shared_ptr<ISimData>(); | ||
| 307 | } | ||
| 308 | shared_ptr<ISimVars> getSimVars(string modelKey) { | ||
| 309 | return _simVars; | ||
| 310 | } | ||
| 311 | void eraseSimData(string modelKey) {} | ||
| 312 | void eraseSimVars(string modelKey) {} | ||
| 313 | shared_ptr<IAlgLoopSolverFactory> getAlgLoopSolverFactory() { | ||
| 314 | return _algLoopSolverFactory; | ||
| 315 | } | ||
| 316 | |||
| 317 | ISimObjects* clone() { | ||
| 318 | return new <%modelShortName%>SimObjects(*this); | ||
| 319 | } | ||
| 320 | protected: | ||
| 321 | shared_ptr<ISimVars> _simVars; | ||
| 322 | shared_ptr<IAlgLoopSolverFactory> _algLoopSolverFactory; | ||
| 323 | }; | ||
| 324 | |||
| 325 | // create instance of <%modelShortName%>FMU | ||
| 326 | <%modelShortName%>FMU *createSystemFMU(IGlobalSettings *globalSettings) { | ||
| 327 | shared_ptr<ISimObjects> simObjects(new <%modelShortName%>SimObjects(globalSettings)); | ||
| 328 | simObjects->LoadSimVars("<%modelShortName%>", <%numRealvars(modelInfo)%>, <%numIntvars(modelInfo)%>, <%numBoolvars(modelInfo)%>, <%numStringvars(modelInfo)%>, <%getPreVarsCount(modelInfo)%>, <%numStatevars(modelInfo)%>, <%numStateVarIndex(modelInfo)%>); | ||
| 329 | simObjects->LoadSimData("<%modelShortName%>"); | ||
| 330 | globalSettings->setOutputFormat(EMPTY); | ||
| 331 | return new <%modelShortName%>FMU(globalSettings, simObjects); | ||
| 332 | } | ||
| 333 | |||
| 334 | // value references of real inputs | ||
| 335 | unsigned int <%modelShortName%>FMU::_inputRefs[] = {<%inputVars |> var => | ||
| 336 | match var case SIMVAR(name=name, type_=T_REAL()) then | ||
| 337 | intSub(getVariableIndex(cref2simvar(name, simCode)), 1) ;separator=", "%>}; | ||
| 338 | // value references of real discrete states and outputs | ||
| 339 | unsigned int <%modelShortName%>FMU::_outputRefs[] = {<%algVars |> var => | ||
| 340 | match var case SIMVAR(name=name, type_=T_REAL(), varKind=CLOCKED_STATE()) | ||
| 341 | case SIMVAR(name=name, type_=T_REAL(), causality=SOME(OUTPUT())) then | ||
| 342 | intSub(getVariableIndex(cref2simvar(name, simCode)), 1) ;separator=", "%>}; | ||
| 343 | |||
| 344 | // constructor | ||
| 345 | <%modelShortName%>FMU::<%modelShortName%>FMU(IGlobalSettings* globalSettings, shared_ptr<ISimObjects> simObjects) | ||
| 346 | : <%modelShortName%>Initialize(globalSettings, simObjects) { | ||
| 347 | } | ||
| 348 | |||
| 349 | // initialization | ||
| 350 | void <%modelShortName%>FMU::initialize() { | ||
| 351 | <%modelShortName%>WriteOutput::initialize(); | ||
| 352 | <%modelShortName%>Initialize::initializeMemory(); | ||
| 353 | <%modelShortName%>Initialize::initializeFreeVariables(); | ||
| 354 | <%modelShortName%>Jacobian::initialize(); | ||
| 355 | } | ||
| 356 | |||
| 357 | // getters | ||
| 358 | <%accessFunctions(simCode, "get", modelShortName, modelInfo)%> | ||
| 359 | |||
| 360 | // setters | ||
| 361 | <%accessFunctions(simCode, "set", modelShortName, modelInfo)%> | ||
| 362 | |||
| 363 | // Jacobian | ||
| 364 | <%directionalDerivativeFunction(simCode)%> | ||
| 365 | |||
| 366 | >> | ||
| 367 | // TODO: | ||
| 368 | // <%setDefaultStartValues(modelInfo)%> | ||
| 369 | // <%setStartValues(modelInfo)%> | ||
| 370 | // <%setExternalFunction(modelInfo)%> | ||
| 371 | end fmuModelCppFile; | ||
| 372 | |||
| 373 | 1 | template fmi3CppOffsetDefines(SimCode simCode) | |
| 374 | "Generates the FMI 3.0 per-base-type value-reference offset macros used by | ||
| 375 | FMU3Interface.cpp. They must match SimCodeCodegenUtil.getFMI3TypeOffset, i.e. reals | ||
| 376 | first, then integers, booleans and strings." | ||
| 377 | ::= | ||
| 378 | match simCode | ||
| 379 | case SIMCODE(modelInfo=MODELINFO(varInfo=varInfo as VARINFO(__), nClocks=numberOfClocks)) then | ||
| 380 | // emit the per-base-type counts as C preprocessor expressions (the compiler | ||
| 381 | // evaluates them); the offsets are then composed from those, matching | ||
| 382 | // SimCodeCodegenUtil.getFMI3TypeOffset. | ||
| 383 | << | ||
| 384 | #define FMI3_NUMBER_OF_REALS (2*<%varInfo.numStateVars%> + <%varInfo.numDiscreteReal%> + <%varInfo.numAlgVars%> + <%varInfo.numParams%> + <%varInfo.numAlgAliasVars%>) | ||
| 385 | #define FMI3_NUMBER_OF_INTEGERS (<%varInfo.numIntAlgVars%> + <%varInfo.numIntParams%> + <%varInfo.numIntAliasVars%>) | ||
| 386 | #define FMI3_NUMBER_OF_BOOLEANS (<%varInfo.numBoolAlgVars%> + <%varInfo.numBoolParams%> + <%varInfo.numBoolAliasVars%>) | ||
| 387 | #define FMI3_NUMBER_OF_STRINGS (<%varInfo.numStringAlgVars%> + <%varInfo.numStringParamVars%> + <%varInfo.numStringAliasVars%>) | ||
| 388 | #define FMI3_NUMBER_OF_EXTERNALOBJECTS (<%varInfo.numExternalObjects%>) | ||
| 389 | #define FMI3_NUMBER_OF_CLOCKS (<%numberOfClocks%>) | ||
| 390 | #define FMI3_REAL_VR_OFFSET 0 | ||
| 391 | #define FMI3_INTEGER_VR_OFFSET (FMI3_NUMBER_OF_REALS) | ||
| 392 | #define FMI3_BOOLEAN_VR_OFFSET (FMI3_NUMBER_OF_REALS + FMI3_NUMBER_OF_INTEGERS) | ||
| 393 | #define FMI3_STRING_VR_OFFSET (FMI3_NUMBER_OF_REALS + FMI3_NUMBER_OF_INTEGERS + FMI3_NUMBER_OF_BOOLEANS) | ||
| 394 | // external objects are exported as FMI 3.0 Binary, then the model clocks, after the string block | ||
| 395 | #define FMI3_BINARY_VR_OFFSET (FMI3_NUMBER_OF_REALS + FMI3_NUMBER_OF_INTEGERS + FMI3_NUMBER_OF_BOOLEANS + FMI3_NUMBER_OF_STRINGS) | ||
| 396 | #define FMI3_CLOCK_VR_OFFSET (FMI3_NUMBER_OF_REALS + FMI3_NUMBER_OF_INTEGERS + FMI3_NUMBER_OF_BOOLEANS + FMI3_NUMBER_OF_STRINGS + FMI3_NUMBER_OF_EXTERNALOBJECTS) | ||
| 397 | #define FMI3_TIME_VR (FMI3_NUMBER_OF_REALS + FMI3_NUMBER_OF_INTEGERS + FMI3_NUMBER_OF_BOOLEANS + FMI3_NUMBER_OF_STRINGS + FMI3_NUMBER_OF_EXTERNALOBJECTS + FMI3_NUMBER_OF_CLOCKS) | ||
| 398 | #define FMI3_EVENT_INDICATOR_VR_START (FMI3_TIME_VR + 1) | ||
| 399 | #define FMI3_NUMBER_OF_STATES <%varInfo.numStateVars%> | ||
| 400 | >> | ||
| 401 | end fmi3CppOffsetDefines; | ||
| 402 | |||
| 403 | 9 | template ModelDefineData(ModelInfo modelInfo) | |
| 404 | "Generates global data in simulation file." | ||
| 405 | ::= | ||
| 406 | match modelInfo | ||
| 407 | case MODELINFO(varInfo=VARINFO(__), vars=SIMVARS(stateVars = listStates)) then | ||
| 408 | << | ||
| 409 | /* TODO: implement external functions in FMU wrapper for c++ target | ||
| 410 | <%System.tmpTickReset(0)%> | ||
| 411 | <%(functions |> fn => defineExternalFunction(fn) ; separator="\n")%> | ||
| 412 | */ | ||
| 413 | >> | ||
| 414 | end ModelDefineData; | ||
| 415 | |||
| 416 | ✗ | template DefineVariables(SimVar simVar, Boolean useFlatArrayNotation) | |
| 417 | "Generates code for defining variables in c file for FMU target. " | ||
| 418 | ::= | ||
| 419 | match simVar | ||
| 420 | case SIMVAR(__) then | ||
| 421 | let description = if comment then '// "<%comment%>"' | ||
| 422 | if stringEq(CodegenUtil.crefStr(name),"$dummy") then | ||
| 423 | <<>> | ||
| 424 | else if stringEq(CodegenUtil.crefStr(name),"der($dummy)") then | ||
| 425 | <<>> | ||
| 426 | else | ||
| 427 | << | ||
| 428 | #define <%cref(name,useFlatArrayNotation)%>_ <%System.tmpTick()%> <%description%> | ||
| 429 | >> | ||
| 430 | end DefineVariables; | ||
| 431 | |||
| 432 | ✗ | template defineExternalFunction(SimCodeFunction.Function fn) | |
| 433 | "Generates external function definitions." | ||
| 434 | ::= | ||
| 435 | match fn | ||
| 436 | case EXTERNAL_FUNCTION(dynamicLoad=true) then | ||
| 437 | let fname = CodegenUtil.extFunctionName(extName, language) | ||
| 438 | << | ||
| 439 | #define $P<%fname%> <%System.tmpTick()%> | ||
| 440 | >> | ||
| 441 | end defineExternalFunction; | ||
| 442 | |||
| 443 | |||
| 444 | ✗ | template setDefaultStartValues(ModelInfo modelInfo) | |
| 445 | "Generates code in c file for function setStartValues() which will set start values for all variables." | ||
| 446 | ::= | ||
| 447 | match modelInfo | ||
| 448 | case MODELINFO(varInfo=VARINFO(numStateVars=numStateVars, numAlgVars= numAlgVars),vars=SIMVARS(__)) then | ||
| 449 | << | ||
| 450 | // Set values for all variables that define a start value | ||
| 451 | void setDefaultStartValues(ModelInstance *comp) { | ||
| 452 | /* | ||
| 453 | <%vars.stateVars |> var => initValsDefault(var,"realVars",0) ;separator="\n"%> | ||
| 454 | <%vars.derivativeVars |> var => initValsDefault(var,"realVars",numStateVars) ;separator="\n"%> | ||
| 455 | <%vars.algVars |> var => initValsDefault(var,"realVars",intMul(2,numStateVars)) ;separator="\n"%> | ||
| 456 | <%vars.discreteAlgVars |> var => initValsDefault(var, "realVars", intAdd(intMul(2,numStateVars), numAlgVars)) ;separator="\n"%> | ||
| 457 | <%vars.intAlgVars |> var => initValsDefault(var,"integerVars",0) ;separator="\n"%> | ||
| 458 | <%vars.boolAlgVars |> var => initValsDefault(var,"booleanVars",0) ;separator="\n"%> | ||
| 459 | <%vars.stringAlgVars |> var => initValsDefault(var,"stringVars",0) ;separator="\n"%> | ||
| 460 | <%vars.paramVars |> var => initParamsDefault(var,"realParameter") ;separator="\n"%> | ||
| 461 | <%vars.intParamVars |> var => initParamsDefault(var,"integerParameter") ;separator="\n"%> | ||
| 462 | <%vars.boolParamVars |> var => initParamsDefault(var,"booleanParameter") ;separator="\n"%> | ||
| 463 | <%vars.stringParamVars |> var => initParamsDefault(var,"stringParameter") ;separator="\n"%> | ||
| 464 | */ | ||
| 465 | } | ||
| 466 | >> | ||
| 467 | end setDefaultStartValues; | ||
| 468 | |||
| 469 | ✗ | template setStartValues(ModelInfo modelInfo) | |
| 470 | "Generates code in c file for function setStartValues() which will set start values for all variables." | ||
| 471 | ::= | ||
| 472 | match modelInfo | ||
| 473 | case MODELINFO(varInfo=VARINFO(numStateVars=numStateVars, numAlgVars= numAlgVars),vars=SIMVARS(__)) then | ||
| 474 | << | ||
| 475 | // Set values for all variables that define a start value | ||
| 476 | void setStartValues(ModelInstance *comp) { | ||
| 477 | /* | ||
| 478 | <%vars.stateVars |> var => initVals(var,"realVars",0) ;separator="\n"%> | ||
| 479 | <%vars.derivativeVars |> var => initVals(var,"realVars",numStateVars) ;separator="\n"%> | ||
| 480 | <%vars.algVars |> var => initVals(var,"realVars",intMul(2,numStateVars)) ;separator="\n"%> | ||
| 481 | <%vars.discreteAlgVars |> var => initVals(var, "realVars", intAdd(intMul(2,numStateVars), numAlgVars)) ;separator="\n"%> | ||
| 482 | <%vars.intAlgVars |> var => initVals(var,"integerVars",0) ;separator="\n"%> | ||
| 483 | <%vars.boolAlgVars |> var => initVals(var,"booleanVars",0) ;separator="\n"%> | ||
| 484 | <%vars.stringAlgVars |> var => initVals(var,"stringVars",0) ;separator="\n"%> | ||
| 485 | <%vars.paramVars |> var => initParams(var,"realParameter") ;separator="\n"%> | ||
| 486 | <%vars.intParamVars |> var => initParams(var,"integerParameter") ;separator="\n"%> | ||
| 487 | <%vars.boolParamVars |> var => initParams(var,"booleanParameter") ;separator="\n"%> | ||
| 488 | <%vars.stringParamVars |> var => initParams(var,"stringParameter") ;separator="\n"%> | ||
| 489 | */ | ||
| 490 | } | ||
| 491 | >> | ||
| 492 | end setStartValues; | ||
| 493 | |||
| 494 | ✗ | template initVals(SimVar var, String arrayName, Integer offset) ::= | |
| 495 | match var | ||
| 496 | case SIMVAR(__) then | ||
| 497 | if stringEq(CodegenUtil.crefStr(name),"$dummy") then | ||
| 498 | <<>> | ||
| 499 | else if stringEq(CodegenUtil.crefStr(name),"der($dummy)") then | ||
| 500 | <<>> | ||
| 501 | else | ||
| 502 | let str = 'comp->fmuData->modelData.<%arrayName%>Data[<%intAdd(index,offset)%>].attribute.start' | ||
| 503 | << | ||
| 504 | <%str%> = comp->fmuData->localData[0]-><%arrayName%>[<%intAdd(index,offset)%>]; | ||
| 505 | >> | ||
| 506 | end initVals; | ||
| 507 | |||
| 508 | ✗ | template initParams(SimVar var, String arrayName) ::= | |
| 509 | match var | ||
| 510 | case SIMVAR(__) then | ||
| 511 | let str = 'comp->fmuData->modelData.<%arrayName%>Data[<%index%>].attribute.start' | ||
| 512 | '<%str%> = comp->fmuData->simulationInfo.<%arrayName%>[<%index%>];' | ||
| 513 | end initParams; | ||
| 514 | |||
| 515 | |||
| 516 | ✗ | template initValsDefault(SimVar var, String arrayName, Integer offset) ::= | |
| 517 | match var | ||
| 518 | case SIMVAR(index=index, type_=type_) then | ||
| 519 | let str = 'comp->fmuData->modelData.<%arrayName%>Data[<%intAdd(index,offset)%>].attribute.start' | ||
| 520 | match initialValue | ||
| 521 | case SOME(v) then | ||
| 522 | '<%str%> = <%initVal(v)%>;' | ||
| 523 | case NONE() then | ||
| 524 | match type_ | ||
| 525 | case T_INTEGER(__) | ||
| 526 | case T_REAL(__) | ||
| 527 | case T_ENUMERATION(__) | ||
| 528 | case T_BOOL(__) then '<%str%> = 0;' | ||
| 529 | case T_STRING(__) then '<%str%> = "";' | ||
| 530 | else 'UNKOWN_TYPE' | ||
| 531 | end initValsDefault; | ||
| 532 | |||
| 533 | ✗ | template initParamsDefault(SimVar var, String arrayName) ::= | |
| 534 | match var | ||
| 535 | case SIMVAR(__) then | ||
| 536 | let str = 'comp->fmuData->modelData.<%arrayName%>Data[<%index%>].attribute.start' | ||
| 537 | match initialValue | ||
| 538 | case SOME(v) then | ||
| 539 | '<%str%> = <%initVal(v)%>;' | ||
| 540 | end initParamsDefault; | ||
| 541 | |||
| 542 | ✗ | template initVal(Exp initialValue) | |
| 543 | ::= | ||
| 544 | match initialValue | ||
| 545 | case ICONST(__) then integer | ||
| 546 | case RCONST(__) then real | ||
| 547 | case SCONST(__) then '"<%Util.escapeModelicaStringToXmlString(string)%>"' | ||
| 548 | case BCONST(__) then if bool then "1" else "0" | ||
| 549 | case ENUM_LITERAL(__) then '<%index%>/*ENUM:<%CodegenUtil.dotPath(name)%>*/' | ||
| 550 | else "*ERROR* initial value of unknown type" | ||
| 551 | end initVal; | ||
| 552 | |||
| 553 | |||
| 554 | ✗ | template setExternalFunction(ModelInfo modelInfo) | |
| 555 | "Generates setString function for c file." | ||
| 556 | ::= | ||
| 557 | match modelInfo | ||
| 558 | case MODELINFO(vars=SIMVARS(__)) then | ||
| 559 | let externalFuncs = setExternalFunctionsSwitch(functions) | ||
| 560 | << | ||
| 561 | fmiStatus setExternalFunction(ModelInstance* c, const fmiValueReference vr, const void* value){ | ||
| 562 | switch (vr) { | ||
| 563 | /* | ||
| 564 | <%externalFuncs%> | ||
| 565 | */ | ||
| 566 | default: | ||
| 567 | return fmiError; | ||
| 568 | } | ||
| 569 | return fmiOK; | ||
| 570 | } | ||
| 571 | |||
| 572 | >> | ||
| 573 | end setExternalFunction; | ||
| 574 | |||
| 575 | ✗ | template setExternalFunctionsSwitch(list<SimCodeFunction.Function> functions) | |
| 576 | "Generates external function definitions." | ||
| 577 | ::= | ||
| 578 | (functions |> fn => setExternalFunctionSwitch(fn) ; separator="\n") | ||
| 579 | end setExternalFunctionsSwitch; | ||
| 580 | |||
| 581 | ✗ | template setExternalFunctionSwitch(SimCodeFunction.Function fn) | |
| 582 | "Generates external function definitions." | ||
| 583 | ::= | ||
| 584 | match fn | ||
| 585 | case EXTERNAL_FUNCTION(dynamicLoad=true) then | ||
| 586 | let fname = CodegenUtil.extFunctionName(extName, language) | ||
| 587 | << | ||
| 588 | case $P<%fname%> : ptr_<%fname%>=(ptrT_<%fname%>)value; break; | ||
| 589 | >> | ||
| 590 | end setExternalFunctionSwitch; | ||
| 591 | |||
| 592 | 18 | template accessFunctions(SimCode simCode, String direction, String modelShortName, ModelInfo modelInfo) | |
| 593 | "Generates getters or setters for Real, Integer, Boolean, and String." | ||
| 594 | ::= | ||
| 595 | match modelInfo | ||
| 596 | case MODELINFO(vars=SIMVARS(__), varInfo=VARINFO(numStateVars=numStateVars, numAlgVars=numAlgVars, numDiscreteReal=numDiscreteReal, numParams=numParams)) then | ||
| 597 | << | ||
| 598 | <%accessVarsFunction(simCode, direction, modelShortName, "Real", "Real", "double", intAdd(intAdd(intAdd(intMul(2, numStateVars), numAlgVars), numDiscreteReal), numParams), vars.aliasVars)%> | ||
| 599 | <%accessVarsFunction(simCode, direction, modelShortName, "Integer", "Int", "int", intAdd(listLength(vars.intAlgVars), listLength(vars.intParamVars)), vars.intAliasVars)%> | ||
| 600 | <%accessVarsFunction(simCode, direction, modelShortName, "Boolean", "Bool", "int", intAdd(listLength(vars.boolAlgVars), listLength(vars.boolParamVars)), vars.boolAliasVars)%> | ||
| 601 | <%accessVarsFunction(simCode, direction, modelShortName, "String", "String", "string", intAdd(listLength(vars.stringAlgVars), listLength(vars.stringParamVars)), vars.stringAliasVars)%> | ||
| 602 | >> | ||
| 603 | end accessFunctions; | ||
| 604 | |||
| 605 | 72 | template accessVarsFunction(SimCode simCode, String direction, String modelShortName, String typeName, String pointerName, String typeImpl, Integer offset, list<SimVar> aliasVars) | |
| 606 | "Generates get<%typeName%> or set<%typeName%> function." | ||
| 607 | ::= | ||
| 608 | let qualifier = if stringEq(direction, "set") then "const" | ||
| 609 | << | ||
| 610 | void <%modelShortName%>FMU::<%direction%><%typeName%>(const unsigned int vr[], size_t nvr, <%qualifier%> <%typeImpl%> value[]) { | ||
| 611 | for (size_t i = 0; i < nvr; i++, vr++, value++) { | ||
| 612 | // access variables and aliases in SimVars memory | ||
| 613 | if (*vr < _dim<%typeName%>) | ||
| 614 | <%if stringEq(direction, "get") then | ||
| 615 | << | ||
| 616 | *value = _pointerTo<%pointerName%>Vars[*vr]; | ||
| 617 | >> | ||
| 618 | else | ||
| 619 | << | ||
| 620 | _pointerTo<%pointerName%>Vars[*vr] = *value; | ||
| 621 | >>%> | ||
| 622 | // convert negated aliases | ||
| 623 | else switch (*vr) { | ||
| 624 | <%aliasVars |> var => match var | ||
| 625 | case SIMVAR(aliasvar=NEGATEDALIAS()) then | ||
| 626 | accessVar(simCode, direction, var, offset) | ||
| 627 | else '' | ||
| 628 | end match; separator="\n"%> | ||
| 629 | default: | ||
| 630 | throw std::invalid_argument("<%direction%><%typeName%> with wrong value reference " + omcpp::to_string(*vr)); | ||
| 631 | } | ||
| 632 | } | ||
| 633 | } | ||
| 634 | >> | ||
| 635 | end accessVarsFunction; | ||
| 636 | |||
| 637 | 14 | template accessVar(SimCode simCode, String direction, SimVar simVar, Integer offset) | |
| 638 | "Generates a case statement accessing one variable." | ||
| 639 | ::= | ||
| 640 | match simVar | ||
| 641 | case SIMVAR(__) then | ||
| 642 | let descName = System.stringReplace(crefStrNoUnderscore(name), "$", "_D_") | ||
| 643 | let description = if comment then '/* <%descName%> "<%comment%>" */' else '/* <%descName%> */' | ||
| 644 | let cppName = getCppName(simCode, simVar) | ||
| 645 | let cppSign = getCppSign(simCode, simVar) | ||
| 646 | if stringEq(direction, "get") then | ||
| 647 | << | ||
| 648 | case <%intAdd(offset, index)%>: <%description%> | ||
| 649 | *value = <%cppSign%><%cppName%>; break; | ||
| 650 | >> | ||
| 651 | else | ||
| 652 | << | ||
| 653 | case <%intAdd(offset, index)%>: <%description%> | ||
| 654 | <%cppName%> = <%cppSign%>*value; break; | ||
| 655 | >> | ||
| 656 | end accessVar; | ||
| 657 | |||
| 658 | 14 | template getCppName(SimCode simCode, SimVar simVar) | |
| 659 | "Get name of variable in Cpp runtime, resolving aliases" | ||
| 660 | ::= | ||
| 661 | match simVar | ||
| 662 | case SIMVAR(__) then | ||
| 663 | let actualName = cref1(name, simCode, "", "", "", contextOther, "", "", false) | ||
| 664 | match aliasvar | ||
| 665 | case ALIAS(__) | ||
| 666 | case NEGATEDALIAS(__) then | ||
| 667 | '<%cref1(varName, simCode, "", "", "", contextOther, "", "", false)%>' | ||
| 668 | else | ||
| 669 | '<%actualName%>' | ||
| 670 | end getCppName; | ||
| 671 | |||
| 672 | 14 | template getCppSign(SimCode simCode, SimVar simVar) | |
| 673 | "Get sign of variable in Cpp runtime, resolving aliases" | ||
| 674 | ::= | ||
| 675 | match simVar | ||
| 676 | case SIMVAR(type_=type_) then | ||
| 677 | match aliasvar | ||
| 678 | case NEGATEDALIAS(__) then | ||
| 679 | match type_ case T_BOOL(__) then '!' else '-' | ||
| 680 | else '' | ||
| 681 | end getCppSign; | ||
| 682 | |||
| 683 | 9 | template directionalDerivativeFunction(SimCode simCode) | |
| 684 | "Generates getDirectionalDerivative." | ||
| 685 | ::= | ||
| 686 | match simCode | ||
| 687 | case SIMCODE(modelInfo=MODELINFO(), modelStructure=fmiModelStructure) then | ||
| 688 | let modelShortName = lastIdentOfPath(modelInfo.name) | ||
| 689 | let dimDiscreteStates = match fmiModelStructure | ||
| 690 | case SOME(modelStructure as FMIMODELSTRUCTURE(__)) then | ||
| 691 | match modelStructure.fmiDiscreteStates | ||
| 692 | case FMIDISCRETESTATES(fmiUnknownsList=fmiUnknownsList) then | ||
| 693 | listLength(fmiUnknownsList) | ||
| 694 | else "0" | ||
| 695 | else "0" | ||
| 696 | << | ||
| 697 | void <%modelShortName%>FMU::getDirectionalDerivative( | ||
| 698 | const unsigned int vrUnknown[], size_t nUnknown, | ||
| 699 | const unsigned int vrKnown[], size_t nKnown, | ||
| 700 | const double dvKnown[], double dvUnknown[]) | ||
| 701 | { | ||
| 702 | <% if providesDirectionalDerivative(simCode) then | ||
| 703 | << | ||
| 704 | unsigned int idx, *ref_p, ref_1; | ||
| 705 | int dimStates = _dimContinuousStates + <%dimDiscreteStates%>; | ||
| 706 | |||
| 707 | _FMIDERjac_x.clear(); | ||
| 708 | ref_p = NULL; | ||
| 709 | for (size_t j = 0; j < nKnown; j++) { | ||
| 710 | idx = vrKnown[j]; | ||
| 711 | if (idx >= dimStates) { | ||
| 712 | // find input reference | ||
| 713 | if (ref_p == NULL || idx < ref_1) | ||
| 714 | ref_p = _inputRefs; // reset ref_p if vrKnown decreases | ||
| 715 | ref_p = std::find(ref_p, _inputRefs + sizeof(_inputRefs)/sizeof(unsigned int), vrKnown[j]); | ||
| 716 | ref_1 = idx; | ||
| 717 | idx = dimStates + (ref_p - _inputRefs); | ||
| 718 | } | ||
| 719 | if (idx >= _FMIDERjac_x.size()) | ||
| 720 | throw std::invalid_argument("getDirectionalDerivative with wrong value reference of known " + omcpp::to_string(vrKnown[j])); | ||
| 721 | _FMIDERjac_x(idx) = dvKnown[j]; | ||
| 722 | } | ||
| 723 | calcFMIDERJacobianColumn(); | ||
| 724 | ref_p = NULL; | ||
| 725 | for (size_t i = 0; i < nUnknown; i++) { | ||
| 726 | idx = vrUnknown[i] - _dimContinuousStates; // derivatives behind states | ||
| 727 | if (idx >= _dimContinuousStates) { | ||
| 728 | // find output reference | ||
| 729 | if (ref_p == NULL || idx < ref_1) | ||
| 730 | ref_p = _outputRefs; // reset ref_p if vrUnknown decreases | ||
| 731 | ref_p = std::find(ref_p, _outputRefs + sizeof(_outputRefs)/sizeof(unsigned int), vrUnknown[i]); | ||
| 732 | ref_1 = idx; | ||
| 733 | idx = _dimContinuousStates + (ref_p - _outputRefs); | ||
| 734 | } | ||
| 735 | if (idx >= _FMIDERjac_y.size()) | ||
| 736 | throw std::invalid_argument("getDirectionalDerivative with wrong value reference of unknown " + omcpp::to_string(vrUnknown[i])); | ||
| 737 | dvUnknown[i] = _FMIDERjac_y(idx); | ||
| 738 | } | ||
| 739 | >> | ||
| 740 | else | ||
| 741 | << | ||
| 742 | throw ModelicaSimulationError(MATH_FUNCTION, "No derivative code, see flag disableDirectionalDerivatives"); | ||
| 743 | >> | ||
| 744 | %> | ||
| 745 | } | ||
| 746 | >> | ||
| 747 | end directionalDerivativeFunction; | ||
| 748 | |||
| 749 | 9 | template fmuMakefile(String target, SimCode simCode, Text& extraFuncs, Text& extraFuncsDecl, Text extraFuncsNamespace, String FMUVersion, String additionalLinkerFlags_GCC, | |
| 750 | String additionalLinkerFlags_MSVC, String additionalCFlags_GCC, String additionalCFlags_MSVC, String extraAnnotations) | ||
| 751 | "Generates the contents of the makefile for the simulation case. Copy libexpat & correct linux fmu" | ||
| 752 | ::= | ||
| 753 | match getGeneralTarget(target) | ||
| 754 | case "msvc" then | ||
| 755 | match simCode | ||
| 756 | case SIMCODE(modelInfo=MODELINFO(__), makefileParams=MAKEFILE_PARAMS(__), simulationSettingsOpt = sopt) then | ||
| 757 | let dirExtra = if modelInfo.directory then '-L"<%modelInfo.directory%>"' //else "" | ||
| 758 | let libsExtra = (makefileParams.libs |> lib => lib ;separator=" ") | ||
| 759 | let ParModelicaLibs = if acceptParModelicaGrammar() then '-lOMOCLRuntime -lOpenCL' // else "" | ||
| 760 | let extraCflags = match sopt case SOME(s as SIMULATION_SETTINGS(__)) then | ||
| 761 | match s.method case "dassljac" then "-D_OMC_JACOBIAN " | ||
| 762 | << | ||
| 763 | # Makefile generated by OpenModelica | ||
| 764 | # run with nmake from Visual Studio Command Prompt | ||
| 765 | # FMU packaging requires PATH to <%makefileParams.omhome%>/mingw/bin | ||
| 766 | OMHOME=<%if boolOr(stringEq(makefileParams.platform, "win32"),stringEq(makefileParams.platform, "win64")) then '$(OPENMODELICAHOME)' else makefileParams.omhome%> | ||
| 767 | <% /* Don't use $(OMHOME) with include. NMAKE fails to evaluate it. */ %> | ||
| 768 | include <%makefileParams.omhome%>/include/omc/cpp/ModelicaConfig_msvc.inc | ||
| 769 | include <%makefileParams.omhome%>/include/omc/cpp/ModelicaLibraryConfig_msvc.inc | ||
| 770 | # Simulations use /Od by default | ||
| 771 | SIM_OR_DYNLOAD_OPT_LEVEL= | ||
| 772 | MODELICAUSERCFLAGS= | ||
| 773 | CXX=cl | ||
| 774 | EXEEXT=.exe | ||
| 775 | DLLEXT=.dll | ||
| 776 | |||
| 777 | # /Od - Optimization disabled | ||
| 778 | # /EHa enable C++ EH (w/ SEH exceptions) | ||
| 779 | # /fp:except - consider floating-point exceptions when generating code | ||
| 780 | # /arch:SSE2 - enable use of instructions available with SSE2 enabled CPUs | ||
| 781 | # /I - Include Directories | ||
| 782 | # /DNOMINMAX - Define NOMINMAX (does what it says) | ||
| 783 | # /TP - Use C++ Compiler | ||
| 784 | |||
| 785 | CFLAGS=$(SYSTEM_CFLAGS) /w /I"<%makefileParams.omhome%>/include/omc/cpp/" /I"$(SUITESPARSE_INCLUDE)" /I. /TP /DNOMINMAX /DNO_INTERACTIVE_DEPENDENCY /DFMU_BUILD /DRUNTIME_STATIC_LINKING | ||
| 786 | |||
| 787 | |||
| 788 | # /MD - link with MSVCRT.LIB | ||
| 789 | # /link - [linker options and libraries] | ||
| 790 | # /LIBPATH: - Directories where libs can be found | ||
| 791 | OMCPP_SOLVER_LIBS=OMCppNewton_static.lib OMCppDgesv_static.lib OMCppDgesvSolver_static.lib -lOMCppSolver_static | ||
| 792 | MODELICA_UTILITIES_LIB=OMCppModelicaUtilities_static.lib | ||
| 793 | EXTRA_LIBS=<%dirExtra%> <%libsExtra%> | ||
| 794 | |||
| 795 | LDFLAGS=/link /DLL /NOENTRY /LIBPATH:"<%makefileParams.omhome%>/lib/<%Config.targetTriple()%>/omc/cpp" /LIBPATH:"<%makefileParams.omhome%>/bin" OMCppSystem_static.lib OMCppMath_static.lib OMCppExtensionUtilities_static.lib OMCppFMU_static.lib $(OMCPP_SOLVER_LIBS) $(EXTRA_LIBS) $(MODELICA_UTILITIES_LIB) | ||
| 796 | |||
| 797 | PLATFORM="<%makefileParams.platform%>" | ||
| 798 | |||
| 799 | MODELICA_SYSTEM_LIB=<%fileNamePrefix%> | ||
| 800 | CALCHELPERMAINFILE=OMCpp$(MODELICA_SYSTEM_LIB)CalcHelperMain.cpp | ||
| 801 | |||
| 802 | <%fmuTargetName%>.fmu: $(MODELICA_SYSTEM_LIB)$(DLLEXT) | ||
| 803 | <%\t%>rm -rf binaries | ||
| 804 | <%\t%>mkdir -p "binaries/$(PLATFORM)" | ||
| 805 | <%\t%>mv $(MODELICA_SYSTEM_LIB)$(DLLEXT) "binaries/$(PLATFORM)/" | ||
| 806 | <%\t%>rm -f $(MODELICA_SYSTEM_LIB).fmu | ||
| 807 | <%\t%>zip -r "<%fmuTargetName%>.fmu" modelDescription.xml binaries | ||
| 808 | <%\t%>rm -rf binaries | ||
| 809 | |||
| 810 | $(MODELICA_SYSTEM_LIB)$(DLLEXT): | ||
| 811 | <%\t%>$(CXX) /Fe$(MODELICA_SYSTEM_LIB)$(DLLEXT) $(CALCHELPERMAINFILE) $(CFLAGS) $(LDFLAGS) | ||
| 812 | >> | ||
| 813 | end match | ||
| 814 | case "gcc" then | ||
| 815 | match simCode | ||
| 816 | case SIMCODE(modelInfo=MODELINFO(__), makefileParams=MAKEFILE_PARAMS(__), simulationSettingsOpt = sopt) then | ||
| 817 | let dirExtra = if modelInfo.directory then '-L"<%modelInfo.directory%>"' //else "" | ||
| 818 | let libsExtra = (makefileParams.libs |> lib => lib ;separator=" ") | ||
| 819 | let extraCflags = match sopt case SOME(s as SIMULATION_SETTINGS(__)) then "" | ||
| 820 | // Note: FMI 1.0 did not distinguish modelIdentifier from fileNamePrefix | ||
| 821 | // FMI 3.0 uses the "{arch}-{os}" platform tuple for the binaries folder | ||
| 822 | // (e.g. x86_64-linux), unlike FMI 1.0/2.0 which use "{os}{bitness}" (linux64). | ||
| 823 | let platformstr = if isFMIVersion30(FMUVersion) then | ||
| 824 | (match makefileParams.platform | ||
| 825 | case "i386-pc-linux" case "linux32" case "i686-linux" then 'i686-linux' | ||
| 826 | case "x86_64-linux" case "linux64" then 'x86_64-linux' | ||
| 827 | case "win64" then 'x86_64-windows' | ||
| 828 | case "win32" then 'i686-windows' | ||
| 829 | case "darwin64" then 'x86_64-darwin' | ||
| 830 | else '<%makefileParams.platform%>') | ||
| 831 | else | ||
| 832 | (match makefileParams.platform case "i386-pc-linux" then 'linux32' case "x86_64-linux" then 'linux64' else '<%makefileParams.platform%>') | ||
| 833 | let omhome = makefileParams.omhome | ||
| 834 | let lapackbins = match platformstr case "win32" case "win64" case "i686-windows" case "x86_64-windows" then '"<%omhome%>/bin/libopenblas.dll"' else '' | ||
| 835 | let mkdir = match makefileParams.platform case "win32" case "win64" then '"mkdir.exe"' else 'mkdir' | ||
| 836 | << | ||
| 837 | # Makefile generated by OpenModelica for native and cross compilation | ||
| 838 | # How to cross compile: | ||
| 839 | # - build OpenModelica (omc) from source code (see github.com/OpenModelica) | ||
| 840 | # - install a cross compiler, e.g. apt-get install g++-mingw-w64-i686 | ||
| 841 | # - fix or work around capitalization of windows.h in MSL, see | ||
| 842 | # https://trac.modelica.org/Modelica/ticket/1962 | ||
| 843 | # - add the target triplet, e.g. i686-w64-mingw32, to | ||
| 844 | # OMCompiler/SimulationRuntime/cpp/Makefile | ||
| 845 | # - rebuild omc to add the new platform | ||
| 846 | # - invoke the omc commands | ||
| 847 | # setCommandLineOptions("--simCodeTarget=Cpp"); | ||
| 848 | # buildModelFMU(MyModel, platforms={"i686-w64-mingw32"}); | ||
| 849 | # - alternatively call this Makefile with | ||
| 850 | # make TARGET_TRIPLET=i686-w64-mingw32 -f <%fileNamePrefix%>_FMU.makefile | ||
| 851 | |||
| 852 | #TARGET_TRIPLET= | ||
| 853 | # escape spaces for make | ||
| 854 | empty := | ||
| 855 | space := $(empty) $(empty) | ||
| 856 | escape_path = $(subst $(space),\$(space),$1) | ||
| 857 | |||
| 858 | OMHOME:=<%if boolOr(stringEq(makefileParams.platform, "win32"),stringEq(makefileParams.platform, "win64")) then '$(OPENMODELICAHOME)' else makefileParams.omhome%> | ||
| 859 | OMHOME_ESCAPED:=$(call escape_path,$(OMHOME)) | ||
| 860 | include $(OMHOME_ESCAPED)/include/omc/cpp/ModelicaConfig_gcc.inc | ||
| 861 | include $(OMHOME_ESCAPED)/include/omc/cpp/ModelicaLibraryConfig_gcc.inc | ||
| 862 | |||
| 863 | # simulations use -O0 by default; can be changed to e.g. -O2 or -Ofast | ||
| 864 | SIM_OPT_LEVEL=-O0 | ||
| 865 | # the default is ON by default, override the var from commandLine to skip the zipping of fmu e.g. make -f ZIP_FMU=OFF | ||
| 866 | ZIP_FMU = ON | ||
| 867 | # native build or cross compilation | ||
| 868 | ifeq ($(TARGET_TRIPLET),) | ||
| 869 | TRIPLET=<%Config.targetTriple()%> | ||
| 870 | CC=<%makefileParams.ccompiler%> | ||
| 871 | CXX=<%makefileParams.cxxcompiler%> | ||
| 872 | ABI_CFLAG= | ||
| 873 | DLLEXT=<%makefileParams.dllext%> | ||
| 874 | PLATFORM=<%platformstr%> | ||
| 875 | else | ||
| 876 | TRIPLET=$(TARGET_TRIPLET) | ||
| 877 | CC=$(TRIPLET)-gcc | ||
| 878 | CXX=$(TRIPLET)-g++ | ||
| 879 | ABI_CFLAG=-D_GLIBCXX_USE_CXX11_ABI=0 | ||
| 880 | DLLEXT=$(if $(findstring mingw,$(TRIPLET)),.dll,.so) | ||
| 881 | WORDSIZE=$(if $(findstring x86_64,$(TRIPLET)),64,32) | ||
| 882 | PLATFORM=$(if $(findstring darwin,$(TRIPLET)),darwin,$(if $(findstring mingw,$(TRIPLET)),win,linux))$(WORDSIZE) | ||
| 883 | endif | ||
| 884 | |||
| 885 | CFLAGS_BASED_ON_INIT_FILE=<%extraCflags%> | ||
| 886 | FMU_CFLAGS=$(subst -DUSE_THREAD,,$(subst -O0,$(SIM_OPT_LEVEL),$(SYSTEM_CFLAGS))) $(ABI_CFLAG) | ||
| 887 | CFLAGS=$(CFLAGS_BASED_ON_INIT_FILE) -Winvalid-pch $(FMU_CFLAGS) -DFMU_BUILD -DRUNTIME_STATIC_LINKING -I"$(OMHOME)/include/omc/cpp" -I"$(UMFPACK_INCLUDE)" -I"$(SUNDIALS_INCLUDE)" <%makefileParams.includes ; separator=" "%> <%additionalCFlags_GCC%> | ||
| 888 | |||
| 889 | ifeq ($(USE_LOGGER),ON) | ||
| 890 | $(eval CFLAGS=$(CFLAGS) -DUSE_LOGGER) | ||
| 891 | endif | ||
| 892 | |||
| 893 | LDFLAGS=-L"$(OMHOME)/lib/$(TRIPLET)/omc/cpp" <%additionalLinkerFlags_GCC%> $(SYSTEM_LDFLAGS) | ||
| 894 | ifeq ($(findstring linux,$(PLATFORM)),linux) | ||
| 895 | LDFLAGS+=-Wl,--no-undefined | ||
| 896 | endif | ||
| 897 | |||
| 898 | CALCHELPERMAINFILE=OMCpp<%fileNamePrefix%>CalcHelperMain.cpp | ||
| 899 | |||
| 900 | # CVode can be used for Co-Simulation FMUs, Kinsol is available to handle non linear equation systems | ||
| 901 | OMCPP_SOLVER_LIBS=-lOMCppNewton_static -lOMCppDgesvSolver_static -lOMCppSolver_static | ||
| 902 | ifeq ($(USE_FMU_SUNDIALS),ON) | ||
| 903 | $(eval OMCPP_SOLVER_LIBS=$(OMCPP_SOLVER_LIBS) -lOMCppKinsol_static $(SUNDIALS_LIBRARIES)) | ||
| 904 | $(eval CFLAGS=-DENABLE_SUNDIALS_STATIC $(CFLAGS)) | ||
| 905 | endif | ||
| 906 | |||
| 907 | CPPFLAGS=$(CFLAGS) | ||
| 908 | |||
| 909 | BINARIES=<%fileNamePrefix%>$(DLLEXT) | ||
| 910 | |||
| 911 | OMCPP_LIBS=-lOMCppSystem_static -lOMCppMath_static -lOMCppFMU_static $(OMCPP_SOLVER_LIBS) -lOMCppExtensionUtilities_static | ||
| 912 | MODELICA_UTILITIES_LIB=-lOMCppModelicaUtilities_static | ||
| 913 | EXTRA_LIBS=<%dirExtra%> <%libsExtra%> | ||
| 914 | LIBS=$(EXTRA_LIBS) $(OMCPP_LIBS) $(MODELICA_UTILITIES_LIB) $(BASE_LIB) | ||
| 915 | |||
| 916 | # link with simple dgesv or full lapack | ||
| 917 | ifeq ($(USE_DGESV),ON) | ||
| 918 | $(eval LIBS=$(LIBS) -lOMCppDgesv_static) | ||
| 919 | else | ||
| 920 | $(eval LIBS=$(LIBS) -L$(LAPACK_LIBS) $(LAPACK_LIBRARIES)) | ||
| 921 | $(eval BINARIES=$(BINARIES) <%lapackbins%>) | ||
| 922 | endif | ||
| 923 | |||
| 924 | # link static libs to avoid dependencies; can't link all static under Linux | ||
| 925 | ifeq ($(findstring gcc,$(CC)),gcc) | ||
| 926 | $(eval LIBS=$(LIBS) $(if $(findstring linux,$(PLATFORM)),-static-libstdc++ -static-libgcc)) | ||
| 927 | else ifeq ($(findstring clang,$(CC)),clang) | ||
| 928 | $(eval LIBS=$(LIBS) $(if $(findstring linux,$(PLATFORM)),-static-libstdc++ -static-libgcc)) | ||
| 929 | endif | ||
| 930 | ifeq ($(findstring linux,$(PLATFORM))$(findstring darwin,$(PLATFORM)),) | ||
| 931 | $(eval LIBS=$(LIBS) -static) | ||
| 932 | endif | ||
| 933 | |||
| 934 | CPPFILES=$(CALCHELPERMAINFILE) | ||
| 935 | OFILES=$(CPPFILES:.cpp=.o) | ||
| 936 | |||
| 937 | .PHONY: <%fmuTargetName%>.fmu $(CPPFILES) clean | ||
| 938 | |||
| 939 | <%fmuTargetName%>.fmu: $(OFILES) | ||
| 940 | <%\t%>$(CXX) -shared -o <%fileNamePrefix%>$(DLLEXT) $(OFILES) $(LDFLAGS) $(LIBS) | ||
| 941 | <%\t%><%mkdir%> -p "binaries/$(PLATFORM)" | ||
| 942 | # copy (do not move): $(BINARIES) includes shared dependency DLLs such as | ||
| 943 | # libopenblas.dll taken from <omhome>/bin; moving them out would break a | ||
| 944 | # second FMU export in the same session (the C target copies here too). | ||
| 945 | <%\t%>cp $(BINARIES) "binaries/$(PLATFORM)/" | ||
| 946 | <%\t%>rm -rf sources | ||
| 947 | <%\t%><%mkdir%> -p sources | ||
| 948 | <%\t%>install -p OMCpp<%fileNamePrefix%>*.h OMCpp<%fileNamePrefix%>*.cpp <%fileNamePrefix%>_init.xml <%fileNamePrefix%>_FMU.makefile sources/ | ||
| 949 | ifeq ($(USE_FMU_SUNDIALS),ON) | ||
| 950 | <%\t%>rm -rf documentation | ||
| 951 | <%\t%><%mkdir%> -p "documentation" | ||
| 952 | <%\t%>cp $(SUNDIALS_LIBRARIES_KINSOL) "binaries/$(PLATFORM)/" | ||
| 953 | <%\t%>cp "$(OMHOME)/share/omc/runtime/cpp/licenses/sundials.license" "documentation/" | ||
| 954 | endif | ||
| 955 | <%\t%>rm -f <%fmuTargetName%>.fmu | ||
| 956 | ifeq ($(ZIP_FMU),ON) | ||
| 957 | ifeq ($(USE_FMU_SUNDIALS),ON) | ||
| 958 | <%\t%>zip -r "<%fmuTargetName%>.fmu" modelDescription.xml binaries sources documentation | ||
| 959 | <%\t%>rm -rf documentation | ||
| 960 | else | ||
| 961 | <%\t%>zip -r "<%fmuTargetName%>.fmu" modelDescription.xml binaries sources | ||
| 962 | endif | ||
| 963 | endif | ||
| 964 | ifneq ("<%extraAnnotations%>","") | ||
| 965 | <%\t%><%mkdir%> -p extra/org.openmodelica | ||
| 966 | ifneq ("$(wildcard <%fileNamePrefix%>_modelInstance.json)","") | ||
| 967 | <%\t%>jq --arg regex "<%extraAnnotations%>" -f "$(OMHOME)/share/omc/scripts/filter-annotations.jq" <%fileNamePrefix%>_modelInstance.json > extra/org.openmodelica/modelAnnotations.json | ||
| 968 | endif | ||
| 969 | ifeq ($(ZIP_FMU),ON) | ||
| 970 | <%\t%>zip -ur "<%fmuTargetName%>.fmu" extra | ||
| 971 | endif | ||
| 972 | endif | ||
| 973 | |||
| 974 | ifeq ($(ZIP_FMU),OFF) | ||
| 975 | <%\t%>rm -f OMCpp<%fileNamePrefix%>* <%fileNamePrefix%>_FMU.* <%fileNamePrefix%>.def <%fileNamePrefix%>.sh <%fileNamePrefix%>.bat <%fileNamePrefix%>.makefile <%fileNamePrefix%>_init.xml | ||
| 976 | <%\t%>rm -f <%fileNamePrefix%>_modelInstance.json | ||
| 977 | endif | ||
| 978 | |||
| 979 | clean: | ||
| 980 | <%\t%>rm -f OMCpp<%fileNamePrefix%>* <%fileNamePrefix%>_FMU.* <%fileNamePrefix%>.def <%fileNamePrefix%>.sh <%fileNamePrefix%>.bat <%fileNamePrefix%>.makefile <%fileNamePrefix%>_init.xml | ||
| 981 | <%\t%>rm -rf modelDescription.xml binaries sources documentation extra <%fileNamePrefix%>_modelInstance.json | ||
| 982 | |||
| 983 | >> | ||
| 984 | end fmuMakefile; | ||
| 985 | |||
| 986 | annotation(__OpenModelica_Interface="codegen_cpp_ext"); | ||
| 987 | end CodegenFMUCpp; | ||
| 988 | |||
| 989 | // vim: filetype=susan sw=2 sts=2 | ||
| 990 |