@abi-software/simulationvuer 1.0.0 → 2.0.1
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 -7743
- 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 +408 -139
- 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
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<p v-if="!hasValidSimulationUiInfo && !showUserMessage" class="default error"><span class="error">Error:</span> an unknown or invalid model was provided.</p>
|
|
4
4
|
<div class="main" v-if="hasValidSimulationUiInfo">
|
|
5
5
|
<div class="main-left">
|
|
6
|
-
<p class="default name">{{name}}</p>
|
|
7
|
-
<el-divider></el-divider>
|
|
6
|
+
<p class="default name" v-if="libopencor === undefined">{{name}}</p>
|
|
7
|
+
<el-divider v-if="libopencor === undefined"></el-divider>
|
|
8
8
|
<p class="default input-parameters">Input parameters</p>
|
|
9
9
|
<div class="input scrollbar">
|
|
10
10
|
<SimulationVuerInput v-for="(input, index) in simulationUiInfo.input"
|
|
@@ -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="libopencor === undefined">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="libopencor === undefined">View Dataset</el-button>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="secondary-button">
|
|
31
|
+
<el-button size="small" @click="viewWorkspace()" v-if="libopencor !== undefined">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,
|
|
@@ -76,50 +89,57 @@ export default {
|
|
|
76
89
|
type: String,
|
|
77
90
|
},
|
|
78
91
|
/**
|
|
79
|
-
* The ID of the simulation-based dataset
|
|
92
|
+
* The ID of the simulation-based dataset, if it is a number, or the path
|
|
93
|
+
* to a PMR file, if it is a string.
|
|
80
94
|
*/
|
|
81
95
|
id: {
|
|
82
96
|
required: true,
|
|
83
|
-
type: Number,
|
|
84
97
|
},
|
|
85
98
|
},
|
|
86
99
|
data: function() {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
// Retrieve some information about the dataset.
|
|
101
|
+
|
|
102
|
+
if (this.id > 0) {
|
|
103
|
+
const xmlhttp = new XMLHttpRequest();
|
|
104
|
+
|
|
105
|
+
xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id);
|
|
106
|
+
xmlhttp.onreadystatechange = () => {
|
|
107
|
+
if (xmlhttp.readyState === 4) {
|
|
108
|
+
if (xmlhttp.status === 200) {
|
|
109
|
+
const datasetInfo = JSON.parse(xmlhttp.responseText);
|
|
110
|
+
|
|
111
|
+
this.name = datasetInfo.name;
|
|
112
|
+
this.uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
|
|
113
|
+
}
|
|
100
114
|
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
};
|
|
116
|
+
xmlhttp.send();
|
|
117
|
+
}
|
|
104
118
|
|
|
105
119
|
return {
|
|
106
120
|
errorMessage: "",
|
|
121
|
+
fileManager: undefined,
|
|
107
122
|
hasFinalisedUi: false,
|
|
108
123
|
hasValidSimulationUiInfo: false,
|
|
124
|
+
instance: undefined,
|
|
109
125
|
isMounted: false,
|
|
110
126
|
isSimulationValid: true,
|
|
111
127
|
layout: [],
|
|
112
|
-
|
|
128
|
+
libopencor: undefined,
|
|
129
|
+
name: null,
|
|
130
|
+
opencorBasedSimulation: true,
|
|
131
|
+
output: undefined,
|
|
113
132
|
perfectScollbarOptions: {
|
|
114
133
|
suppressScrollX: true,
|
|
115
134
|
},
|
|
116
135
|
showUserMessage: false,
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
simulationResults: {},
|
|
137
|
+
simulationResultsId: {},
|
|
119
138
|
simulationUiInfo: {},
|
|
139
|
+
solver: undefined,
|
|
120
140
|
userMessage: "",
|
|
121
141
|
ui: null,
|
|
122
|
-
uuid:
|
|
142
|
+
uuid: null,
|
|
123
143
|
};
|
|
124
144
|
},
|
|
125
145
|
methods: {
|
|
@@ -138,6 +158,115 @@ export default {
|
|
|
138
158
|
},
|
|
139
159
|
};
|
|
140
160
|
},
|
|
161
|
+
/**
|
|
162
|
+
* @vuese
|
|
163
|
+
* Download the PMR file associated with the given `url`.
|
|
164
|
+
* @arg `url`
|
|
165
|
+
*/
|
|
166
|
+
downloadPmrFile(url) {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
const xmlhttp = new XMLHttpRequest();
|
|
169
|
+
|
|
170
|
+
xmlhttp.open("POST", this.apiLocation + "/pmr_file");
|
|
171
|
+
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
172
|
+
xmlhttp.onreadystatechange = () => {
|
|
173
|
+
if (xmlhttp.readyState === 4) {
|
|
174
|
+
if (xmlhttp.status === 200) {
|
|
175
|
+
resolve(Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0)));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
reject();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
xmlhttp.send(JSON.stringify({path: url.replace(PMR_URL, "")}));
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* @vuese
|
|
186
|
+
* Manage the file associated with the given `url` and `fileContents`.
|
|
187
|
+
* @arg `url`
|
|
188
|
+
* @arg `fileContents`
|
|
189
|
+
*/
|
|
190
|
+
manageFile(url, fileContents) {
|
|
191
|
+
let file = toRaw(this.fileManager).file(url);
|
|
192
|
+
|
|
193
|
+
if (file === null) {
|
|
194
|
+
file = new this.libopencor.File(url);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const fileContentsPtr = this.libopencor._malloc(fileContents.length);
|
|
198
|
+
const mem = new Uint8Array(this.libopencor.HEAPU8.buffer, fileContentsPtr, fileContents.length);
|
|
199
|
+
|
|
200
|
+
mem.set(fileContents);
|
|
201
|
+
|
|
202
|
+
file.setContents(fileContentsPtr, fileContents.length);
|
|
203
|
+
|
|
204
|
+
this.libopencor._free(fileContentsPtr);
|
|
205
|
+
|
|
206
|
+
return file;
|
|
207
|
+
},
|
|
208
|
+
/**
|
|
209
|
+
* @vuese
|
|
210
|
+
* Run a PMR-based COMBINE archive using libOpenCOR.
|
|
211
|
+
*/
|
|
212
|
+
runSimulation() {
|
|
213
|
+
if (this.instance === undefined) {
|
|
214
|
+
// Retrieve an instance of the model.
|
|
215
|
+
|
|
216
|
+
const document = new this.libopencor.SedDocument(toRaw(this.fileManager).file(PMR_URL + this.id));
|
|
217
|
+
|
|
218
|
+
this.instance = document.instantiate();
|
|
219
|
+
|
|
220
|
+
document.delete();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Run the simulation after passing some initial conditions to it, if any.
|
|
224
|
+
|
|
225
|
+
const instance = toRaw(this.instance);
|
|
226
|
+
|
|
227
|
+
instance.removeAllInitialConditions();
|
|
228
|
+
|
|
229
|
+
for (const [parameter, value] of Object.entries(this.parametersData())) {
|
|
230
|
+
instance.addInitialCondition(parameter, value);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
instance.run();
|
|
234
|
+
|
|
235
|
+
// Retrieve the simulation results.
|
|
236
|
+
|
|
237
|
+
const res = {};
|
|
238
|
+
const instanceTask = instance.tasks().get(0);
|
|
239
|
+
|
|
240
|
+
for (const output of this.outputData()) {
|
|
241
|
+
if (output === instanceTask.voiName()) {
|
|
242
|
+
res[output] = instanceTask.voiAsArray();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (res[output] === undefined) {
|
|
246
|
+
for (let i = 0; i < instanceTask.stateCount(); ++i) {
|
|
247
|
+
if (output === instanceTask.stateName(i)) {
|
|
248
|
+
res[output] = instanceTask.stateAsArray(i);
|
|
249
|
+
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (res[output] === undefined) {
|
|
256
|
+
for (let i = 0; instanceTask.variableCount(); ++i) {
|
|
257
|
+
if (output === instanceTask.variableName(i)) {
|
|
258
|
+
res[output] = instanceTask.variableAsArray(i);
|
|
259
|
+
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
this.processSimulationResults(res);
|
|
267
|
+
|
|
268
|
+
this.showUserMessage = false;
|
|
269
|
+
},
|
|
141
270
|
/**
|
|
142
271
|
* @vuese
|
|
143
272
|
* Build the simulation UI using `simulationUiInfo`, a JSON object that describes the contents of the simulation UI.
|
|
@@ -150,15 +279,128 @@ export default {
|
|
|
150
279
|
|
|
151
280
|
// Make sure that the simulation UI information is valid.
|
|
152
281
|
|
|
153
|
-
this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
|
|
282
|
+
this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo, this.libopencor === undefined);
|
|
154
283
|
|
|
155
284
|
if (!this.hasValidSimulationUiInfo) {
|
|
156
285
|
return;
|
|
157
286
|
}
|
|
158
287
|
|
|
288
|
+
// Retrieve and keep track of the solver to be used for the simulation, if
|
|
289
|
+
// needed.
|
|
290
|
+
|
|
291
|
+
if (this.libopencor === undefined) {
|
|
292
|
+
this.simulationUiInfo.simulation.solvers.forEach((solver) => {
|
|
293
|
+
if ((solver.if === undefined) || evaluateValue(this, solver.if)) {
|
|
294
|
+
this.solver = solver;
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (this.solver === undefined) {
|
|
299
|
+
console.warn("SIMULATION: no solver name and/or solver version specified.");
|
|
300
|
+
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this.opencorBasedSimulation = this.solver.name === OPENCOR_SOLVER_NAME;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Run the model if we are dealing with a PMR-based COMBINE archive or
|
|
308
|
+
// load libOpenCOR if we are dealing with an OpenCOR-based simulation and
|
|
309
|
+
// we want to use libOpenCOR.
|
|
310
|
+
|
|
311
|
+
if (this.libopencor !== undefined) {
|
|
312
|
+
this.userMessage = "Running the model...";
|
|
313
|
+
this.showUserMessage = true;
|
|
314
|
+
|
|
315
|
+
this.$nextTick(() => {
|
|
316
|
+
this.runSimulation();
|
|
317
|
+
});
|
|
318
|
+
} else if (this.opencorBasedSimulation && (this.libopencor !== undefined)) {
|
|
319
|
+
this.userMessage = "Retrieving and running the model...";
|
|
320
|
+
this.showUserMessage = true;
|
|
321
|
+
|
|
322
|
+
this.$nextTick(() => {
|
|
323
|
+
libOpenCOR().then((libopencor) => {
|
|
324
|
+
// Keep track of the libOpenCOR module and its file manager.
|
|
325
|
+
|
|
326
|
+
this.libopencor = libopencor;
|
|
327
|
+
this.fileManager = this.libopencor.FileManager.instance();
|
|
328
|
+
|
|
329
|
+
// Retrieve the model file, if needed.
|
|
330
|
+
|
|
331
|
+
const modelUrl = this.simulationUiInfo.simulation.opencor.resource;
|
|
332
|
+
|
|
333
|
+
this.downloadPmrFile(modelUrl).then((fileContents) => {
|
|
334
|
+
const file = this.manageFile(modelUrl, fileContents);
|
|
335
|
+
|
|
336
|
+
// In the case of a SED-ML file, we also need to retrieve its
|
|
337
|
+
// corresponding CellML file.
|
|
338
|
+
|
|
339
|
+
if (file.type().value === this.libopencor.File.Type.SEDML_FILE.value) {
|
|
340
|
+
const document = new this.libopencor.SedDocument(file);
|
|
341
|
+
const cellmlUrl = document.models().get(0).file().url();
|
|
342
|
+
|
|
343
|
+
this.downloadPmrFile(cellmlUrl).then((cellmlFileContents) => {
|
|
344
|
+
this.manageFile(cellmlUrl, cellmlFileContents);
|
|
345
|
+
|
|
346
|
+
this.runSimulation();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
document.delete();
|
|
350
|
+
} else {
|
|
351
|
+
this.runSimulation();
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
159
358
|
// Initialise our UI.
|
|
160
359
|
|
|
161
|
-
|
|
360
|
+
this.simulationUiInfo.output.data.forEach((data) => {
|
|
361
|
+
this.simulationResultsId[data.id] = data.name;
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
let index = -1;
|
|
365
|
+
|
|
366
|
+
this.simulationUiInfo.output.plots.forEach((outputPlot) => {
|
|
367
|
+
++index;
|
|
368
|
+
|
|
369
|
+
this.layout[index] = {
|
|
370
|
+
paper_bgcolor: "rgba(0, 0, 0, 0)",
|
|
371
|
+
plot_bgcolor: "rgba(0, 0, 0, 0)",
|
|
372
|
+
autosize: true,
|
|
373
|
+
margin: {
|
|
374
|
+
t: 25,
|
|
375
|
+
l: 55,
|
|
376
|
+
r: 25,
|
|
377
|
+
b: 30,
|
|
378
|
+
pad: 4,
|
|
379
|
+
},
|
|
380
|
+
loading: false,
|
|
381
|
+
options: {
|
|
382
|
+
responsive: true,
|
|
383
|
+
scrollZoom: true,
|
|
384
|
+
},
|
|
385
|
+
dragmode: "pan",
|
|
386
|
+
xaxis: {
|
|
387
|
+
title: {
|
|
388
|
+
text: outputPlot.xAxisTitle,
|
|
389
|
+
font: {
|
|
390
|
+
size: 10,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
yaxis: {
|
|
395
|
+
title: {
|
|
396
|
+
text: outputPlot.yAxisTitle,
|
|
397
|
+
font: {
|
|
398
|
+
size: 10,
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
});
|
|
162
404
|
|
|
163
405
|
// Finalise our UI.
|
|
164
406
|
// Note: we try both here and in the mounted() function since we have no
|
|
@@ -167,14 +409,6 @@ export default {
|
|
|
167
409
|
|
|
168
410
|
this.$nextTick(() => {
|
|
169
411
|
finaliseUi(this);
|
|
170
|
-
|
|
171
|
-
this.simulationData.forEach((data, index) => {
|
|
172
|
-
this.simulationData[index] = [{
|
|
173
|
-
x: [],
|
|
174
|
-
y: [],
|
|
175
|
-
type: "scatter",
|
|
176
|
-
}];
|
|
177
|
-
});
|
|
178
412
|
});
|
|
179
413
|
},
|
|
180
414
|
/**
|
|
@@ -196,62 +430,78 @@ export default {
|
|
|
196
430
|
},
|
|
197
431
|
/**
|
|
198
432
|
* @vuese
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
* additional is added.
|
|
202
|
-
* @arg `request`
|
|
433
|
+
* View the simulation-based dataset on PMR. The simulation UI has a `View Workspace` button which, when clicked,
|
|
434
|
+
* calls this method.
|
|
203
435
|
*/
|
|
204
|
-
|
|
205
|
-
|
|
436
|
+
viewWorkspace() {
|
|
437
|
+
const url = PMR_URL + this.id;
|
|
206
438
|
|
|
207
|
-
|
|
439
|
+
window.open(url.substring(0, url.lastIndexOf("/")), "_blank");
|
|
440
|
+
},
|
|
441
|
+
/**
|
|
442
|
+
* @vuese
|
|
443
|
+
* Data needed to set a model's parameters.
|
|
444
|
+
*/
|
|
445
|
+
parametersData() {
|
|
446
|
+
const res = {};
|
|
208
447
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
json_config: {},
|
|
213
|
-
};
|
|
214
|
-
} else {
|
|
215
|
-
request.osparc = {};
|
|
216
|
-
}
|
|
448
|
+
this.simulationUiInfo.parameters.forEach((parameter) => {
|
|
449
|
+
res[parameter.name] = evaluateValue(this, parameter.value);
|
|
450
|
+
});
|
|
217
451
|
|
|
218
|
-
|
|
452
|
+
return res;
|
|
453
|
+
},
|
|
454
|
+
/**
|
|
455
|
+
* @vuese
|
|
456
|
+
* Data needed to specify the model output.
|
|
457
|
+
*/
|
|
458
|
+
outputData() {
|
|
459
|
+
if (this.output === undefined) {
|
|
460
|
+
if (this.simulationUiInfo.output.data !== undefined) {
|
|
461
|
+
this.output = [];
|
|
219
462
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
"Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
|
|
225
|
-
"Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
|
|
226
|
-
};
|
|
463
|
+
this.simulationUiInfo.output.data.forEach((output) => {
|
|
464
|
+
this.output.push(output.name);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
227
467
|
}
|
|
228
468
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
469
|
+
return this.output;
|
|
470
|
+
},
|
|
471
|
+
/**
|
|
472
|
+
* @vuese
|
|
473
|
+
* Create the `request` that is going to be used by `startSimulation` to ask oSPARC to start the simulation.
|
|
474
|
+
*/
|
|
475
|
+
retrieveRequest() {
|
|
476
|
+
const request = {
|
|
477
|
+
solver: this.solver
|
|
478
|
+
};
|
|
233
479
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
480
|
+
if (this.opencorBasedSimulation) {
|
|
481
|
+
request.opencor = {
|
|
482
|
+
model_url: this.simulationUiInfo.simulation.opencor.resource,
|
|
483
|
+
json_config: {},
|
|
484
|
+
};
|
|
237
485
|
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
486
|
+
if ( (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
|
|
487
|
+
&& (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
|
|
488
|
+
request.opencor.json_config.simulation = {
|
|
489
|
+
"Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
|
|
490
|
+
"Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
|
|
491
|
+
};
|
|
242
492
|
}
|
|
243
|
-
}
|
|
244
493
|
|
|
245
|
-
|
|
494
|
+
request.opencor.json_config.parameters = this.parametersData();
|
|
246
495
|
|
|
247
|
-
|
|
248
|
-
let index = -1;
|
|
496
|
+
const output = this.outputData();
|
|
249
497
|
|
|
250
|
-
|
|
498
|
+
if (output !== undefined) {
|
|
499
|
+
request.opencor.json_config.output = output;
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
request.osparc = {};
|
|
251
503
|
|
|
252
|
-
|
|
253
|
-
request.opencor.json_config.output[++index] = outputData.name;
|
|
254
|
-
});
|
|
504
|
+
request.osparc.job_inputs = this.parametersData();
|
|
255
505
|
}
|
|
256
506
|
|
|
257
507
|
return request;
|
|
@@ -268,9 +518,8 @@ export default {
|
|
|
268
518
|
|
|
269
519
|
if (typeof(results) === "string") {
|
|
270
520
|
const SPACES = /[ \t]+/g;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let iMax = lines[0].trim().split(SPACES).length;
|
|
521
|
+
const lines = results.trim().split("\n");
|
|
522
|
+
const iMax = lines[0].trim().split(SPACES).length;
|
|
274
523
|
|
|
275
524
|
results = {};
|
|
276
525
|
|
|
@@ -284,7 +533,7 @@ export default {
|
|
|
284
533
|
++i;
|
|
285
534
|
|
|
286
535
|
let j = -1;
|
|
287
|
-
|
|
536
|
+
const values = line.trim().split(SPACES);
|
|
288
537
|
|
|
289
538
|
values.forEach((value) => {
|
|
290
539
|
results[++j][i] = Number(value);
|
|
@@ -294,27 +543,34 @@ export default {
|
|
|
294
543
|
|
|
295
544
|
// Get the results ready for plotting.
|
|
296
545
|
|
|
297
|
-
|
|
298
|
-
let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
|
|
546
|
+
const parser = new math.parser();
|
|
299
547
|
|
|
300
|
-
this.
|
|
301
|
-
|
|
302
|
-
|
|
548
|
+
Object.keys(this.simulationResultsId).forEach((id) => {
|
|
549
|
+
parser.set(id, results[this.simulationResultsId[id]]);
|
|
550
|
+
});
|
|
303
551
|
|
|
304
|
-
|
|
305
|
-
xValue[i] = evaluateSimulationValue(this, results, outputPlot.xValue, i);
|
|
306
|
-
yValue[i] = evaluateSimulationValue(this, results, outputPlot.yValue, i);
|
|
307
|
-
}
|
|
552
|
+
let index = -1;
|
|
308
553
|
|
|
309
|
-
|
|
554
|
+
this.simulationUiInfo.output.plots.forEach((outputPlot) => {
|
|
555
|
+
this.simulationResults[++index] = [
|
|
310
556
|
{
|
|
311
|
-
x: xValue,
|
|
312
|
-
y: yValue,
|
|
557
|
+
x: parser.evaluate(outputPlot.xValue),
|
|
558
|
+
y: parser.evaluate(outputPlot.yValue),
|
|
313
559
|
type: "scatter",
|
|
314
560
|
},
|
|
315
561
|
];
|
|
316
562
|
});
|
|
317
563
|
},
|
|
564
|
+
/**
|
|
565
|
+
* @vuese
|
|
566
|
+
* Show an HTTP issue using the given `xmlhttp`.
|
|
567
|
+
* @arg `xmlhttp`
|
|
568
|
+
*/
|
|
569
|
+
showHttpIssue(xmlhttp) {
|
|
570
|
+
this.isSimulationValid = false;
|
|
571
|
+
this.showUserMessage = false;
|
|
572
|
+
this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
|
|
573
|
+
},
|
|
318
574
|
/**
|
|
319
575
|
* @vuese
|
|
320
576
|
* Check the progress of the simulation using the given `data`, a JSON object that contains the simulation job ID,
|
|
@@ -325,9 +581,9 @@ export default {
|
|
|
325
581
|
checkSimulation(data) {
|
|
326
582
|
// Check the simulation.
|
|
327
583
|
|
|
328
|
-
|
|
584
|
+
const xmlhttp = new XMLHttpRequest();
|
|
329
585
|
|
|
330
|
-
xmlhttp.open("POST", this.apiLocation + "/check_simulation"
|
|
586
|
+
xmlhttp.open("POST", this.apiLocation + "/check_simulation");
|
|
331
587
|
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
332
588
|
xmlhttp.onreadystatechange = () => {
|
|
333
589
|
if (xmlhttp.readyState === 4) {
|
|
@@ -355,12 +611,10 @@ export default {
|
|
|
355
611
|
}
|
|
356
612
|
} else {
|
|
357
613
|
this.showUserMessage = false;
|
|
358
|
-
this.errorMessage = response.description;
|
|
614
|
+
this.errorMessage = response.description + "QWEQWEQWE";
|
|
359
615
|
}
|
|
360
616
|
} else {
|
|
361
|
-
this.
|
|
362
|
-
this.showUserMessage = false;
|
|
363
|
-
this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
|
|
617
|
+
this.showHttpIssue(xmlhttp);
|
|
364
618
|
}
|
|
365
619
|
}
|
|
366
620
|
};
|
|
@@ -372,36 +626,18 @@ export default {
|
|
|
372
626
|
* button which, when clicked, calls this method.
|
|
373
627
|
*/
|
|
374
628
|
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
629
|
// Start the simulation (after resetting our previous simulation data, in
|
|
392
630
|
// 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
631
|
|
|
396
632
|
this.userMessage = "Loading simulation results...";
|
|
397
633
|
this.showUserMessage = true;
|
|
398
634
|
|
|
399
635
|
this.$nextTick(() => {
|
|
400
|
-
this.
|
|
636
|
+
this.simulationResults = {};
|
|
401
637
|
|
|
402
|
-
|
|
638
|
+
const xmlhttp = new XMLHttpRequest();
|
|
403
639
|
|
|
404
|
-
xmlhttp.open("POST", this.apiLocation + "/start_simulation"
|
|
640
|
+
xmlhttp.open("POST", this.apiLocation + "/start_simulation");
|
|
405
641
|
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
406
642
|
xmlhttp.onreadystatechange = () => {
|
|
407
643
|
if (xmlhttp.readyState === 4) {
|
|
@@ -414,48 +650,81 @@ export default {
|
|
|
414
650
|
this.checkSimulation(response.data);
|
|
415
651
|
} else {
|
|
416
652
|
this.showUserMessage = false;
|
|
417
|
-
this.errorMessage = response.description;
|
|
653
|
+
this.errorMessage = response.description + "ASDASDASD";
|
|
418
654
|
}
|
|
419
655
|
} else {
|
|
420
|
-
this.
|
|
421
|
-
this.showUserMessage = false;
|
|
422
|
-
this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
|
|
656
|
+
this.showHttpIssue(xmlhttp);
|
|
423
657
|
}
|
|
424
658
|
}
|
|
425
659
|
};
|
|
426
|
-
xmlhttp.send(JSON.stringify(this.retrieveRequest(
|
|
427
|
-
solver: solver
|
|
428
|
-
})));
|
|
660
|
+
xmlhttp.send(JSON.stringify(this.retrieveRequest()));
|
|
429
661
|
});
|
|
430
662
|
},
|
|
431
663
|
},
|
|
432
664
|
created: function() {
|
|
433
|
-
// Try to retrieve the UI information
|
|
665
|
+
// Try to retrieve the UI information.
|
|
434
666
|
|
|
435
|
-
if (this.
|
|
667
|
+
if (this.id > 0) {
|
|
436
668
|
this.userMessage = "Retrieving UI information...";
|
|
437
669
|
this.showUserMessage = true;
|
|
438
670
|
|
|
439
671
|
// 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
672
|
|
|
443
673
|
this.$nextTick(() => {
|
|
444
|
-
|
|
674
|
+
const xmlhttp = new XMLHttpRequest();
|
|
445
675
|
|
|
446
|
-
xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id
|
|
447
|
-
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
676
|
+
xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id);
|
|
448
677
|
xmlhttp.onreadystatechange = () => {
|
|
449
678
|
if (xmlhttp.readyState === 4) {
|
|
450
679
|
this.showUserMessage = false;
|
|
451
680
|
|
|
452
681
|
if (xmlhttp.status === 200) {
|
|
453
|
-
this
|
|
682
|
+
this.$nextTick(() => {
|
|
683
|
+
this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
|
|
684
|
+
});
|
|
454
685
|
}
|
|
455
686
|
}
|
|
456
687
|
};
|
|
457
688
|
xmlhttp.send();
|
|
458
689
|
});
|
|
690
|
+
} else if (this.id !== 0) {
|
|
691
|
+
this.userMessage = "Retrieving COMBINE archive from PMR...";
|
|
692
|
+
this.showUserMessage = true;
|
|
693
|
+
|
|
694
|
+
// Retrieve the OMEX file, extract the simulation UI JSON file from it and
|
|
695
|
+
// then build the simulation UI.
|
|
696
|
+
|
|
697
|
+
this.$nextTick(() => {
|
|
698
|
+
const xmlhttp = new XMLHttpRequest();
|
|
699
|
+
|
|
700
|
+
xmlhttp.open("POST", this.apiLocation + "/pmr_file");
|
|
701
|
+
xmlhttp.setRequestHeader("Content-type", "application/json");
|
|
702
|
+
xmlhttp.onreadystatechange = () => {
|
|
703
|
+
if (xmlhttp.readyState === 4) {
|
|
704
|
+
if (xmlhttp.status === 200) {
|
|
705
|
+
libOpenCOR().then((libopencor) => {
|
|
706
|
+
this.libopencor = libopencor;
|
|
707
|
+
this.fileManager = this.libopencor.FileManager.instance();
|
|
708
|
+
|
|
709
|
+
const fileContents = Uint8Array.from(atob(xmlhttp.response), (c) => c.charCodeAt(0));
|
|
710
|
+
const file = this.manageFile(PMR_URL + this.id, fileContents);
|
|
711
|
+
|
|
712
|
+
const decoder = new TextDecoder();
|
|
713
|
+
const simulationUiInfo = JSON.parse(decoder.decode(file.childFile("simulation.json").contents()));
|
|
714
|
+
|
|
715
|
+
this.showUserMessage = false;
|
|
716
|
+
|
|
717
|
+
this.$nextTick(() => {
|
|
718
|
+
this.buildSimulationUi(simulationUiInfo);
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
} else {
|
|
722
|
+
this.showUserMessage = false;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
xmlhttp.send(JSON.stringify({path: this.id}));
|
|
727
|
+
});
|
|
459
728
|
}
|
|
460
729
|
},
|
|
461
730
|
mounted: function() {
|