@abi-software/simulationvuer 0.7.0-vue-3-alpha.5 → 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.
@@ -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: simulationData[index]}"
39
+ :data-source="{data: simulationResults[index]}"
37
40
  :plotLayout="layout[index]"
38
41
  :plotType="'plotly-only'"
39
42
  :selectorUi="false"
@@ -51,11 +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, evaluateSimulationValue, OPENCOR_SOLVER_NAME } from "./common.js";
57
+ import { evaluateValue, finaliseUi, OPENCOR_SOLVER_NAME } from "./common.js";
55
58
  import { validJson } from "./json.js";
56
- import { initialiseUi, finaliseUi } from "./ui.js";
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/";
57
66
 
67
+ const math = create(all, {});
68
+
69
+ /**
70
+ * SimulationVuer
71
+ */
58
72
  export default {
73
+ LIBOPENCOR_SOLVER: LIBOPENCOR_SOLVER,
74
+ OSPARC_SOLVER: OSPARC_SOLVER,
59
75
  name: "SimulationVuer",
60
76
  components: {
61
77
  PlotVuer,
@@ -65,55 +81,82 @@ export default {
65
81
  ElLoading,
66
82
  },
67
83
  props: {
84
+ /**
85
+ * The the URL to the API location.
86
+ */
68
87
  apiLocation: {
69
88
  required: true,
70
89
  type: String,
71
90
  },
91
+ /**
92
+ * The ID of the simulation-based dataset.
93
+ */
72
94
  id: {
73
95
  required: true,
74
- type: Number,
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,
75
105
  },
76
106
  },
77
107
  data: function() {
78
- let xmlhttp = new XMLHttpRequest();
79
- let name = undefined;
80
- let uuid = undefined;
81
-
82
- xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id, false);
83
- xmlhttp.setRequestHeader("Content-type", "application/json");
84
- xmlhttp.onreadystatechange = () => {
85
- if (xmlhttp.readyState === 4) {
86
- if (xmlhttp.status === 200) {
87
- let datasetInfo = JSON.parse(xmlhttp.responseText);
88
-
89
- name = datasetInfo.name;
90
- uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
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
+ }
91
122
  }
92
- }
93
- };
94
- xmlhttp.send();
123
+ };
124
+ xmlhttp.send();
125
+ }
95
126
 
96
127
  return {
97
128
  errorMessage: "",
129
+ fileManager: undefined,
98
130
  hasFinalisedUi: false,
99
131
  hasValidSimulationUiInfo: false,
132
+ instance: undefined,
100
133
  isMounted: false,
101
134
  isSimulationValid: true,
102
135
  layout: [],
103
- name: name,
136
+ libopencor: undefined,
137
+ name: null,
138
+ opencorBasedSimulation: true,
139
+ output: undefined,
104
140
  perfectScollbarOptions: {
105
141
  suppressScrollX: true,
106
142
  },
143
+ pmrBasedCombineArchive: false,
107
144
  showUserMessage: false,
108
- simulationData: [],
109
- simulationDataId: {},
145
+ simulationResults: {},
146
+ simulationResultsId: {},
110
147
  simulationUiInfo: {},
148
+ solver: undefined,
111
149
  userMessage: "",
112
150
  ui: null,
113
- uuid: uuid,
151
+ uuid: null,
114
152
  };
115
153
  },
