Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --set-stop-time option to fmusim #614

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading