Skip to content

Commit

Permalink
[FMU] Adding docuemntation and inproving flags
Browse files Browse the repository at this point in the history
  - Add documetnation for CVODE 2.0 CS FMUs
  - Updated test to check if example from doc is working
  - Add break for infinit while loop
  - Disable internal root finding of CVODE for FMI
  - Test example with event handling
  • Loading branch information
AnHeuermann authored and adrpo committed Jun 11, 2020
1 parent 1014b60 commit bdcc647
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 54 deletions.
5 changes: 2 additions & 3 deletions OMCompiler/Compiler/SimCode/SimCode.mo
Expand Up @@ -620,16 +620,15 @@ end FmiModelStructure;

public uniontype FmiSimulationFlags
record FMI_SIMULATION_FLAGS
String solver;
String nonLinearSolver;
list<tuple<String,String>> nameValueTuples;
end FMI_SIMULATION_FLAGS;

record FMI_SIMULATION_FLAGS_FILE
String path;
end FMI_SIMULATION_FLAGS_FILE;
end FmiSimulationFlags;

constant FmiSimulationFlags defaultFmiSimulationFlags = FMI_SIMULATION_FLAGS(solver="euler", nonLinearSolver="homotopy");
constant FmiSimulationFlags defaultFmiSimulationFlags = FMI_SIMULATION_FLAGS({("solver","euler")});

annotation(__OpenModelica_Interface="backend");
end SimCode;
19 changes: 5 additions & 14 deletions OMCompiler/Compiler/SimCode/SimCodeUtil.mo
Expand Up @@ -13245,8 +13245,7 @@ protected
String msg;
String tmpName, tmpValue;
list<String> tmpSplitted;
String solverName = "euler";
String nonLinearSolverName = "homotopy";
list<tuple<String,String>> nameValueTouples = {} ;
algorithm
fmiFlagsList := Flags.getConfigStringList(Flags.FMI_FLAGS);

Expand Down Expand Up @@ -13296,24 +13295,16 @@ algorithm
// Save value
if stringEqual(tmpName, "s") then
if not stringEqual(tmpValue, "euler") and not stringEqual(tmpValue, "cvode") then
msg := "Unknown value \"" + tmpValue + "\" for flag \"s\". Using \"euler\"";
msg := "Unknown value \"" + tmpValue + "\" for flag \"s\".";
Error.addCompilerWarning(msg);
else
solverName := tmpValue;
end if;
elseif stringEqual(tmpName, "nls") then
if not stringEqual(tmpValue, "homotopy") then
msg := "Unknown value \"" + tmpValue + "\" for flag \"nls\". Using \"homotopy\"";
Error.addCompilerWarning(msg);
else
nonLinearSolverName := tmpValue;
end if;
else
msg := "Ignoring unknown flag \"" + tmpName + "\".";
msg := "Adding unknown FMU simulation flag \"" + tmpName + "\" .";
Error.addCompilerWarning(msg);
end if;
nameValueTouples := listAppend(nameValueTouples, {(tmpName, tmpValue)});
end for;
fmiSimulationFlags := SOME(SimCode.FMI_SIMULATION_FLAGS(solver=solverName, nonLinearSolver=nonLinearSolverName));
fmiSimulationFlags := SOME(SimCode.FMI_SIMULATION_FLAGS(nameValueTouples));
end if;
end createFMISimulationFlags;

Expand Down
6 changes: 4 additions & 2 deletions OMCompiler/Compiler/Template/CodegenFMU.tpl
Expand Up @@ -205,10 +205,12 @@ template fmuSimulationFlagsFile(FmiSimulationFlags fmiSimulationFlags)
::=
match fmiSimulationFlags
case flags as FMI_SIMULATION_FLAGS(__) then
let fileContent = (flags.nameValueTuples |> (name, value) =>
'"<%name%>" : "<%value%>"'
;separator=",\n")
<<
{
"s" : "<%flags.solver%>",
"nls" : "<%flags.nonLinearSolver%>"
<%fileContent%>
}
>>
end fmuSimulationFlagsFile;
Expand Down
3 changes: 1 addition & 2 deletions OMCompiler/Compiler/Template/SimCodeTV.mo
Expand Up @@ -835,8 +835,7 @@ package SimCode