116
154
  methods: {
155
+ /**
156
+ * @vuese
157
+ * Generate the metadata associated with the plot which `index` is given.
158
+ * @arg `index`
159
+ */
117
160
  plotMetadata(index) {
118
161
  return {
119
162
  version: "1.1.0",
@@ -124,22 +167,262 @@ export default {
124
167
  },
125
168
  };
126
169
  },
127
- retrieveAndBuildSimulationUi(simulationUiInfo) {
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
+ },
292
+ /**
293
+ * @vuese
294
+ * Build the simulation UI using `simulationUiInfo`, a JSON object that describes the contents of the simulation UI.
295
+ * @arg `simulationUiInfo`
296
+ */
297
+ buildSimulationUi(simulationUiInfo) {
128
298
  // Keep track of the simulation UI information.
129
299
 
130
300
  this.simulationUiInfo = simulationUiInfo;
131
301
 
132
302
  // Make sure that the simulation UI information is valid.
133
303
 
134
- this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
304
+ this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo, !this.pmrBasedCombineArchive);
135
305
 
136
306
  if (!this.hasValidSimulationUiInfo) {
137
307
  return;
138
308
  }
139
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
+
140
380
  // Initialise our UI.
141
381
 
142
- initialiseUi(this);
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
+ });
143
426
 
144
427
  // Finalise our UI.
145
428
  // Note: we try both here and in the mounted() function since we have no
@@ -148,86 +431,117 @@ export default {
148
431
 
149
432
  this.$nextTick(() => {
150
433
  finaliseUi(this);
151
-
152
- this.simulationData.forEach((data, index) => {
153
- this.simulationData[index] = [{
154
- x: [],
155
- y: [],
156
- type: "scatter",
157
- }];
158
- });
159
434
  });
160
435
  },
436
+ /**
437
+ * @vuese
438
+ * Run the simulation-based dataset directly on oSPARC. Not all simulation-based datasets can be run directly on
439
+ * oSPARC, but for those that can the simulation UI shows a `Run on oSPARC` button which, when clicked, calls this
440
+ * method.
441
+ */
161
442
  runOnOsparc() {
162
443
  window.open(`https://osparc.io/study/${this.uuid}`, "_blank");
163
444
  },
445
+ /**
446
+ * @vuese
447
+ * View the simulation-based dataset on the SPARC portal. The simulation UI has a `View Dataset` button which, when
448
+ * clicked, calls this method.
449
+ */
164
450
  viewDataset() {
165
451
  window.open(`https://sparc.science/datasets/${this.id}?type=dataset`, "_blank");
166
452
  },
167
- retrieveRequest(request) {
168
- // Settings specific to OpenCOR/oSPARC.
453
+ /**
454
+ * @vuese
455
+ * View the simulation-based dataset on PMR. The simulation UI has a `View Workspace` button which, when clicked,
456
+ * calls this method.
457
+ */
458
+ viewWorkspace() {
459
+ const url = PMR_URL + this.id;
460
+
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 = {};
469
+
470
+ this.simulationUiInfo.parameters.forEach((parameter) => {
471
+ res[parameter.name] = evaluateValue(this, parameter.value);
472
+ });
473
+
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 = [];
484
+
485
+ this.simulationUiInfo.output.data.forEach((output) => {
486
+ this.output.push(output.name);
487
+ });
488
+ }
489
+ }
169
490
 
170
- let isOpencorSimulation = request.solver.name === OPENCOR_SOLVER_NAME;
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
+ };
171
501
 
172
- if (isOpencorSimulation) {
502
+ if (this.opencorBasedSimulation) {
173
503
  request.opencor = {
174
504
  model_url: this.simulationUiInfo.simulation.opencor.resource,
175
505
  json_config: {},
176
506
  };
177
- } else {
178
- request.osparc = {};
179
- }
180
-
181
- // Specify the ending point and point interval, if we have some.
182
-
183
- if ( isOpencorSimulation
184
- && (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
185
- && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
186
- request.opencor.json_config.simulation = {
187
- "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
188
- "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
189
- };
190
- }
191
-
192
- // Specify the parameters, if any.
193
-
194
- if (this.simulationUiInfo.parameters !== undefined) {
195
- let parameters = {};
196
507
 
197
- this.simulationUiInfo.parameters.forEach((parameter) => {
198
- parameters[parameter.name] = evaluateValue(this, parameter.value);
199
- });
200
-
201
- if (isOpencorSimulation) {
202
- request.opencor.json_config.parameters = parameters;
203
- } else {
204
- request.osparc.job_inputs = parameters;
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
+ };
205
514
  }
206
- }
207
515
 
208
- // Specify what we want to retrieve, if anything.
516
+ request.opencor.json_config.parameters = this.parametersData();
209
517
 
210
- if (isOpencorSimulation && (this.simulationUiInfo.output.data !== undefined)) {
211
- let index = -1;
518
+ const output = this.outputData();
212
519
 
213
- request.opencor.json_config.output = [];
520
+ if (output !== undefined) {
521
+ request.opencor.json_config.output = output;
522
+ }
523
+ } else {
524
+ request.osparc = {};
214
525
 
215
- this.simulationUiInfo.output.data.forEach((outputData) => {
216
- request.opencor.json_config.output[++index] = outputData.name;
217
- });
526
+ request.osparc.job_inputs = this.parametersData();
218
527
  }
219
528
 
220
529
  return request;
221
530
  },
