Skip to content

Commit

Permalink
Add --set-stop-time option to fmusim (#614)
Browse files Browse the repository at this point in the history
fixes #612
  • Loading branch information
t-sommer authored Oct 15, 2024
1 parent 81fb6dc commit f7f44fa
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 46 deletions.
21 changes: 12 additions & 9 deletions fmusim/FMI1CSSimulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ FMIStatus FMI1CSSimulate(const FMISimulationSettings* s) {

FMIStatus status = FMIOK;

fmi1Real time = s->startTime;

FMIInstance* S = s->S;

char fmuLocation[FMI_PATH_MAX] = "";
Expand All @@ -29,20 +31,20 @@ FMIStatus FMI1CSSimulate(const FMISimulationSettings* s) {

// set start values
CALL(FMIApplyStartValues(S, s));
CALL(FMIApplyInput(S, s->input, s->startTime, true, true, true));
CALL(FMIApplyInput(S, s->input, time, true, true, true));

// initialize
CALL(FMI1InitializeSlave(S, s->startTime, fmi1False, 0));
CALL(FMI1InitializeSlave(S, time, s->setStopTime, s->stopTime));

CALL(FMISample(S, s->startTime, s->initialRecorder));
CALL(FMISample(S, time, s->initialRecorder));

for (unsigned long step = 0;; step++) {

const fmi1Real time = s->startTime + step * s->outputInterval;

CALL(FMISample(S, time, s->recorder));

if (time > s->stopTime || FMIIsClose(time, s->stopTime)) {
const fmi1Real nextCommunicationPoint = s->startTime + (step + 1) * s->outputInterval;

if (nextCommunicationPoint > s->stopTime && !FMIIsClose(nextCommunicationPoint, s->stopTime)) {
break;
}

Expand All @@ -58,17 +60,18 @@ FMIStatus FMI1CSSimulate(const FMISimulationSettings* s) {

if (terminated) {

fmi1Real lastSuccessfulTime;

CALL(FMI1GetRealStatus(S, fmi1LastSuccessfulTime, &lastSuccessfulTime));
CALL(FMI1GetRealStatus(S, fmi1LastSuccessfulTime, &time));

CALL(FMISample(S, time, s->recorder));

break;
}

} else {

CALL(doStepStatus);

time = nextCommunicationPoint;
}

if (s->stepFinished && !s->stepFinished(s, time)) {
Expand Down
4 changes: 4 additions & 0 deletions fmusim/FMI1MESimulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ FMIStatus FMI1MESimulate(const FMISimulationSettings* s) {
nextCommunicationPoint = eventInfo.nextEventTime;
}

if (nextCommunicationPoint > s->stopTime && !FMIIsClose(nextCommunicationPoint, s->stopTime)) {
nextCommunicationPoint = s->stopTime;
}

inputEvent = FMIIsClose(nextCommunicationPoint, nextInputEventTime);

timeEvent = eventInfo.upcomingTimeEvent && FMIIsClose(nextCommunicationPoint, eventInfo.nextEventTime);
Expand Down
65 changes: 47 additions & 18 deletions fmusim/FMI2CSSimulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ FMIStatus FMI2CSSimulate(const FMISimulationSettings* s) {

FMIStatus status = FMIOK;

fmi2Boolean terminateSimulation = fmi2False;
fmi2Real time = s->startTime;
fmi2Real nextRegularPoint = 0.0;
fmi2Real nextCommunicationPoint = 0.0;
fmi2Real nextInputEventTime = 0.0;
fmi2Real stepSize = 0.0;

char resourcePath[FMI_PATH_MAX] = "";
char resourceURI[FMI_PATH_MAX] = "";

Expand Down Expand Up @@ -38,46 +45,68 @@ FMIStatus FMI2CSSimulate(const FMISimulationSettings* s) {
CALL(FMIApplyStartValues(S, s));

if (!s->initialFMUStateFile) {
CALL(FMI2SetupExperiment(S, s->tolerance > 0, s->tolerance, s->startTime, fmi2False, 0));
CALL(FMI2SetupExperiment(S, s->tolerance > 0, s->tolerance, time, s->setStopTime, s->stopTime));
CALL(FMI2EnterInitializationMode(S));
CALL(FMIApplyInput(S, s->input, s->startTime, true, true, true));
CALL(FMI2ExitInitializationMode(S));
}

CALL(FMISample(S, s->startTime, s->initialRecorder));

for (unsigned long step = 0;; step++) {

const fmi2Real time = s->startTime + step * s->outputInterval;
CALL(FMISample(S, time, s->initialRecorder));
CALL(FMISample(S, time, s->recorder));

CALL(FMISample(S, time, s->recorder));
size_t nSteps = 0;

for (;;) {

if (time > s->stopTime || FMIIsClose(time, s->stopTime)) {
break;
}

CALL(FMIApplyInput(S, s->input, time, true, true, true));
nextRegularPoint = s->startTime + (nSteps + 1) * s->outputInterval;

const FMIStatus doStepStatus = FMI2DoStep(S, time, s->outputInterval, fmi2True);
nextCommunicationPoint = nextRegularPoint;

if (doStepStatus == fmi2Discard) {
nextInputEventTime = FMINextInputEvent(s->input, time);

if (nextCommunicationPoint > nextInputEventTime && !FMIIsClose(nextCommunicationPoint, nextInputEventTime)) {
nextCommunicationPoint = nextInputEventTime;
}

if (nextCommunicationPoint > s->stopTime && !FMIIsClose(nextCommunicationPoint, s->stopTime)) {
if (s->modelDescription->coSimulation->canHandleVariableCommunicationStepSize) {
nextCommunicationPoint = s->stopTime;
} else {
break;
}
}

fmi2Boolean terminated;
CALL(FMI2GetBooleanStatus(S, fmi2Terminated, &terminated));
stepSize = nextCommunicationPoint - time;

if (terminated) {
CALL(FMIApplyInput(S, s->input, time, true, true, true));

fmi2Real lastSuccessfulTime;
const FMIStatus doStepStatus = FMI2DoStep(S, time, stepSize, fmi2True);

CALL(FMI2GetRealStatus(S, fmi2LastSuccessfulTime, &lastSuccessfulTime));
if (doStepStatus == fmi2Discard) {

CALL(FMISample(S, lastSuccessfulTime, s->recorder));
CALL(FMI2GetRealStatus(S, fmi2LastSuccessfulTime, &time));

break;
}
CALL(FMI2GetBooleanStatus(S, fmi2Terminated, &terminateSimulation));

} else {

CALL(doStepStatus);

time = nextCommunicationPoint;
}

if (FMIIsClose(time, nextRegularPoint)) {
nSteps++;
}

CALL(FMISample(S, time, s->recorder));

if (terminateSimulation) {
goto TERMINATE;
}

if (s->stepFinished && !s->stepFinished(s, time)) {
Expand Down
6 changes: 5 additions & 1 deletion fmusim/FMI2MESimulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ FMIStatus FMI2MESimulate(const FMISimulationSettings* s) {

if (!s->initialFMUStateFile) {

CALL(FMI2SetupExperiment(S, s->tolerance > 0, s->tolerance, time, fmi2False, 0));
CALL(FMI2SetupExperiment(S, s->tolerance > 0, s->tolerance, time, s->setStopTime, s->stopTime));
CALL(FMI2EnterInitializationMode(S));
CALL(FMIApplyInput(S, s->input, time,
true, // discrete
Expand Down Expand Up @@ -144,6 +144,10 @@ FMIStatus FMI2MESimulate(const FMISimulationSettings* s) {
nextCommunicationPoint = eventInfo.nextEventTime;
}

if (nextCommunicationPoint > s->stopTime && !FMIIsClose(nextCommunicationPoint, s->stopTime)) {
nextCommunicationPoint = s->stopTime;
}

inputEvent = FMIIsClose(nextCommunicationPoint, nextInputEventTime);

timeEvent = eventInfo.nextEventTimeDefined && FMIIsClose(nextCommunicationPoint, eventInfo.nextEventTime);
Expand Down
10 changes: 9 additions & 1 deletion fmusim/FMI3CSSimulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ FMIStatus FMI3CSSimulate(const FMISimulationSettings* s) {

if (!s->initialFMUStateFile) {

CALL(FMI3EnterInitializationMode(S, s->tolerance > 0, s->tolerance, s->startTime, fmi3False, 0));
CALL(FMI3EnterInitializationMode(S, s->tolerance > 0, s->tolerance, s->startTime, s->setStopTime, s->stopTime));

CALL(FMIApplyInput(S, s->input, s->startTime, true, true, true));

Expand Down Expand Up @@ -145,6 +145,14 @@ FMIStatus FMI3CSSimulate(const FMISimulationSettings* s) {
nextCommunicationPoint = nextInputEventTime;
}

if (nextCommunicationPoint > s->stopTime && !FMIIsClose(nextCommunicationPoint, s->stopTime)) {
if (s->modelDescription->coSimulation->canHandleVariableCommunicationStepSize) {
nextCommunicationPoint = s->stopTime;
} else {
break;
}
}

inputEvent = FMIIsClose(nextCommunicationPoint, nextInputEventTime);

stepSize = nextCommunicationPoint - time;
Expand Down
6 changes: 5 additions & 1 deletion fmusim/FMI3MESimulation.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ FMIStatus FMI3MESimulate(const FMISimulationSettings* s) {
if (!s->initialFMUStateFile) {

// initialize
CALL(FMI3EnterInitializationMode(S, s->tolerance > 0, s->tolerance, time, fmi3False, 0));
CALL(FMI3EnterInitializationMode(S, s->tolerance > 0, s->tolerance, time, s->setStopTime, s->stopTime));

CALL(FMIApplyInput(S, s->input, time,
true, // discrete
Expand Down Expand Up @@ -152,6 +152,10 @@ FMIStatus FMI3MESimulate(const FMISimulationSettings* s) {
nextCommunicationPoint = nextEventTime;
}

if (nextCommunicationPoint > s->stopTime && !FMIIsClose(nextCommunicationPoint, s->stopTime)) {
nextCommunicationPoint = s->stopTime;
}

inputEvent = FMIIsClose(nextCommunicationPoint, nextInputEventTime);

timeEvent = nextEventTimeDefined && FMIIsClose(nextCommunicationPoint, nextEventTime);
Expand Down
5 changes: 4 additions & 1 deletion fmusim/FMIModelDescription.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,9 @@ static FMIModelDescription* readModelDescriptionFMI2(xmlNodePtr root) {
xpathObj = xmlXPathEvalExpression((xmlChar*)"/fmiModelDescription/CoSimulation", xpathCtx);
if (xpathObj->nodesetval->nodeNr == 1) {
CALL(FMICalloc((void**)&modelDescription->coSimulation, 1, sizeof(FMICoSimulationInterface)));
modelDescription->coSimulation->modelIdentifier = getStringAttribute(xpathObj->nodesetval->nodeTab[0], "modelIdentifier");
const xmlNodePtr node = xpathObj->nodesetval->nodeTab[0];
modelDescription->coSimulation->modelIdentifier = getStringAttribute(node, "modelIdentifier");
modelDescription->coSimulation->canHandleVariableCommunicationStepSize = getBooleanAttribute(node, "canHandleVariableCommunicationStepSize");
CALL(readSourceFiles(xpathCtx, "/fmiModelDescription/CoSimulation/SourceFiles/File", &modelDescription->coSimulation->nSourceFiles, &modelDescription->coSimulation->sourceFiles));
}
xmlXPathFreeObject(xpathObj);
Expand Down Expand Up @@ -764,6 +766,7 @@ static FMIModelDescription* readModelDescriptionFMI3(xmlNodePtr root) {
CALL(FMICalloc((void**)&modelDescription->coSimulation, 1, sizeof(FMICoSimulationInterface)));
const xmlNodePtr node = xpathObj->nodesetval->nodeTab[0];
modelDescription->coSimulation->modelIdentifier = getStringAttribute(node, "modelIdentifier");
modelDescription->coSimulation->canHandleVariableCommunicationStepSize = getBooleanAttribute(node, "canHandleVariableCommunicationStepSize");
modelDescription->coSimulation->hasEventMode = getBooleanAttribute(node, "hasEventMode");
}
xmlXPathFreeObject(xpathObj);
Expand Down
1 change: 1 addition & 0 deletions fmusim/FMIModelDescription.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ typedef struct {
typedef struct {

const char* modelIdentifier;
bool canHandleVariableCommunicationStepSize;
bool hasEventMode;
size_t nSourceFiles;
const char** sourceFiles;
Expand Down
1 change: 1 addition & 0 deletions fmusim/FMISimulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ typedef struct FMISimulationSettings {
const char* finalFMUStateFile;
bool visible;
bool loggingOn;
bool setStopTime;

// Co-Simulation
bool earlyReturnAllowed;
Expand Down
31 changes: 16 additions & 15 deletions fmusim/fmusim.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ void printUsage() {
" --tolerance [TOLERANCE] relative tolerance\n"
" --start-time [VALUE] start time\n"
" --stop-time [VALUE] stop time\n"
" --set-stop-time set stop time explicitly\n"
" --output-interval [VALUE] set the output interval\n"
" --start-value [name] [value] set a start value\n"
" --output-variable [name] record a specific variable\n"
Expand Down Expand Up @@ -151,6 +152,7 @@ int main(int argc, const char* argv[]) {

const char* startTimeLiteral = NULL;
const char* stopTimeLiteral = NULL;
bool setStopTime = false;

double outputInterval = 0;

Expand Down Expand Up @@ -251,6 +253,8 @@ int main(int argc, const char* argv[]) {
startTimeLiteral = argv[++i];
} else if (!strcmp(v, "--stop-time")) {
stopTimeLiteral = argv[++i];
} else if (!strcmp(v, "--set-stop-time")) {
setStopTime = true;
} else if (!strcmp(v, "--output-interval")) {
char* error;
outputInterval = strtod(argv[++i], &error);
Expand Down Expand Up @@ -442,26 +446,22 @@ int main(int argc, const char* argv[]) {
}
}

if (!startTimeLiteral) {
if (modelDescription->defaultExperiment && modelDescription->defaultExperiment->startTime) {
startTimeLiteral = modelDescription->defaultExperiment->startTime;
} else {
startTimeLiteral = "0";
}
double startTime = 0;

if (startTimeLiteral) {
startTime = strtod(startTimeLiteral, NULL);
} else if (modelDescription->defaultExperiment && modelDescription->defaultExperiment->startTime) {
startTime = strtod(modelDescription->defaultExperiment->startTime, NULL);
}

const double startTime = strtod(startTimeLiteral, NULL);
double stopTime = startTime + 1;

if (!stopTimeLiteral) {
if (modelDescription->defaultExperiment && modelDescription->defaultExperiment->stopTime) {
stopTimeLiteral = modelDescription->defaultExperiment->stopTime;
} else {
stopTimeLiteral = "1";
}
if (stopTimeLiteral) {
stopTime = strtod(stopTimeLiteral, NULL);
} else if (modelDescription->defaultExperiment && modelDescription->defaultExperiment->stopTime) {
stopTime = strtod(modelDescription->defaultExperiment->stopTime, NULL);
}

const double stopTime = strtod(stopTimeLiteral, NULL);

if (outputInterval == 0) {
if (modelDescription->defaultExperiment && modelDescription->defaultExperiment->stepSize) {
outputInterval = strtod(modelDescription->defaultExperiment->stepSize, NULL);
Expand All @@ -480,6 +480,7 @@ int main(int argc, const char* argv[]) {
settings.startTime = startTime;
settings.outputInterval = outputInterval;
settings.stopTime = stopTime;
settings.setStopTime = setStopTime;
settings.earlyReturnAllowed = earlyReturnAllowed;
settings.eventModeUsed = eventModeUsed;
settings.recordIntermediateValues = recordIntermediateValues;
Expand Down
17 changes: 17 additions & 0 deletions tests/test_fmusim.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ def test_stop_time(fmi_version, interface_type, arch, platform):
assert result['time'][-1] == pytest.approx(1.5)


@pytest.mark.parametrize('fmi_version, interface_type', product([1, 2, 3], ['cs', 'me']))
def test_set_stop_time(fmi_version, interface_type, arch, platform):

if fmi_version in {1, 2} and arch not in {'x86', 'x86_64'}:
pytest.skip(f"FMI version {fmi_version} is not supported on {arch}.")

result = call_fmusim(platform, fmi_version, interface_type, 'test_set_stop_time',
['--stop-time', '3', '--set-stop-time', '--output-interval', '0.7'])

last = result['time'][-1]

if fmi_version == 1 and interface_type == 'cs':
assert last == pytest.approx(2.8)
else:
assert last == pytest.approx(3.0)


@pytest.mark.parametrize('fmi_version, interface_type', product([1, 2, 3], ['cs', 'me']))
def test_start_value_types(fmi_version, interface_type, arch, platform):

Expand Down

0 comments on commit f7f44fa

Please sign in to comment.