uniontype FmiSimulationFlags
record FMI_SIMULATION_FLAGS
String solver;
String nonLinearSolver;
list<tuple<String,String>> nameValueTuples;
end FMI_SIMULATION_FLAGS;

record FMI_SIMULATION_FLAGS_FILE
Expand Down
25 changes: 19 additions & 6 deletions OMCompiler/SimulationRuntime/c/simulation/solver/cvode_solver.c
Expand Up @@ -79,6 +79,7 @@ const char *CVODE_ITER_DESC[CVODE_ITER_MAX + 1] = {

/* Internal function prototypes */
int cvodeRightHandSideODEFunction(realtype time, N_Vector y, N_Vector ydot, void *userData);
void cvodeGetConfig(CVODE_CONFIG *config, threadData_t *threadData, booleantype isFMI);

/**
* @brief Computes the ODE right-hand side for a given value of the independent variable t and state vector y
Expand Down Expand Up @@ -340,7 +341,7 @@ int rootsFunctionCVODE(double time, N_Vector y, double *gout, void *userData)
* @param cvodeData CVODE solver data struckt
* @param threadData Thread data for error handling
*/
void cvodeGetConfig(CVODE_CONFIG *config, threadData_t *threadData)
void cvodeGetConfig(CVODE_CONFIG *config, threadData_t *threadData, booleantype isFMI)
{
/* Variables */
int i;
Expand Down Expand Up @@ -492,6 +493,15 @@ void cvodeGetConfig(CVODE_CONFIG *config, threadData_t *threadData)
{
config->BDFStabDetect = FALSE;
}

if(omc_flag[FLAG_NO_ROOTFINDING] || isFMI)
{
config->solverRootFinding = FALSE;
}
else
{
config->solverRootFinding = TRUE;
}
}

/**
Expand All @@ -503,7 +513,7 @@ void cvodeGetConfig(CVODE_CONFIG *config, threadData_t *threadData)
* @param cvodeData CVODE solver data struckt.
* @return int Return 0 on success.
*/
int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData)
int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData, int isFMI)
{
/* Variables */
int flag;
Expand All @@ -522,7 +532,7 @@ int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solv
cvodeData->isInitialized = FALSE;

/* Get CVODE settings from user flags */
cvodeGetConfig(&(cvodeData->config), threadData);
cvodeGetConfig(&(cvodeData->config), threadData, isFMI);

/* Initialize states */
cvodeData->y = N_VMake_Serial(data->modelData->nStates, (realtype *)data->localData[0]->realVars);
Expand Down Expand Up @@ -602,9 +612,12 @@ int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solv
}

/* Set root finding function */
solverInfo->solverRootFinding = 1;
flag = CVodeRootInit(cvodeData->cvode_mem, data->modelData->nZeroCrossings, rootsFunctionCVODE);
assertStreamPrint(threadData, flag >= 0, "CVODE_ERROR: CVodeRootInit failed with flag %i", flag);
if (cvodeData->config.solverRootFinding)
{
solverInfo->solverRootFinding = 1;
flag = CVodeRootInit(cvodeData->cvode_mem, data->modelData->nZeroCrossings, rootsFunctionCVODE);
assertStreamPrint(threadData, flag >= 0, "CVODE_ERROR: CVodeRootInit failed with flag %i", flag);
}
infoStreamPrint(LOG_SOLVER, 0, "CVODE uses internal root finding method %s", solverInfo->solverRootFinding ? "YES" : "NO");

/* ### Set optional settings ### */
Expand Down
Expand Up @@ -87,6 +87,8 @@ typedef struct CVODE_CONFIG
* Default value is 10. */
booleantype BDFStabDetect; /* BDF stability limit detection.
* Only usable for lmm=CV_BDF. */
booleantype solverRootFinding; /* True if internal root finding should be used, false otherwiese.
* Disable for FMI */
} CVODE_CONFIG;