531
+ /**
532
+ * @vuese
533
+ * Process the simulation results retrieved by `checkSimulation`. The simulation results are post-processed, if
534
+ * needed, and then readied for use by `PlotVuer`.
535
+ * @arg `results`
536
+ */
222
537
  processSimulationResults(results) {
223
538
  // Convert, if needed, the results to a JSON format that is compatible
224
539
  // with our OpenCOR results.
225
540
 
226
541
  if (typeof(results) === "string") {
227
542
  const SPACES = /[ \t]+/g;
228
-
229
- let lines = results.trim().split("\n");
230
- let iMax = lines[0].trim().split(SPACES).length;
543
+ const lines = results.trim().split("\n");
544
+ const iMax = lines[0].trim().split(SPACES).length;
231
545
 
232
546
  results = {};
233
547
 
@@ -241,7 +555,7 @@ export default {
241
555
  ++i;
242
556
 
243
557
  let j = -1;
244
- let values = line.trim().split(SPACES);
558
+ const values = line.trim().split(SPACES);
245
559
 
246
560
  values.forEach((value) => {
247
561
  results[++j][i] = Number(value);
@@ -251,33 +565,37 @@ export default {
251
565
 
252
566
  // Get the results ready for plotting.
253
567
 
254
- let index = -1;
255
- let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
568
+ const parser = new math.parser();
256
569
 
257
- this.simulationUiInfo.output.plots.forEach((outputPlot) => {
258
- let xValue = [];
259
- let yValue = [];
570
+ Object.keys(this.simulationResultsId).forEach((id) => {
571
+ parser.set(id, results[this.simulationResultsId[id]]);
572
+ });
260
573
 
261
- for (let i = 0; i < iMax; ++i) {
262
- xValue[i] = evaluateSimulationValue(this, results, outputPlot.xValue, i);
263
- yValue[i] = evaluateSimulationValue(this, results, outputPlot.yValue, i);
264
- }
574
+ let index = -1;
265
575
 
266
- this.simulationData[++index] = [
576
+ this.simulationUiInfo.output.plots.forEach((outputPlot) => {
577
+ this.simulationResults[++index] = [
267
578
  {
268
- x: xValue,
269
- y: yValue,
579
+ x: parser.evaluate(outputPlot.xValue),
580
+ y: parser.evaluate(outputPlot.yValue),
270
581
  type: "scatter",
271
582
  },
272
583
  ];
273
584
  });
274
585
  },
586
+ /**
587
+ * @vuese
588
+ * Check the progress of the simulation using the given `data`, a JSON object that contains the simulation job ID,
589
+ * as well as the solver name and version. This method is first called by `startSimulation` and then every second by
590
+ * itself until the simulation is finished.
591
+ * @arg `data`
592
+ */
275
593
  checkSimulation(data) {
276
594
  // Check the simulation.
277
595
 
278
- let xmlhttp = new XMLHttpRequest();
596
+ const xmlhttp = new XMLHttpRequest();
279
597
 
280
- xmlhttp.open("POST", this.apiLocation + "/check_simulation", true);
598
+ xmlhttp.open("POST", this.apiLocation + "/check_simulation");
281
599
  xmlhttp.setRequestHeader("Content-type", "application/json");
282
600
  xmlhttp.onreadystatechange = () => {
283
601
  if (xmlhttp.readyState === 4) {
@@ -316,37 +634,24 @@ export default {
316
634
  };
317
635
  xmlhttp.send(JSON.stringify(data));
318
636
  },
637
+ /**
638
+ * @vuese
639
+ * Start the simulation associated with the simulation-based dataset. The simulation UI has a `Run Simulation`
640
+ * button which, when clicked, calls this method.
641
+ */
319
642
  startSimulation() {
320
- // Retrieve the solver to be used for the simulation.
321
-
322
- let solver = undefined;
323
-
324
- this.simulationUiInfo.simulation.solvers.forEach((crtSolver) => {
325
- if ((crtSolver.if === undefined) || evaluateValue(this, crtSolver.if)) {
326
- solver = crtSolver;
327
- }
328
- });
329
-
330
- if (solver === undefined) {
331
- console.warn("SIMULATION: no solver name and/or solver version specified.");
332
-
333
- return;
334
- }
335
-
336
643
  // Start the simulation (after resetting our previous simulation data, in
337
644
  // case there were sonme).
338
- // Note: we use this.$nextTick() so that the user message is shown before
339
- // we get to post our HTTP request.
340
645
 
341
646
  this.userMessage = "Loading simulation results...";
342
647
  this.showUserMessage = true;
343
648
 
344
649
  this.$nextTick(() => {
345
- this.simulationData = [];
650
+ this.simulationResults = {};
346
651
 
347
- let xmlhttp = new XMLHttpRequest();
652
+ const xmlhttp = new XMLHttpRequest();
348
653
 
349
- xmlhttp.open("POST", this.apiLocation + "/start_simulation", true);
654
+ xmlhttp.open("POST", this.apiLocation + "/start_simulation");
350
655
  xmlhttp.setRequestHeader("Content-type", "application/json");
351
656
  xmlhttp.onreadystatechange = () => {
352
657
  if (xmlhttp.readyState === 4) {
@@ -368,39 +673,71 @@ export default {
368
673
  }
369
674
  }
370
675
  };
371
- xmlhttp.send(JSON.stringify(this.retrieveRequest({
372
- solver: solver
373
- })));
676
+ xmlhttp.send(JSON.stringify(this.retrieveRequest()));
374
677
  });
375
678
  },
376
679
  },
377
680
  created: function() {
378
- // Try to retrieve the UI information, but only if we have a name.
681
+ // Try to retrieve the UI information.
379
682
 
380
- if (this.name !== undefined) {
683
+ if (this.id > 0) {
381
684
  this.userMessage = "Retrieving UI information...";
382
685
  this.showUserMessage = true;
383
686
 
384
687
  // Retrieve and build the simulation UI.
385
- // Note: we use this.$nextTick() so that the user message is shown before
386
- // we get to post our HTTP request.
387
688
 
388
689
  this.$nextTick(() => {
389
- let xmlhttp = new XMLHttpRequest();
690
+ const xmlhttp = new XMLHttpRequest();
390
691
 
391
- xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id, true);
392
- xmlhttp.setRequestHeader("Content-type", "application/json");
692
+ xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id);
393
693
  xmlhttp.onreadystatechange = () => {
394
694
  if (xmlhttp.readyState === 4) {
395
695
  this.showUserMessage = false;
396
696
 
397
697
  if (xmlhttp.status === 200) {
398
- this.retrieveAndBuildSimulationUi(JSON.parse(xmlhttp.responseText));
698
+ this.$nextTick(() => {
699
+ this.buildSimulationUi(JSON.parse(xmlhttp.responseText));
700
+ });
399
701
  }
400
702
  }
401
703
  };
402
704
  xmlhttp.send();
403
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
+ });
404
741
  }
405
742
  },
406
743
  mounted: function() {