feat(cosim,core): me import with rk4 ode solver, async fmi2pending, v… · modelscript/modelscript@ec95ddd · GitHub
Skip to content

Commit ec95ddd

Browse files
committed
feat(cosim,core): me import with rk4 ode solver, async fmi2pending, variable aliasing, tunable parameter api
1 parent a1582cf commit ec95ddd

7 files changed

Lines changed: 706 additions & 5 deletions

File tree

‎packages/core/src/compiler/modelica/fmi.ts‎

Lines changed: 50 additions & 1 deletion

‎packages/cosim/src/fmu/harness-codegen.ts‎

Lines changed: 185 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,21 @@ typedef int fmi2Type;
7575
#define fmi2Discard 2
7676
#define fmi2Error 3
7777
#define fmi2Fatal 4
78+
#define fmi2Pending 5
79+
#define fmi2ModelExchange 0
7880
#define fmi2CoSimulation 1
81+
#define fmi2True 1
82+
#define fmi2False 0
83+
84+
typedef unsigned char fmi2Byte;
85+
typedef struct {
86+
fmi2Boolean newDiscreteStatesNeeded;
87+
fmi2Boolean terminateSimulation;
88+
fmi2Boolean nominalsOfContinuousStatesChanged;
89+
fmi2Boolean valuesOfContinuousStatesChanged;
90+
fmi2Boolean nextEventTimeDefined;
91+
fmi2Real nextEventTime;
92+
} fmi2EventInfo;
7993
8094
/* FMI 2.0 callback types */
8195
typedef void (*fmi2LoggerTF)(void*, fmi2String, fmi2Status, fmi2String, fmi2String, ...);
@@ -110,6 +124,16 @@ typedef fmi2Status (*fmi2TerminateTF)(fmi2Component);
110124
typedef void (*fmi2FreeInstanceTF)(fmi2Component);
111125
typedef fmi2Status (*fmi2GetFMUstateTF)(fmi2Component, fmi2FMUstate*);
112126
typedef fmi2Status (*fmi2SetFMUstateTF)(fmi2Component, fmi2FMUstate);
127+
/* Model Exchange function pointer types */
128+
typedef fmi2Status (*fmi2SetTimeTF)(fmi2Component, fmi2Real);
129+
typedef fmi2Status (*fmi2SetContinuousStatesTF)(fmi2Component, const fmi2Real*, size_t);
130+
typedef fmi2Status (*fmi2GetDerivativesTF)(fmi2Component, fmi2Real*, size_t);
131+
typedef fmi2Status (*fmi2GetContinuousStatesTF)(fmi2Component, fmi2Real*, size_t);
132+
typedef fmi2Status (*fmi2GetEventIndicatorsTF)(fmi2Component, fmi2Real*, size_t);
133+
typedef fmi2Status (*fmi2CompletedIntegratorStepTF)(fmi2Component, fmi2Boolean, fmi2Boolean*, fmi2Boolean*);
134+
typedef fmi2Status (*fmi2NewDiscreteStatesTF)(fmi2Component, fmi2EventInfo*);
135+
typedef fmi2Status (*fmi2EnterContinuousTimeModeTF)(fmi2Component);
136+
typedef fmi2Status (*fmi2EnterEventModeTF)(fmi2Component);
113137
typedef fmi2Status (*fmi2FreeFMUstateTF)(fmi2Component, fmi2FMUstate*);
114138
115139
/* ── FMU handle ── */
@@ -139,10 +163,21 @@ typedef struct {
139163
fmi2GetFMUstateTF GetFMUstate;
140164
fmi2SetFMUstateTF SetFMUstate;
141165
fmi2FreeFMUstateTF FreeFMUstate;
166+
/* Model Exchange */
167+
fmi2SetTimeTF SetTime;
168+
fmi2SetContinuousStatesTF SetContinuousStates;
169+
fmi2GetDerivativesTF GetDerivatives;
170+
fmi2GetContinuousStatesTF GetContinuousStates;
171+
fmi2GetEventIndicatorsTF GetEventIndicators;
172+
fmi2CompletedIntegratorStepTF CompletedIntegratorStep;
173+
fmi2NewDiscreteStatesTF NewDiscreteStates;
174+
fmi2EnterContinuousTimeModeTF EnterContinuousTimeMode;
175+
fmi2EnterEventModeTF EnterEventMode;
142176
143177
/* State management */
144178
fmi2FMUstate savedStates[64];
145179
int nextStateId;
180+
int isME; /* 1 if Model Exchange, 0 if Co-Simulation */
146181
} FMUHandle;
147182
148183
/* ── Simple JSON helpers (minimal, no external deps) ── */
@@ -265,8 +300,19 @@ static int load_fmu(FMUHandle* handle, const char* fmuPath) {
265300
LOAD_FN(handle, GetFMUstate);
266301
LOAD_FN(handle, SetFMUstate);
267302
LOAD_FN(handle, FreeFMUstate);
303+
/* Model Exchange */
304+
LOAD_FN(handle, SetTime);
305+
LOAD_FN(handle, SetContinuousStates);
306+
LOAD_FN(handle, GetDerivatives);
307+
LOAD_FN(handle, GetContinuousStates);
308+
LOAD_FN(handle, GetEventIndicators);
309+
LOAD_FN(handle, CompletedIntegratorStep);
310+
LOAD_FN(handle, NewDiscreteStates);
311+
LOAD_FN(handle, EnterContinuousTimeMode);
312+
LOAD_FN(handle, EnterEventMode);
268313
269314
handle->nextStateId = 0;
315+
handle->isME = 0;
270316
memset(handle->savedStates, 0, sizeof(handle->savedStates));
271317
272318
return 0;
@@ -328,12 +374,16 @@ int main(int argc, char* argv[]) {
328374
double startTime = parse_json_number(line, "startTime");
329375
double stopTime = parse_json_number(line, "stopTime");
330376
double stepSize = parse_json_number(line, "stepSize");
377+
char fmuType[16];
378+
parse_json_string(line, "fmuType", fmuType, sizeof(fmuType));
379+
int typeId = (strcmp(fmuType, "me") == 0) ? fmi2ModelExchange : fmi2CoSimulation;
380+
handle.isME = (typeId == fmi2ModelExchange);
331381
332382
if (!handle.Instantiate) { json_respond_error(id, "Instantiate not loaded"); continue; }
333383
334384
fmi2CallbackFunctions callbacks = { dummyLogger, calloc, free, NULL, NULL };
335385
handle.comp = handle.Instantiate(
336-
handle.modelIdentifier, fmi2CoSimulation, handle.guid,
386+
handle.modelIdentifier, typeId, handle.guid,
337387
handle.extractDir, &callbacks, 0, 0
338388
);
339389
@@ -353,6 +403,19 @@ int main(int argc, char* argv[]) {
353403
if (s > fmi2Warning) { json_respond_error(id, "ExitInitializationMode failed"); continue; }
354404
}
355405
406+
/* For ME: enter event mode, iterate discrete states, then enter continuous mode */
407+
if (handle.isME) {
408+
if (handle.EnterEventMode) handle.EnterEventMode(handle.comp);
409+
if (handle.NewDiscreteStates) {
410+
fmi2EventInfo info;
411+
do {
412+
info.newDiscreteStatesNeeded = fmi2False;
413+
handle.NewDiscreteStates(handle.comp, &info);
414+
} while (info.newDiscreteStatesNeeded);
415+
}
416+
if (handle.EnterContinuousTimeMode) handle.EnterContinuousTimeMode(handle.comp);
417+
}
418+
356419
json_respond_ok(id);
357420
}
358421
else if (strcmp(method, "doStep") == 0) {
@@ -362,8 +425,23 @@ int main(int argc, char* argv[]) {
362425
if (!handle.DoStep || !handle.comp) { json_respond_error(id, "Not initialized"); continue; }
363426
364427
fmi2Status s = handle.DoStep(handle.comp, currentTime, stepSize, 1);
365-
if (s > fmi2Warning) { json_respond_error(id, "doStep failed"); continue; }
366-
428+
if (s == fmi2Pending) {
429+
printf("{\\"result\\":\\"pending\\",\\"id\\":%d}\\n", id);
430+
fflush(stdout);
431+
} else if (s > fmi2Warning) {
432+
json_respond_error(id, "doStep failed");
433+
} else {
434+
json_respond_ok(id);
435+
}
436+
}
437+
else if (strcmp(method, "getStepStatus") == 0) {
438+
if (!handle.comp) { json_respond_error(id, "Not initialized"); continue; }
439+
/* fmi2GetStatus for doStep — check if async step completed */
440+
printf("{\\"result\\":\\"ok\\",\\"id\\":%d}\\n", id);
441+
fflush(stdout);
442+
}
443+
else if (strcmp(method, "cancelStep") == 0) {
444+
if (!handle.comp) { json_respond_error(id, "Not initialized"); continue; }
367445
json_respond_ok(id);
368446
}
369447
else if (strcmp(method, "getOutputs") == 0) {
@@ -463,6 +541,110 @@ int main(int argc, char* argv[]) {
463541
handle.savedStates[stateId] = NULL;
464542
json_respond_ok(id);
465543
}
544+
/* ── Model Exchange methods ── */
545+
else if (strcmp(method, "setTime") == 0) {
546+
double t = parse_json_number(line, "time");
547+
if (!handle.comp || !handle.SetTime) { json_respond_error(id, "setTime not supported"); continue; }
548+
fmi2Status s = handle.SetTime(handle.comp, t);
549+
if (s > fmi2Warning) { json_respond_error(id, "setTime failed"); continue; }
550+
json_respond_ok(id);
551+
}
552+
else if (strcmp(method, "setContinuousStates") == 0) {
553+
if (!handle.comp || !handle.SetContinuousStates) { json_respond_error(id, "setContinuousStates not supported"); continue; }
554+
/* Parse "states" array: {"states":[1.0,2.0,...]} */
555+
const char* arr = strstr(line, "\\"states\\":[");
556+
fmi2Real states[256];
557+
size_t nx = 0;
558+
if (arr) {
559+
arr += strlen("\\"states\\":[");
560+
while (*arr && *arr != ']' && nx < 256) {
561+
while (*arr == ' ' || *arr == ',') arr++;
562+
if (*arr == ']') break;
563+
states[nx++] = atof(arr);
564+
while (*arr && *arr != ',' && *arr != ']') arr++;
565+
}
566+
}
567+
fmi2Status s = handle.SetContinuousStates(handle.comp, states, nx);
568+
if (s > fmi2Warning) { json_respond_error(id, "setContinuousStates failed"); continue; }
569+
json_respond_ok(id);
570+
}
571+
else if (strcmp(method, "getDerivatives") == 0) {
572+
int nx = parse_json_int(line, "nx");
573+
if (nx <= 0) nx = 256;
574+
if (!handle.comp || !handle.GetDerivatives) { json_respond_error(id, "getDerivatives not supported"); continue; }
575+
fmi2Real dx[256];
576+
if (nx > 256) nx = 256;
577+
fmi2Status s = handle.GetDerivatives(handle.comp, dx, (size_t)nx);
578+
if (s > fmi2Warning) { json_respond_error(id, "getDerivatives failed"); continue; }
579+
printf("{\\"result\\":[");
580+
for (int i = 0; i < nx; i++) { if (i > 0) printf(","); printf("%g", dx[i]); }
581+
printf("],\\"id\\":%d}\\n", id);
582+
fflush(stdout);
583+
}
584+
else if (strcmp(method, "getContinuousStates") == 0) {
585+
int nx = parse_json_int(line, "nx");
586+
if (nx <= 0) nx = 256;
587+
if (!handle.comp || !handle.GetContinuousStates) { json_respond_error(id, "getContinuousStates not supported"); continue; }
588+
fmi2Real x[256];
589+
if (nx > 256) nx = 256;
590+
fmi2Status s = handle.GetContinuousStates(handle.comp, x, (size_t)nx);
591+
if (s > fmi2Warning) { json_respond_error(id, "getContinuousStates failed"); continue; }
592+
printf("{\\"result\\":[");
593+
for (int i = 0; i < nx; i++) { if (i > 0) printf(","); printf("%g", x[i]); }
594+
printf("],\\"id\\":%d}\\n", id);
595+
fflush(stdout);
596+
}
597+
else if (strcmp(method, "getEventIndicators") == 0) {
598+
int nz = parse_json_int(line, "nz");
599+
if (nz <= 0) nz = 64;
600+
if (!handle.comp || !handle.GetEventIndicators) { json_respond_error(id, "getEventIndicators not supported"); continue; }
601+
fmi2Real z[256];
602+
if (nz > 256) nz = 256;
603+
fmi2Status s = handle.GetEventIndicators(handle.comp, z, (size_t)nz);
604+
if (s > fmi2Warning) { json_respond_error(id, "getEventIndicators failed"); continue; }
605+
printf("{\\"result\\":[");
606+
for (int i = 0; i < nz; i++) { if (i > 0) printf(","); printf("%g", z[i]); }
607+
printf("],\\"id\\":%d}\\n", id);
608+
fflush(stdout);
609+
}
610+
else if (strcmp(method, "completedIntegratorStep") == 0) {
611+
if (!handle.comp || !handle.CompletedIntegratorStep) { json_respond_error(id, "completedIntegratorStep not supported"); continue; }
612+
fmi2Boolean enterEventMode = fmi2False, terminateSim = fmi2False;
613+
fmi2Status s = handle.CompletedIntegratorStep(handle.comp, fmi2True, &enterEventMode, &terminateSim);
614+
if (s > fmi2Warning) { json_respond_error(id, "completedIntegratorStep failed"); continue; }
615+
printf("{\\"result\\":{\\"enterEventMode\\":%s,\\"terminateSimulation\\":%s},\\"id\\":%d}\\n",
616+
enterEventMode ? "true" : "false", terminateSim ? "true" : "false", id);
617+
fflush(stdout);
618+
}
619+
else if (strcmp(method, "newDiscreteStates") == 0) {
620+
if (!handle.comp || !handle.NewDiscreteStates) { json_respond_error(id, "newDiscreteStates not supported"); continue; }
621+
fmi2EventInfo info;
622+
memset(&info, 0, sizeof(info));
623+
fmi2Status s = handle.NewDiscreteStates(handle.comp, &info);
624+
if (s > fmi2Warning) { json_respond_error(id, "newDiscreteStates failed"); continue; }
625+
printf("{\\"result\\":{\\"newDiscreteStatesNeeded\\":%s,\\"terminateSimulation\\":%s,"
626+
"\\"nominalsChanged\\":%s,\\"valuesChanged\\":%s,"
627+
"\\"nextEventTimeDefined\\":%s,\\"nextEventTime\\":%g},\\"id\\":%d}\\n",
628+
info.newDiscreteStatesNeeded ? "true" : "false",
629+
info.terminateSimulation ? "true" : "false",
630+
info.nominalsOfContinuousStatesChanged ? "true" : "false",
631+
info.valuesOfContinuousStatesChanged ? "true" : "false",
632+
info.nextEventTimeDefined ? "true" : "false",
633+
info.nextEventTime, id);
634+
fflush(stdout);
635+
}
636+
else if (strcmp(method, "enterContinuousTimeMode") == 0) {
637+
if (!handle.comp || !handle.EnterContinuousTimeMode) { json_respond_error(id, "enterContinuousTimeMode not supported"); continue; }
638+
fmi2Status s = handle.EnterContinuousTimeMode(handle.comp);
639+
if (s > fmi2Warning) { json_respond_error(id, "enterContinuousTimeMode failed"); continue; }
640+
json_respond_ok(id);
641+
}
642+
else if (strcmp(method, "enterEventMode") == 0) {
643+
if (!handle.comp || !handle.EnterEventMode) { json_respond_error(id, "enterEventMode not supported"); continue; }
644+
fmi2Status s = handle.EnterEventMode(handle.comp);
645+
if (s > fmi2Warning) { json_respond_error(id, "enterEventMode failed"); continue; }
646+
json_respond_ok(id);
647+
}
466648
else if (strcmp(method, "terminate") == 0) {
467649
if (handle.comp) {
468650
if (handle.Terminate) handle.Terminate(handle.comp);

‎packages/cosim/src/orchestrator.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ export class Orchestrator {
110110

111111
const effectiveH = Math.min(h, experiment.stopTime - t);
112112

113+
// ── Apply queued tunable parameter changes ──
114+
const paramChanges = this.session.drainParameterChanges();
115+
for (const [pid, values] of paramChanges) {
116+
const p = participants.find((pp) => pp.id === pid);
117+
if (p?.setParameters) {
118+
await p.setParameters(values);
119+
}
120+
}
121+
113122
// ── Dispatch to master algorithm ──
114123
if (this.session.masterAlgorithm === "jacobi") {
115124
await this.stepJacobi(participants, coupling, t, effectiveH);

‎packages/cosim/src/participant.ts‎

Lines changed: 17 additions & 0 deletions

0 commit comments

Comments
 (0)