typedef struct CVODE_SOLVER
Expand All @@ -104,7 +106,7 @@ typedef struct CVODE_SOLVER
} CVODE_SOLVER;

/* Function prototypes */
int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData);
int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData, int isFMI);
int cvode_solver_reinit(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData);
int cvode_solver_deinitial(CVODE_SOLVER *cvodeData);
int cvode_solver_step(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo);
Expand All @@ -114,7 +116,7 @@ int cvode_solver_fmi_step(DATA* data, threadData_t* threadData, SOLVER_INFO* sol
typedef void CVODE_SOLVER;

// TODO: Move to .c file
int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData)
int cvode_solver_initial(DATA *data, threadData_t *threadData, SOLVER_INFO *solverInfo, CVODE_SOLVER *cvodeData, int isFMI)
{
#ifdef OMC_FMI_RUNTIME
printf("##CVODE## SUNDIALS not available in FMU. See OpenModelica command line flag \"--fmiFlags\" from \"omc --help\" on how to enable CVODE in FMUs.\n");
Expand Down
Expand Up @@ -351,7 +351,7 @@ int initializeSolverData(DATA* data, threadData_t *threadData, SOLVER_INFO* solv
infoStreamPrint(LOG_SOLVER, 0, "Initializing CVODE ODE Solver");
cvodeData = (CVODE_SOLVER*) calloc(1, sizeof(CVODE_SOLVER));
assertStreamPrint(threadData, cvodeData != NULL, "Out of memory");
retValue = cvode_solver_initial(data, threadData, solverInfo, cvodeData);
retValue = cvode_solver_initial(data, threadData, solverInfo, cvodeData, 0 /* not FMI */);
solverInfo->solverData = cvodeData;
break;
}
Expand Down
Expand Up @@ -95,16 +95,20 @@ static inline const char* setSolverMethod(SOLVER_INFO *solverInfo, const char *s
* @brief parses flags from resources/modelName_flags.json
*
* @param solverInfo
* @param str string read from file
* @param str string read from file
*/
void parseFlags(SOLVER_INFO *solverInfo, const char *str)
{
/* Variables */
int i;
int i, k;
const int k_max = 1000;
char* value;

str = skipTo(str, '\"');
while(*str != '\0')
k = 0;
while(*str != '\0' && k < k_max)
{
k++;
for(i=1; i<FMU_FLAG_MAX; i++)
{
// map the fmu flags to regular flags
Expand Down Expand Up @@ -155,6 +159,7 @@ int FMI2CS_initializeSolverData(ModelInstance* comp)
solverInfo->currentTime = 0;
solverInfo->currentStepSize = 0;
solverInfo->laststep = 0;
solverInfo->solverMethod = 0;
solverInfo->solverRootFinding = 0;
solverInfo->solverNoEquidistantGrid = 0;
solverInfo->lastdesiredStep = solverInfo->currentTime + solverInfo->currentStepSize;
Expand Down Expand Up @@ -201,7 +206,7 @@ int FMI2CS_initializeSolverData(ModelInstance* comp)
if (!cvodeData) {
FILTERED_LOG(comp, fmi2Error, LOG_STATUSERROR, "fmi2Instantiate: Out of memory.")
}
retValue = cvode_solver_initial(data, threadData, solverInfo, cvodeData); /* TODO: cvode_solver_initial needs to use malloc and free from fmi2CallbackFunctions */
retValue = cvode_solver_initial(data, threadData, solverInfo, cvodeData, 1 /* is FMI */); /* TODO: cvode_solver_initial needs to use malloc and free from fmi2CallbackFunctions */
solverInfo->solverData = cvodeData;
useStream[LOG_SOLVER] = 0;
break;
Expand Down
36 changes: 35 additions & 1 deletion doc/UsersGuide/source/fmitlm.rst
Expand Up @@ -52,11 +52,42 @@ By default an FMU that can be used for both Model Exchange and
Co-Simulation is generated. We support FMI 1.0 & FMI 2.0 for Model Exchange FMUs
and FMI 2.0 for Co-Simulation FMUs.

Currently the Co-Simulation FMU supports only the forward Euler solver
Currently the Co-Simulation FMU uses the forward Euler solver as default
with root finding which does an Euler step of communicationStepSize
in fmi2DoStep. Events are checked for before and after the call to
fmi2GetDerivatives.

For FMI 2.0 for Co-Simulation OpenModelica can export an experimental
implementation of SUNDIALS CVODE (see [#f1]_) as internal integrator.

To export a Co-Simulation FMU with CVODE for the bouncing ball example use the
following commands:

.. omc-mos ::
:erroratend:
loadFile(getInstallationDirectoryPath() + "/share/doc/omc/testmodels/BouncingBall.mo")
setCommandLineOptions("--fmiFlags=s:cvode")
translateModelFMU(BouncingBall, version = "2.0", fmuType="cs")
system("unzip -cqq BouncingBall.fmu resources/BouncingBall_flags.json > BouncingBall_flags.json")
The FMU BouncingBall.fmu will have a new file BouncingBall_flags.json in its
resources directory. By manualy changing its contant users can change the
solver method without recompiling the FMU.

The BouncingBall_flags.json for this example is displayed in
:numref:`BouncingBall FMI flags`.

.. literalinclude :: ../tmp/BouncingBall_flags.json
:name: BouncingBall FMI flags
:caption: BouncingBall FMI flags
For this to work OpenModelica will export all needed dependecies into the FMU
if and only if the flag fmiFlags was set.
To have CVODE in a SourceCode FMU the user needs to add all sources for
SUNDIALS manualy and create a build script as well.

FMI Import
~~~~~~~~~~

Expand Down Expand Up @@ -379,3 +410,6 @@ which you will be able to specify the simulation parameters.
:name: tlm-change-cosimulation-parameters-dialog
Changing Co-Simulation Parameters Dialog.
.. rubric:: Footnotes
.. [#f1] `Sundials Webpage <http://computation.llnl.gov/projects/sundials-suite-nonlinear-differential-algebraic-equation-solvers>`__
Expand Up @@ -36,8 +36,7 @@ val(rev.w, 1.0, "Pendulum_dynamic_res.mat"); getErrorString();
// 0
// ""
// "{
// \"s\" : \"cvode\",
// \"nls\" : \"homotopy\"
// \"s\" : \"cvode\"
// }"
// ""
// 0
Expand All @@ -47,7 +46,7 @@ val(rev.w, 1.0, "Pendulum_dynamic_res.mat"); getErrorString();
// LOG_SOLVER | info | CVODE use equidistant time grid YES
// LOG_SOLVER | info | CVODE Using relative error tolerance 1.000000e-06
// LOG_SOLVER | info | CVODE uses internal dense numeric jacobian method
// LOG_SOLVER | info | CVODE uses internal root finding method YES
// LOG_SOLVER | info | CVODE uses internal root finding method NO
// LOG_SOLVER | info | CVODE maximum absolut step size 0
// LOG_SOLVER | info | CVODE initial step size is set automatically
// LOG_SOLVER | info | CVODE maximum integration order 5
Expand Down
Expand Up @@ -36,8 +36,7 @@ val(rev.w, 1.0, "Pendulum_static_res.mat"); getErrorString();
// 0
// ""
// "{
// \"s\" : \"cvode\",
// \"nls\" : \"homotopy\"
// \"s\" : \"cvode\"
// }"
// ""
// 0
Expand All @@ -47,7 +46,7 @@ val(rev.w, 1.0, "Pendulum_static_res.mat"); getErrorString();
// LOG_SOLVER | info | CVODE use equidistant time grid YES
// LOG_SOLVER | info | CVODE Using relative error tolerance 1.000000e-06
// LOG_SOLVER | info | CVODE uses internal dense numeric jacobian method
// LOG_SOLVER | info | CVODE uses internal root finding method YES
// LOG_SOLVER | info | CVODE uses internal root finding method NO
// LOG_SOLVER | info | CVODE maximum absolut step size 0
// LOG_SOLVER | info | CVODE initial step size is set automatically
// LOG_SOLVER | info | CVODE maximum integration order 5
Expand Down

0 comments on commit bdcc647

Please sign in to comment.