@abi-software/simulationvuer 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/simulationvuer.js +41025 -7741
- package/dist/simulationvuer.umd.cjs +96 -13
- package/dist/style.css +1 -1
- package/package.json +2 -1
- package/src/App.vue +24 -11
- package/src/components/SimulationVuer.vue +410 -128
- package/src/components/SimulationVuerInput.vue +1 -1
- package/src/components/common.js +54 -15
- package/src/components/json.js +68 -66
- package/src/components/libopencor.js +16 -0
- package/src/components/libopencor.wasm +0 -0
- package/src/main.js +10 -13
- package/src/components/ui.js +0 -85
|
@@ -19,13 +19,16 @@
|
|
|
19
19
|
/>
|
|
20
20
|
</div>
|
|
21
21
|
<div class="primary-button">
|
|
22
|
-
<el-button type="primary" size="small" @click="startSimulation()">Run Simulation</el-button>
|
|
22
|
+
<el-button type="primary" size="small" @click="startSimulation()" v-if="!this.libopencor">Run Simulation</el-button>
|
|
23
23
|
</div>
|
|
24
24
|
<div class="secondary-button" v-if="uuid">
|
|
25
25
|
<el-button size="small" @click="runOnOsparc()">Run on oSPARC</el-button>
|
|
26
26
|
</div>
|
|
27
27
|
<div class="secondary-button">
|
|
28
|
-
<el-button size="small" @click="viewDataset()">View Dataset</el-button>
|
|
28
|
+
<el-button size="small" @click="viewDataset()" v-if="typeof this.id === 'number'">View Dataset</el-button>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="secondary-button">
|
|
31
|
+
<el-button size="small" @click="viewWorkspace()" v-if="typeof this.id !== 'number'">View Workspace</el-button>
|
|
29
32
|
</div>
|
|
30
33
|
<p class="default note" v-if="uuid">Additional parameters are available on oSPARC</p>
|
|
31
34
|
</div>
|
|
@@ -33,7 +36,7 @@
|
|
|
33
36
|
<PlotVuer v-for="(outputPlot, index) in simulationUiInfo.output.plots"
|
|
34
37
|
:key="`output-${index}`"
|
|
35
38
|
:metadata="plotMetadata(index)"
|
|
36
|
-
:data-source="{data:
|
|
39
|
+
:data-source="{data: simulationResults[index]}"
|
|
37
40
|
:plotLayout="layout[index]"
|
|
38
41
|
:plotType="'plotly-only'"
|
|
39
42
|
:selectorUi="false"
|
|
@@ -51,14 +54,24 @@ import { PlotVuer } from "@abi-software/plotvuer";
|
|
|
51
54
|
import "@abi-software/plotvuer/dist/style.css";
|
|
52
55
|
import SimulationVuerInput from "./SimulationVuerInput.vue";
|
|
53
56
|
import { ElButton, ElDivider, ElLoading } from "element-plus";
|
|
54
|
-
import { evaluateValue,
|
|
57
|
+
import { evaluateValue, finaliseUi, OPENCOR_SOLVER_NAME } from "./common.js";
|
|
55
58
|
import { validJson } from "./json.js";
|
|
56
|
-
import
|
|
59
|
+
import libOpenCOR from "./libopencor.js";
|
|
60
|
+
import { toRaw } from "vue";
|
|
61
|
+
import { create, all } from "mathjs";
|
|
62
|
+
|
|
63
|
+
const LIBOPENCOR_SOLVER = "libOpenCOR";
|
|
64
|
+
const OSPARC_SOLVER = "oSPARC";
|
|
65
|
+
const PMR_URL = "https://models.physiomeproject.org/";
|
|
66
|
+
|
|
67
|
+
const math = create(all, {});
|
|
57
68
|
|
|
58
69
|
/**
|
|
59
70
|
* SimulationVuer
|
|
60
71
|
*/
|
|
61
72
|
export default {
|
|
73
|
+
LIBOPENCOR_SOLVER: LIBOPENCOR_SOLVER,
|
|
74
|
+
OSPARC_SOLVER: OSPARC_SOLVER,
|
|
62
75
|
name: "SimulationVuer",
|
|
63
76
|
components: {
|
|
64
77
|
PlotVuer,
|
|
@@ -80,46 +93,62 @@ export default {
|
|
|
80
93
|
*/
|
|
81
94
|
id: {
|
|
82
95
|
required: true,
|
|
83
|
-
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* The preferred solver to use for OpenCOR-based simulations. This property
|
|
99
|
+
* is optional and defaults to "oSPARC". The only value that is currently
|
|
100
|
+
* supported is "libOpenCOR". Any other value will default to "oSPARC".
|
|
101
|
+
*/
|
|
102
|
+
preferredSolver: {
|
|
103
|
+
type: String,
|
|
104
|
+
default: OSPARC_SOLVER,
|
|
84
105
|
},
|
|
85
106
|
},
|
|
86
107
|
data: function() {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
// Retrieve some information about the dataset.
|
|
109
|
+
|
|
110
|
+
if (this.id > 0) {
|
|
111
|
+
const xmlhttp = new XMLHttpRequest();
|
|
112
|
+
|
|
113
|
+
xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id);
|
|
114
|
+
xmlhttp.onreadystatechange = () => {
|
|
115
|
+
if (xmlhttp.readyState === 4) {
|
|
116
|
+
if (xmlhttp.status === 200) {
|
|
117
|
+
const datasetInfo = JSON.parse(xmlhttp.responseText);
|
|
118
|
+
|
|
119
|
+
this.name = datasetInfo.name;
|
|
120
|
+
this.uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
|
|
121
|
+
}
|
|
100
122
|
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
123
|
+
};
|
|
124
|
+
xmlhttp.send();
|
|
125
|
+
}
|
|
104
126
|
|
|
105
127
|
return {
|
|
106
128
|
errorMessage: "",
|
|
129
|
+
fileManager: undefined,
|
|
107
130
|
hasFinalisedUi: false,
|
|
108
131
|
hasValidSimulationUiInfo: false,
|
|
132
|
+
instance: undefined,
|
|
109
133
|
isMounted: false,
|
|
110
134
|
isSimulationValid: true,
|
|
111
135
|
layout: [],
|
|
112
|
-
|
|
136
|
+
libopencor: undefined,
|
|
137
|
+
name: null,
|
|
138
|
+
opencorBasedSimulation: true,
|
|
139
|
+
output: undefined,
|
|
113
140
|
perfectScollbarOptions: {
|
|
114
141
|
suppressScrollX: true,
|
|
115
142
|
},
|
|
143
|
+
pmrBasedCombineArchive: false,
|
|
116
144
|
showUserMessage: false,
|
|
117
|
-
|
|
118
|
-
|
|
145
|
+
simulationResults: {},
|
|
146
|
+
simulationResultsId: {},
|
|
119
147
|
simulationUiInfo: {},
|
|
148
|
+
solver: undefined,
|
|
120
149
|
userMessage: "",
|
|
121
150
|
ui: null,
|
|
122
|
-
uuid:
|
|
151
|
+
uuid: null,
|
|
123
152
|
};
|
|
124
153
|
},
|
|
125
154
|
methods: {
|
|
@@ -138,6 +167,128 @@ export default {
|
|
|
138
167
|
},
|
|
139
168
|
};
|
|
140
169
|
},
|
|
170
|
+
/**
|
|
171
|
+
* @vuese
|
|
172
|
+
* Download the PMR file associated with the given `url`.
|
|
173
|
+
* @arg `url`
|
|
174
|
+
*/
|
|
175
|
+
downloadPmrFile(url) {
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
const xmlhttp = new XMLHttpRequest();
|
|
178
|
+
|
|
179
|
+
xmlhttp.open("POST", this.apiLocation + "/pmr_file");
|
|
180
|
+
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
181
|
+
xmlhttp.onreadystatechange = () => {
|
|
182
|
+
if (xmlhttp.readyState === 4) {
|
|
183
|
+
if (xmlhttp.status === 200) {
|
|
184
|
+
resolve(Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0)));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
reject();
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
xmlhttp.send(JSON.stringify({path: url.replace(PMR_URL, "")}));
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
/**
|
|
194
|
+
* @vuese
|
|
195
|
+
* Manage the file associated with the given `url` and `fileContents`.
|
|
196
|
+
* @arg `url`
|
|
197
|
+
* @arg `fileContents`
|
|
198
|
+
*/
|
|
199
|
+
manageFile(url, fileContents) {
|
|
200
|
+
let file = toRaw(this.fileManager).file(url);
|
|
201
|
+
|
|
202
|
+
if (file === null) {
|
|
203
|
+
file = new this.libopencor.File(url);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const fileContentsPtr = this.libopencor._malloc(fileContents.length);
|
|
207
|
+
const mem = new Uint8Array(this.libopencor.HEAPU8.buffer, fileContentsPtr, fileContents.length);
|
|
208
|
+
|
|
209
|
+
mem.set(fileContents);
|
|
210
|
+
|
|
211
|
+
file.setContents(fileContentsPtr, fileContents.length);
|
|
212
|
+
|
|
213
|
+
this.libopencor._free(fileContentsPtr);
|
|
214
|
+
|
|
215
|
+
return file;
|
|
216
|
+
},
|
|
217
|
+
/**
|
|
218
|
+
* @vuese
|
|
219
|
+
* Run the simulation using libOpenCOR.
|
|
220
|
+
*/
|
|
221
|
+
runSimulation() {
|
|
222
|
+
if (this.instance === undefined) {
|
|
223
|
+
const fileNameOrUrl = this.pmrBasedCombineArchive ? PMR_URL + this.id : this.simulationUiInfo.simulation.opencor.resource;
|
|
224
|
+
const document = new this.libopencor.SedDocument(toRaw(this.fileManager).file(fileNameOrUrl));
|
|
225
|
+
|
|
226
|
+
// Customise the ending point and point interval of the simulation, if
|
|
227
|
+
// needed.
|
|
228
|
+
|
|
229
|
+
if ( !this.pmrBasedCombineArchive
|
|
230
|
+
&& (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
|
|
231
|
+
&& (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
|
|
232
|
+
const simulation = document.simulations().get(0);
|
|
233
|
+
|
|
234
|
+
simulation.setOutputEndTime(this.simulationUiInfo.simulation.opencor.endingPoint);
|
|
235
|
+
simulation.setNumberOfSteps(this.simulationUiInfo.simulation.opencor.endingPoint / this.simulationUiInfo.simulation.opencor.pointInterval);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Retrieve an instance of the model.
|
|
239
|
+
|
|
240
|
+
this.instance = document.instantiate();
|
|
241
|
+
|
|
242
|
+
document.delete();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Run the simulation after passing some initial conditions to it, if any.
|
|
246
|
+
|
|
247
|
+
const instance = toRaw(this.instance);
|
|
248
|
+
|
|
249
|
+
instance.removeAllInitialConditions();
|
|
250
|
+
|
|
251
|
+
for (const [parameter, value] of Object.entries(this.parametersData())) {
|
|
252
|
+
instance.addInitialCondition(parameter, value);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
instance.run();
|
|
256
|
+
|
|
257
|
+
// Retrieve the simulation results.
|
|
258
|
+
|
|
259
|
+
const res = {};
|
|
260
|
+
const instanceTask = instance.tasks().get(0);
|
|
261
|
+
|
|
262
|
+
for (const output of this.outputData()) {
|
|
263
|
+
if (output === instanceTask.voiName()) {
|
|
264
|
+
res[output] = instanceTask.voiAsArray();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (res[output] === undefined) {
|
|
268
|
+
for (let i = 0; i < instanceTask.stateCount(); ++i) {
|
|
269
|
+
if (output === instanceTask.stateName(i)) {
|
|
270
|
+
res[output] = instanceTask.stateAsArray(i);
|
|
271
|
+
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (res[output] === undefined) {
|
|
278
|
+
for (let i = 0; instanceTask.variableCount(); ++i) {
|
|
279
|
+
if (output === instanceTask.variableName(i)) {
|
|
280
|
+
res[output] = instanceTask.variableAsArray(i);
|
|
281
|
+
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.processSimulationResults(res);
|
|
289
|
+
|
|
290
|
+
this.showUserMessage = false;
|
|
291
|
+
},
|
|
141
292
|
/**
|
|
142
293
|
* @vuese
|
|
143
294
|
* Build the simulation UI using `simulationUiInfo`, a JSON object that describes the contents of the simulation UI.
|
|
@@ -150,15 +301,128 @@ export default {
|
|
|
150
301
|
|
|
151
302
|
// Make sure that the simulation UI information is valid.
|
|
152
303
|
|
|
153
|
-
this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
|
|
304
|
+
this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo, !this.pmrBasedCombineArchive);
|
|
154
305
|
|
|
155
306
|
if (!this.hasValidSimulationUiInfo) {
|
|
156
307
|
return;
|
|
157
308
|
}
|
|
158
309
|
|
|
310
|
+
// Retrieve and keep track of the solver to be used for the simulation, if
|
|
311
|
+
// needed.
|
|
312
|
+
|
|
313
|
+
if (!this.pmrBasedCombineArchive) {
|
|
314
|
+
this.simulationUiInfo.simulation.solvers.forEach((solver) => {
|
|
315
|
+
if ((solver.if === undefined) || evaluateValue(this, solver.if)) {
|
|
316
|
+
this.solver = solver;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
if (this.solver === undefined) {
|
|
321
|
+
console.warn("SIMULATION: no solver name and/or solver version specified.");
|
|
322
|
+
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.opencorBasedSimulation = this.solver.name === OPENCOR_SOLVER_NAME;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Run the model if we are dealing with a PMR-based COMBINE archive or
|
|
330
|
+
// load libOpenCOR if we are dealing with an OpenCOR-based simulation and
|
|
331
|
+
// we want to use libOpenCOR.
|
|
332
|
+
|
|
333
|
+
if (this.pmrBasedCombineArchive) {
|
|
334
|
+
this.userMessage = "Running the model...";
|
|
335
|
+
this.showUserMessage = true;
|
|
336
|
+
|
|
337
|
+
this.$nextTick(() => {
|
|
338
|
+
this.runSimulation();
|
|
339
|
+
});
|
|
340
|
+
} else if (this.opencorBasedSimulation && (this.preferredSolver === LIBOPENCOR_SOLVER)) {
|
|
341
|
+
this.userMessage = "Retrieving and running the model...";
|
|
342
|
+
this.showUserMessage = true;
|
|
343
|
+
|
|
344
|
+
this.$nextTick(() => {
|
|
345
|
+
libOpenCOR().then((libopencor) => {
|
|
346
|
+
// Keep track of the libOpenCOR module and its file manager.
|
|
347
|
+
|
|
348
|
+
this.libopencor = libopencor;
|
|
349
|
+
this.fileManager = this.libopencor.FileManager.instance();
|
|
350
|
+
|
|
351
|
+
// Retrieve the model file, if needed.
|
|
352
|
+
|
|
353
|
+
const modelUrl = this.simulationUiInfo.simulation.opencor.resource;
|
|
354
|
+
|
|
355
|
+
this.downloadPmrFile(modelUrl).then((fileContents) => {
|
|
356
|
+
const file = this.manageFile(modelUrl, fileContents);
|
|
357
|
+
|
|
358
|
+
// In the case of a SED-ML file, we also need to retrieve its
|
|
359
|
+
// corresponding CellML file.
|
|
360
|
+
|
|
361
|
+
if (file.type().value === this.libopencor.File.Type.SEDML_FILE.value) {
|
|
362
|
+
const document = new this.libopencor.SedDocument(file);
|
|
363
|
+
const cellmlUrl = document.models().get(0).file().url();
|
|
364
|
+
|
|
365
|
+
this.downloadPmrFile(cellmlUrl).then((cellmlFileContents) => {
|
|
366
|
+
this.manageFile(cellmlUrl, cellmlFileContents);
|
|
367
|
+
|
|
368
|
+
this.runSimulation();
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
document.delete();
|
|
372
|
+
} else {
|
|
373
|
+
this.runSimulation();
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
159
380
|
// Initialise our UI.
|
|
160
381
|
|
|
161
|
-
|
|
382
|
+
this.simulationUiInfo.output.data.forEach((data) => {
|
|
383
|
+
this.simulationResultsId[data.id] = data.name;
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
let index = -1;
|
|
387
|
+
|
|
388
|
+
this.simulationUiInfo.output.plots.forEach((outputPlot) => {
|
|
389
|
+
++index;
|
|
390
|
+
|
|
391
|
+
this.layout[index] = {
|
|
392
|
+
paper_bgcolor: "rgba(0, 0, 0, 0)",
|
|
393
|
+
plot_bgcolor: "rgba(0, 0, 0, 0)",
|
|
394
|
+
autosize: true,
|
|
395
|
+
margin: {
|
|
396
|
+
t: 25,
|
|
397
|
+
l: 55,
|
|
398
|
+
r: 25,
|
|
399
|
+
b: 30,
|
|
400
|
+
pad: 4,
|
|
401
|
+
},
|
|
402
|
+
loading: false,
|
|
403
|
+
options: {
|
|
404
|
+
responsive: true,
|
|
405
|
+
scrollZoom: true,
|
|
406
|
+
},
|
|
407
|
+
dragmode: "pan",
|
|
408
|
+
xaxis: {
|
|
409
|
+
title: {
|
|
410
|
+
text: outputPlot.xAxisTitle,
|
|
411
|
+
font: {
|
|
412
|
+
size: 10,
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
yaxis: {
|
|
417
|
+
title: {
|
|
418
|
+
text: outputPlot.yAxisTitle,
|
|
419
|
+
font: {
|
|
420
|
+
size: 10,
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
});
|
|
162
426
|
|
|
163
427
|
// Finalise our UI.
|
|
164
428
|
// Note: we try both here and in the mounted() function since we have no
|
|
@@ -167,14 +431,6 @@ export default {
|
|
|
167
431
|
|
|
168
432
|
this.$nextTick(() => {
|
|
169
433
|
finaliseUi(this);
|
|
170
|
-
|
|
171
|
-
this.simulationData.forEach((data, index) => {
|
|
172
|
-
this.simulationData[index] = [{
|
|
173
|
-
x: [],
|
|
174
|
-
y: [],
|
|
175
|
-
type: "scatter",
|
|
176
|
-
}];
|
|
177
|
-
});
|
|
178
434
|
});
|
|
179
435
|
},
|
|
180
436
|
/**
|
|
@@ -196,62 +452,78 @@ export default {
|
|
|
196
452
|
},
|
|
197
453
|
/**
|
|
198
454
|
* @vuese
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* additional is added.
|
|
202
|
-
* @arg `request`
|
|
455
|
+
* View the simulation-based dataset on PMR. The simulation UI has a `View Workspace` button which, when clicked,
|
|
456
|
+
* calls this method.
|
|
203
457
|
*/
|
|
204
|
-
|
|
205
|
-
|
|
458
|
+
viewWorkspace() {
|
|
459
|
+
const url = PMR_URL + this.id;
|
|
206
460
|
|
|
207
|
-
|
|
461
|
+
window.open(url.substring(0, url.lastIndexOf("/")), "_blank");
|
|
462
|
+
},
|
|
463
|
+
/**
|
|
464
|
+
* @vuese
|
|
465
|
+
* Data needed to set a model's parameters.
|
|
466
|
+
*/
|
|
467
|
+
parametersData() {
|
|
468
|
+
const res = {};
|
|
208
469
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
json_config: {},
|
|
213
|
-
};
|
|
214
|
-
} else {
|
|
215
|
-
request.osparc = {};
|
|
216
|
-
}
|
|
470
|
+
this.simulationUiInfo.parameters.forEach((parameter) => {
|
|
471
|
+
res[parameter.name] = evaluateValue(this, parameter.value);
|
|
472
|
+
});
|
|
217
473
|
|
|
218
|
-
|
|
474
|
+
return res;
|
|
475
|
+
},
|
|
476
|
+
/**
|
|
477
|
+
* @vuese
|
|
478
|
+
* Data needed to specify the model output.
|
|
479
|
+
*/
|
|
480
|
+
outputData() {
|
|
481
|
+
if (this.output === undefined) {
|
|
482
|
+
if (this.simulationUiInfo.output.data !== undefined) {
|
|
483
|
+
this.output = [];
|
|
219
484
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
|
|
225
|
-
"Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
|
|
226
|
-
};
|
|
485
|
+
this.simulationUiInfo.output.data.forEach((output) => {
|
|
486
|
+
this.output.push(output.name);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
227
489
|
}
|
|
228
490
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
491
|
+
return this.output;
|
|
492
|
+
},
|
|
493
|
+
/**
|
|
494
|
+
* @vuese
|
|
495
|
+
* Create the `request` that is going to be used by `startSimulation` to ask oSPARC to start the simulation.
|
|
496
|
+
*/
|
|
497
|
+
retrieveRequest() {
|
|
498
|
+
const request = {
|
|
499
|
+
solver: this.solver
|
|
500
|
+
};
|
|
233
501
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
502
|
+
if (this.opencorBasedSimulation) {
|
|
503
|
+
request.opencor = {
|
|
504
|
+
model_url: this.simulationUiInfo.simulation.opencor.resource,
|
|
505
|
+
json_config: {},
|
|
506
|
+
};
|
|
237
507
|
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
508
|
+
if ( (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
|
|
509
|
+
&& (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
|
|
510
|
+
request.opencor.json_config.simulation = {
|
|
511
|
+
"Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
|
|
512
|
+
"Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
|
|
513
|
+
};
|
|
242
514
|
}
|
|
243
|
-
}
|
|
244
515
|
|
|
245
|
-
|
|
516
|
+
request.opencor.json_config.parameters = this.parametersData();
|
|
246
517
|
|
|
247
|
-
|
|
248
|
-
let index = -1;
|
|
518
|
+
const output = this.outputData();
|
|
249
519
|
|
|
250
|
-
|
|
520
|
+
if (output !== undefined) {
|
|
521
|
+
request.opencor.json_config.output = output;
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
request.osparc = {};
|
|
251
525
|
|
|
252
|
-
|
|
253
|
-
request.opencor.json_config.output[++index] = outputData.name;
|
|
254
|
-
});
|
|
526
|
+
request.osparc.job_inputs = this.parametersData();
|
|
255
527
|
}
|
|
256
528
|
|
|
257
529
|
return request;
|
|
@@ -268,9 +540,8 @@ export default {
|
|
|
268
540
|
|
|
269
541
|
if (typeof(results) === "string") {
|
|
270
542
|
const SPACES = /[ \t]+/g;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let iMax = lines[0].trim().split(SPACES).length;
|
|
543
|
+
const lines = results.trim().split("\n");
|
|
544
|
+
const iMax = lines[0].trim().split(SPACES).length;
|
|
274
545
|
|
|
275
546
|
results = {};
|
|
276
547
|
|
|
@@ -284,7 +555,7 @@ export default {
|
|
|
284
555
|
++i;
|
|
285
556
|
|
|
286
557
|
let j = -1;
|
|
287
|
-
|
|
558
|
+
const values = line.trim().split(SPACES);
|
|
288
559
|
|
|
289
560
|
values.forEach((value) => {
|
|
290
561
|
results[++j][i] = Number(value);
|
|
@@ -294,22 +565,19 @@ export default {
|
|
|
294
565
|
|
|
295
566
|
// Get the results ready for plotting.
|
|
296
567
|
|
|
297
|
-
|
|
298
|
-
let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
|
|
568
|
+
const parser = new math.parser();
|
|
299
569
|
|
|
300
|
-
this.
|
|
301
|
-
|
|
302
|
-
|
|
570
|
+
Object.keys(this.simulationResultsId).forEach((id) => {
|
|
571
|
+
parser.set(id, results[this.simulationResultsId[id]]);
|
|
572
|
+
});
|
|
303
573
|
|
|
304
|
-
|
|
305
|
-
xValue[i] = evaluateSimulationValue(this, results, outputPlot.xValue, i);
|
|
306
|
-
yValue[i] = evaluateSimulationValue(this, results, outputPlot.yValue, i);
|
|
307
|
-
}
|
|
574
|
+
let index = -1;
|
|
308
575
|
|
|
309
|
-
|
|
576
|
+
this.simulationUiInfo.output.plots.forEach((outputPlot) => {
|
|
577
|
+
this.simulationResults[++index] = [
|
|
310
578
|
{
|
|
311
|
-
x: xValue,
|
|
312
|
-
y: yValue,
|
|
579
|
+
x: parser.evaluate(outputPlot.xValue),
|
|
580
|
+
y: parser.evaluate(outputPlot.yValue),
|
|
313
581
|
type: "scatter",
|
|
314
582
|
},
|
|
315
583
|
];
|
|
@@ -325,9 +593,9 @@ export default {
|
|
|
325
593
|
checkSimulation(data) {
|
|
326
594
|
// Check the simulation.
|
|
327
595
|
|
|
328
|
-
|
|
596
|
+
const xmlhttp = new XMLHttpRequest();
|
|
329
597
|
|
|
330
|
-
xmlhttp.open("POST", this.apiLocation + "/check_simulation"
|
|
598
|
+
xmlhttp.open("POST", this.apiLocation + "/check_simulation");
|
|
331
599
|
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
332
600
|
xmlhttp.onreadystatechange = () => {
|
|
333
601
|
if (xmlhttp.readyState === 4) {
|
|
@@ -372,36 +640,18 @@ export default {
|
|
|
372
640
|
* button which, when clicked, calls this method.
|
|
373
641
|
*/
|
|
374
642
|
startSimulation() {
|
|
375
|
-
// Retrieve the solver to be used for the simulation.
|
|
376
|
-
|
|
377
|
-
let solver = undefined;
|
|
378
|
-
|
|
379
|
-
this.simulationUiInfo.simulation.solvers.forEach((crtSolver) => {
|
|
380
|
-
if ((crtSolver.if === undefined) || evaluateValue(this, crtSolver.if)) {
|
|
381
|
-
solver = crtSolver;
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
if (solver === undefined) {
|
|
386
|
-
console.warn("SIMULATION: no solver name and/or solver version specified.");
|
|
387
|
-
|
|
388
|
-
return;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
643
|
// Start the simulation (after resetting our previous simulation data, in
|
|
392
644
|
// case there were sonme).
|
|
393
|
-
// Note: we use this.$nextTick() so that the user message is shown before
|
|
394
|
-
// we get to post our HTTP request.
|
|
395
645
|
|
|
396
646
|
this.userMessage = "Loading simulation results...";
|
|
397
647
|
this.showUserMessage = true;
|
|
398
648
|
|
|
399
649
|
this.$nextTick(() => {
|
|
400
|
-
this.
|
|
650
|
+
this.simulationResults = {};
|
|
401
651
|
|
|
402
|
-
|
|
652
|
+
const xmlhttp = new XMLHttpRequest();
|
|
403
653
|
|
|
404
|
-
xmlhttp.open("POST", this.apiLocation + "/start_simulation"
|
|
654
|
+
xmlhttp.open("POST", this.apiLocation + "/start_simulation");
|
|
405
655
|
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
406
656
|
xmlhttp.onreadystatechange = () => {
|
|
407
657
|
if (xmlhttp.readyState === 4) {
|
|
@@ -423,39 +673,71 @@ export default {
|
|
|
423
673
|
}
|
|
424
674
|
}
|
|
425
675
|
};
|
|
426
|
-
xmlhttp.send(JSON.stringify(this.retrieveRequest(
|
|
427
|
-
solver: solver
|
|
428
|
-
})));
|
|
676
|
+
xmlhttp.send(JSON.stringify(this.retrieveRequest()));
|
|
429
677
|
});
|
|
430
678
|
},
|
|
431
679
|
},
|
|
432
680
|
created: function() {
|
|
433
|
-
// Try to retrieve the UI information
|
|
681
|
+
// Try to retrieve the UI information.
|
|
434
682
|
|
|
435
|
-
if (this.
|
|
683
|
+
if (this.id > 0) {
|
|
436
684
|
this.userMessage = "Retrieving UI information...";
|
|
437
685
|
this.showUserMessage = true;
|
|
438
686
|
|
|
439
687
|
// Retrieve and build the simulation UI.
|
|
440
|
-
// Note: we use this.$nextTick() so that the user message is shown before
|
|
441
|
-
// we get to post our HTTP request.
|
|
442
688
|
|
|
443
689
|
this.$nextTick(() => {
|
|
444
|
-
|
|
690
|
+
const xmlhttp = new XMLHttpRequest();
|
|
445
691
|
|
|
446
|
-
xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id
|
|
447
|
-
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
692
|
+
xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id);
|
|
448
693
|
xmlhttp.onreadystatechange = () => {
|
|
449
694
|
if (xmlhttp.readyState === 4) {
|
|
450
695
|
this.showUserMessage = false;
|
|
451
696
|
|
|
452
697
|
if (xmlhttp.status === 200) {
|
|
453
|
-
this
|
|
698
|
+
this.$nextTick(() => {
|
|
699
|
+
this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
|
|
700
|
+
});
|
|
454
701
|
}
|
|
455
702
|
}
|
|
456
703
|
};
|
|
457
704
|
xmlhttp.send();
|
|
458
705
|
});
|
|
706
|
+
} else if (this.id !== 0) {
|
|
707
|
+
this.userMessage = "Retrieving OMEX file...";
|
|
708
|
+
this.showUserMessage = true;
|
|
709
|
+
|
|
710
|
+
// Retrieve the OMEX file, extract the simulation UI JSON file from it and
|
|
711
|
+
// then build the simulation UI.
|
|
712
|
+
|
|
713
|
+
this.$nextTick(() => {
|
|
714
|
+
const xmlhttp = new XMLHttpRequest();
|
|
715
|
+
|
|
716
|
+
xmlhttp.open("POST", this.apiLocation + "/pmr_file");
|
|
717
|
+
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
718
|
+
xmlhttp.onreadystatechange = () => {
|
|
719
|
+
if ((xmlhttp.readyState === 4) && (xmlhttp.status === 200)) {
|
|
720
|
+
libOpenCOR().then((libopencor) => {
|
|
721
|
+
this.pmrBasedCombineArchive = true;
|
|
722
|
+
this.libopencor = libopencor;
|
|
723
|
+
this.fileManager = this.libopencor.FileManager.instance();
|
|
724
|
+
|
|
725
|
+
const fileContents = Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0));
|
|
726
|
+
const file = this.manageFile(PMR_URL + this.id, fileContents);
|
|
727
|
+
|
|
728
|
+
const decoder = new TextDecoder();
|
|
729
|
+
const simulationUiInfo = JSON.parse(decoder.decode(file.childFile("simulation.json").contents()));
|
|
730
|
+
|
|
731
|
+
this.showUserMessage = false;
|
|
732
|
+
|
|
733
|
+
this.$nextTick(() => {
|
|
734
|
+
this.buildSimulationUi(simulationUiInfo);
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
xmlhttp.send(JSON.stringify({path: this.id}));
|
|
740
|
+
});
|
|
459
741
|
}
|
|
460
742
|
},
|
|
461
743
|
mounted: function() {
|