@abi-software/simulationvuer 0.6.5 → 0.7.0-vue-3-alpha.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.
@@ -1,530 +1,535 @@
1
- <template>
2
- <div class="simulation-vuer" v-loading="showUserMessage" :element-loading-text="userMessage">
3
- <p v-if="!hasValidSimulationUiInfo && !showUserMessage" class="default error"><span class="error">Error:</span> an unknown or invalid model was provided.</p>
4
- <div class="main" v-if="hasValidSimulationUiInfo">
5
- <div class="main-left">
6
- <p class="default name">{{name}}</p>
7
- <el-divider></el-divider>
8
- <p class="default input-parameters">Input parameters</p>
9
- <div class="input scrollbar">
10
- <SimulationVuerInput v-for="(input, index) in simulationUiInfo.input" :defaultValue="input.defaultValue" :key="`input-${index}`" :name="input.name" :maximumValue="input.maximumValue" :minimumValue="input.minimumValue" :possibleValues="input.possibleValues" :stepValue="input.stepValue" />
11
- </div>
12
- <div class="primary-button">
13
- <el-button type="primary" size="mini" @click="startSimulation()">Run Simulation</el-button>
14
- </div>
15
- <div class="secondary-button" v-if="uuid">
16
- <el-button size="mini" @click="runOnOsparc()">Run on oSPARC</el-button>
17
- </div>
18
- <div class="secondary-button">
19
- <el-button size="mini" @click="viewDataset()">View Dataset</el-button>
20
- </div>
21
- <p class="default note" v-if="uuid">Additional parameters are available on oSPARC</p>
22
- </div>
23
- <div class="main-right" ref="output" v-show="isSimulationValid">
24
- <PlotVuer v-for="(outputPlot, index) in simulationUiInfo.output.plots" :key="`output-${index}`" :layout-input="layout[index]" :dataInput="simulationData[index]" :plotType="'plotly-only'" />
25
- </div>
26
- <div class="main-right" v-show="!isSimulationValid">
27
- <p class="default error"><span class="error">Error:</span> <span v-html="errorMessage"></span>.</p>
28
- </div>
29
- </div>
30
- </div>
31
- </template>
32
-
33
- <script>
34
- import Vue from "vue";
35
- import { PlotVuer } from "@abi-software/plotvuer";
36
- import "@abi-software/plotvuer/dist/plotvuer.css";
37
- import SimulationVuerInput from "./SimulationVuerInput.vue";
38
- import { Button, Divider, Loading } from "element-ui";
39
- import { evaluateValue, evaluateSimulationValue, OPENCOR_SOLVER_NAME } from "./common.js";
40
- import { validJson } from "./json.js";
41
- import { initialiseUi, finaliseUi } from "./ui.js";
42
-
43
- Vue.use(Button);
44
- Vue.use(Divider);
45
- Vue.use(Loading.directive);
46
-
47
- export default {
48
- name: "SimulationVuer",
49
- components: {
50
- PlotVuer,
51
- SimulationVuerInput,
52
- },
53
- props: {
54
- apiLocation: {
55
- required: true,
56
- type: String,
57
- },
58
- id: {
59
- required: true,
60
- type: Number,
61
- },
62
- },
63
- data: function() {
64
- let xmlhttp = new XMLHttpRequest();
65
- let name = undefined;
66
- let uuid = undefined;
67
-
68
- xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id, false);
69
- xmlhttp.setRequestHeader("Content-type", "application/json");
70
- xmlhttp.onreadystatechange = () => {
71
- if (xmlhttp.readyState === 4) {
72
- if (xmlhttp.status === 200) {
73
- let datasetInfo = JSON.parse(xmlhttp.responseText);
74
-
75
- name = datasetInfo.name;
76
- uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
77
- }
78
- }
79
- };
80
- xmlhttp.send();
81
-
82
- return {
83
- errorMessage: "",
84
- hasFinalisedUi: false,
85
- hasValidSimulationUiInfo: false,
86
- isMounted: false,
87
- isSimulationValid: true,
88
- layout: [],
89
- name: name,
90
- perfectScollbarOptions: {
91
- suppressScrollX: true,
92
- },
93
- showUserMessage: false,
94
- simulationData: [],
95
- simulationDataId: {},
96
- simulationUiInfo: {},
97
- userMessage: "",
98
- ui: null,
99
- uuid: uuid,
100
- };
101
- },
102
- methods: {
103
- retrieveAndBuildSimulationUi(simulationUiInfo) {
104
- // Keep track of the simulation UI information.
105
-
106
- this.simulationUiInfo = simulationUiInfo;
107
-
108
- // Make sure that the simulation UI information is valid.
109
-
110
- this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
111
-
112
- if (!this.hasValidSimulationUiInfo) {
113
- return;
114
- }
115
-
116
- // Initialise our UI.
117
-
118
- initialiseUi(this);
119
-
120
- // Finalise our UI.
121
- // Note: we try both here and in the mounted() function since we have no
122
- // idea how long it's going to take to retrieve the simulation UI
123
- // information.
124
-
125
- this.$nextTick(() => {
126
- finaliseUi(this);
127
- });
128
- },
129
- runOnOsparc() {
130
- window.open(`https://osparc.io/study/${this.uuid}`, "_blank");
131
- },
132
- viewDataset() {
133
- window.open(`https://sparc.science/datasets/${this.id}?type=dataset`, "_blank");
134
- },
135
- retrieveRequest(request) {
136
- // Settings specific to OpenCOR/oSPARC.
137
-
138
- let isOpencorSimulation = request.solver.name === OPENCOR_SOLVER_NAME;
139
-
140
- if (isOpencorSimulation) {
141
- request.opencor = {
142
- model_url: this.simulationUiInfo.simulation.opencor.resource,
143
- json_config: {},
144
- };
145
- } else {
146
- request.osparc = {};
147
- }
148
-
149
- // Specify the ending point and point interval, if we have some.
150
-
151
- if ( isOpencorSimulation
152
- && (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
153
- && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
154
- request.opencor.json_config.simulation = {
155
- "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
156
- "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
157
- };
158
- }
159
-
160
- // Specify the parameters, if any.
161
-
162
- if (this.simulationUiInfo.parameters !== undefined) {
163
- let parameters = {};
164
-
165
- this.simulationUiInfo.parameters.forEach((parameter) => {
166
- parameters[parameter.name] = evaluateValue(this, parameter.value);
167
- });
168
-
169
- if (isOpencorSimulation) {
170
- request.opencor.json_config.parameters = parameters;
171
- } else {
172
- request.osparc.job_inputs = parameters;
173
- }
174
- }
175
-
176
- // Specify what we want to retrieve, if anything.
177
-
178
- if (isOpencorSimulation && (this.simulationUiInfo.output.data !== undefined)) {
179
- let index = -1;
180
-
181
- request.opencor.json_config.output = [];
182
-
183
- this.simulationUiInfo.output.data.forEach((outputData) => {
184
- request.opencor.json_config.output[++index] = outputData.name;
185
- });
186
- }
187
-
188
- return request;
189
- },
190
- processSimulationResults(results) {
191
- // Convert, if needed, the results to a JSON format that is compatible
192
- // with our OpenCOR results.
193
-
194
- if (typeof(results) === "string") {
195
- const SPACES = /[ \t]+/g;
196
-
197
- let lines = results.trim().split("\n");
198
- let iMax = lines[0].trim().split(SPACES).length;
199
-
200
- results = {};
201
-
202
- for (let i = 0; i < iMax; ++i) {
203
- results[i] = [];
204
- }
205
-
206
- let i = -1;
207
-
208
- lines.forEach((line) => {
209
- ++i;
210
-
211
- let j = -1;
212
- let values = line.trim().split(SPACES);
213
-
214
- values.forEach((value) => {
215
- results[++j][i] = Number(value);
216
- });
217
- });
218
- }
219
-
220
- // Get the results ready for plotting.
221
-
222
- let index = -1;
223
- let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
224
-
225
- this.simulationUiInfo.output.plots.forEach((outputPlot) => {
226
- let xValue = [];
227
- let yValue = [];
228
-
229
- for (let i = 0; i < iMax; ++i) {
230
- xValue[i] = evaluateSimulationValue(this, results, outputPlot.xValue, i);
231
- yValue[i] = evaluateSimulationValue(this, results, outputPlot.yValue, i);
232
- }
233
-
234
- this.simulationData[++index] = [
235
- {
236
- x: xValue,
237
- y: yValue,
238
- },
239
- ];
240
- });
241
- },
242
- checkSimulation(data) {
243
- // Check the simulation.
244
-
245
- let xmlhttp = new XMLHttpRequest();
246
-
247
- xmlhttp.open("POST", this.apiLocation + "/check_simulation", true);
248
- xmlhttp.setRequestHeader("Content-type", "application/json");
249
- xmlhttp.onreadystatechange = () => {
250
- if (xmlhttp.readyState === 4) {
251
- if (xmlhttp.status === 200) {
252
- let response = JSON.parse(xmlhttp.responseText);
253
-
254
- this.isSimulationValid = response.status === "ok";
255
-
256
- if (this.isSimulationValid) {
257
- if (response.results !== undefined) {
258
- // The simulation is finished, so process its results.
259
-
260
- this.showUserMessage = false;
261
-
262
- this.processSimulationResults(response.results);
263
- } else {
264
- // The simulation is not yet finished, so check again in a
265
- // second.
266
-
267
- let that = this;
268
-
269
- setTimeout(function() {
270
- that.checkSimulation(data);
271
- }, 1000);
272
- }
273
- } else {
274
- this.showUserMessage = false;
275
- this.errorMessage = response.description;
276
- }
277
- } else {
278
- this.isSimulationValid = false;
279
- this.showUserMessage = false;
280
- this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
281
- }
282
- }
283
- };
284
- xmlhttp.send(JSON.stringify(data));
285
- },
286
- startSimulation() {
287
- // Retrieve the solver to be used for the simulation.
288
-
289
- let solver = undefined;
290
-
291
- this.simulationUiInfo.simulation.solvers.forEach((crtSolver) => {
292
- if ((crtSolver.if === undefined) || evaluateValue(this, crtSolver.if)) {
293
- solver = crtSolver;
294
- }
295
- });
296
-
297
- if (solver === undefined) {
298
- console.warn("SIMULATION: no solver name and/or solver version specified.");
299
-
300
- return;
301
- }
302
-
303
- // Start the simulation (after resetting our previous simulation data, in
304
- // case there were sonme).
305
- // Note: we use this.$nextTick() so that the user message is shown before
306
- // we get to post our HTTP request.
307
-
308
- this.userMessage = "Loading simulation results...";
309
- this.showUserMessage = true;
310
-
311
- this.$nextTick(() => {
312
- this.simulationData = [];
313
-
314
- let xmlhttp = new XMLHttpRequest();
315
-
316
- xmlhttp.open("POST", this.apiLocation + "/start_simulation", true);
317
- xmlhttp.setRequestHeader("Content-type", "application/json");
318
- xmlhttp.onreadystatechange = () => {
319
- if (xmlhttp.readyState === 4) {
320
- if (xmlhttp.status === 200) {
321
- let response = JSON.parse(xmlhttp.responseText);
322
-
323
- this.isSimulationValid = response.status === "ok";
324
-
325
- if (this.isSimulationValid) {
326
- this.checkSimulation(response.data);
327
- } else {
328
- this.showUserMessage = false;
329
- this.errorMessage = response.description;
330
- }
331
- } else {
332
- this.isSimulationValid = false;
333
- this.showUserMessage = false;
334
- this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
335
- }
336
- }
337
- };
338
- xmlhttp.send(JSON.stringify(this.retrieveRequest({
339
- solver: solver
340
- })));
341
- });
342
- },
343
- },
344
- created: function() {
345
- // Try to retrieve the UI information, but only if we have a name.
346
-
347
- if (this.name !== undefined) {
348
- this.userMessage = "Retrieving UI information...";
349
- this.showUserMessage = true;
350
-
351
- // Retrieve and build the simulation UI.
352
- // Note: we use this.$nextTick() so that the user message is shown before
353
- // we get to post our HTTP request.
354
-
355
- this.$nextTick(() => {
356
- let xmlhttp = new XMLHttpRequest();
357
-
358
- xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id, true);
359
- xmlhttp.setRequestHeader("Content-type", "application/json");
360
- xmlhttp.onreadystatechange = () => {
361
- if (xmlhttp.readyState === 4) {
362
- this.showUserMessage = false;
363
-
364
- if (xmlhttp.status === 200) {
365
- this.retrieveAndBuildSimulationUi(JSON.parse(xmlhttp.responseText));
366
- }
367
- }
368
- };
369
- xmlhttp.send();
370
- });
371
- }
372
- },
373
- mounted: function() {
374
- // Finalise our UI.
375
- // Note: we try both here and in the created() function since we have no
376
- // idea how long it's going to take to retrieve the simulation UI
377
- // information.
378
-
379
- this.isMounted = true;
380
-
381
- finaliseUi(this);
382
- },
383
- };
384
- </script>
385
-
386
- <!-- Add "scoped" attribute to limit CSS to this component only -->
387
- <style scoped lang="scss">
388
- @import "~element-ui/packages/theme-chalk/src/button";
389
- @import "~element-ui/packages/theme-chalk/src/divider";
390
- @import "~element-ui/packages/theme-chalk/src/loading";
391
-
392
- ::v-deep .el-button:hover {
393
- box-shadow: -3px 2px 4px #00000040;
394
- }
395
- ::v-deep .el-divider {
396
- margin: -8px 0 8px 0 !important;
397
- width: 210px;
398
- }
399
- ::v-deep .el-loading-spinner {
400
- .path {
401
- stroke: $app-primary-color;
402
- }
403
- i, .el-loading-text {
404
- color: $app-primary-color;
405
- }
406
- }
407
- div.input {
408
- border: 1px solid #dcdfe6;
409
- padding: 4px;
410
- height: 300px;
411
- }
412
- div.main {
413
- display: grid;
414
- --mainLeftWidth: 243px;
415
- grid-template-columns: var(--mainLeftWidth) calc(100% - var(--mainLeftWidth));
416
- height: 100%;
417
- }
418
- div.main-left {
419
- border-right: 1px solid #dcdfe6;
420
- padding: 12px 20px 12px 12px;
421
- height: 100%;
422
- overflow: auto;
423
- }
424
- div.main-right.x1 {
425
- height: 100%;
426
- }
427
- div.main-right.x2 {
428
- height: 50%;
429
- }
430
- div.main-right.x3 {
431
- height: 33.333%;
432
- }
433
- div.main-right.x4 {
434
- height: 25%;
435
- }
436
- div.main-right.x5 {
437
- height: 20%;
438
- }
439
- div.main-right.x6 {
440
- height: 16.667%;
441
- }
442
- div.main-right.x7 {
443
- height: 14.286%;
444
- }
445
- div.main-right.x8 {
446
- height: 12.5%;
447
- }
448
- div.main-right.x9 {
449
- height: 11.111%;
450
- }
451
- ::v-deep div.main-right div.controls {
452
- height: 0;
453
- }
454
- div.primary-button,
455
- div.secondary-button {
456
- display: flex;
457
- justify-content: flex-end;
458
- width: 210px;
459
- }
460
- div.primary-button {
461
- margin-top: 14px;
462
- }
463
- div.secondary-button {
464
- margin-top: 8px;
465
- }
466
- div.primary-button .el-button,
467
- div.secondary-button .el-button,
468
- div.primary-button .el-button:hover,
469
- div.secondary-button .el-button:hover {
470
- width: 121px;
471
- border-color: #8300bf;
472
- }
473
- div.primary-button .el-button,
474
- div.primary-button .el-button:hover {
475
- background-color: #8300bf;
476
- }
477
- div.secondary-button .el-button,
478
- div.secondary-button .el-button:hover {
479
- background-color: #f9f2fc;
480
- color: #8300bf;
481
- }
482
- div.scrollbar {
483
- overflow-y: scroll;
484
- scrollbar-width: thin;
485
- }
486
- div.scrollbar::-webkit-scrollbar {
487
- width: 8px;
488
- right: -8px;
489
- background-color: #f5f5f5;
490
- }
491
- div.scrollbar::-webkit-scrollbar-thumb {
492
- border-radius: 4px;
493
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
494
- background-color: #979797;
495
- }
496
- div.scrollbar::-webkit-scrollbar-track {
497
- border-radius: 10px;
498
- background-color: #f5f5f5;
499
- }
500
- div.simulation-vuer {
501
- height: 100%;
502
- }
503
- p.default {
504
- font-family: Asap, sans-serif;
505
- letter-spacing: 0;
506
- margin: 16px 0;
507
- text-align: start;
508
- }
509
- p.error {
510
- margin-left: 16px;
511
- }
512
- p.input-parameters {
513
- margin-bottom: 8px;
514
- }
515
- p.name,
516
- p.input-parameters {
517
- margin-top: 0;
518
- font-weight: 500 /* Medium */;
519
- }
520
- p.name {
521
- line-height: 20px;
522
- }
523
- p.note {
524
- font-size: 12px;
525
- line-height: 16px;
526
- }
527
- span.error {
528
- font-weight: 500 /* Medium */;
529
- }
530
- </style>
1
+ <template>
2
+ <div class="simulation-vuer" v-loading="showUserMessage" :element-loading-text="userMessage">
3
+ <p v-if="!hasValidSimulationUiInfo && !showUserMessage" class="default error"><span class="error">Error:</span> an unknown or invalid model was provided.</p>
4
+ <div class="main" v-if="hasValidSimulationUiInfo">
5
+ <div class="main-left">
6
+ <p class="default name">{{name}}</p>
7
+ <el-divider></el-divider>
8
+ <p class="default input-parameters">Input parameters</p>
9
+ <div class="input scrollbar">
10
+ <SimulationVuerInput v-for="(input, index) in simulationUiInfo.input" ref="simInput" :defaultValue="input.defaultValue" :key="`input-${index}`" :name="input.name" :maximumValue="input.maximumValue" :minimumValue="input.minimumValue" :possibleValues="input.possibleValues" :stepValue="input.stepValue" />
11
+ </div>
12
+ <div class="primary-button">
13
+ <el-button type="primary" size="small" @click="startSimulation()">Run Simulation</el-button>
14
+ </div>
15
+ <div class="secondary-button" v-if="uuid">
16
+ <el-button size="small" @click="runOnOsparc()">Run on oSPARC</el-button>
17
+ </div>
18
+ <div class="secondary-button">
19
+ <el-button size="small" @click="viewDataset()">View Dataset</el-button>
20
+ </div>
21
+ <p class="default note" v-if="uuid">Additional parameters are available on oSPARC</p>
22
+ </div>
23
+ <div class="main-right" ref="output" v-show="isSimulationValid">
24
+ <PlotVuer v-for="(outputPlot, index) in simulationUiInfo.output.plots" :key="`output-${index}`" :metadata="plotMetadata" :layout-input="layout[index]" :data-source="{data: simulationData[index]}" :plotType="'plotly-only'" />
25
+ </div>
26
+ <div class="main-right" v-show="!isSimulationValid">
27
+ <p class="default error"><span class="error">Error:</span> <span v-html="errorMessage"></span>.</p>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script>
34
+ import { PlotVuer } from "@abi-software/plotvuer";
35
+ // import "@abi-software/plotvuer/dist/plotvuer.css";
36
+ import SimulationVuerInput from "./SimulationVuerInput.vue";
37
+ // import { Button, Divider, Loading } from "element-ui";
38
+ import { ElButton, ElDivider, ElLoading } from "element-plus";
39
+ import { evaluateValue, evaluateSimulationValue, OPENCOR_SOLVER_NAME } from "./common.js";
40
+ import { validJson } from "./json.js";
41
+ import { initialiseUi, finaliseUi } from "./ui.js";
42
+
43
+
44
+ export default {
45
+ name: "SimulationVuer",
46
+ components: {
47
+ PlotVuer,
48
+ SimulationVuerInput,
49
+ ElButton,
50
+ ElDivider,
51
+ ElLoading,
52
+ },
53
+ props: {
54
+ apiLocation: {
55
+ required: true,
56
+ type: String,
57
+ },
58
+ id: {
59
+ required: true,
60
+ type: Number,
61
+ },
62
+ },
63
+ data: function() {
64
+ let xmlhttp = new XMLHttpRequest();
65
+ let name = undefined;
66
+ let uuid = undefined;
67
+ xmlhttp.open("GET", this.apiLocation + "/sim/dataset/" + this.id, false);
68
+ xmlhttp.setRequestHeader("Content-type", "application/json");
69
+ xmlhttp.onreadystatechange = () => {
70
+ if (xmlhttp.readyState === 4) {
71
+ if (xmlhttp.status === 200) {
72
+ let datasetInfo = JSON.parse(xmlhttp.responseText);
73
+
74
+ name = datasetInfo.name;
75
+ uuid = (datasetInfo.study !== undefined)?datasetInfo.study.uuid:undefined;
76
+ }
77
+ }
78
+ };
79
+ xmlhttp.send();
80
+
81
+ return {
82
+ plotMetadata: {
83
+ version: "1.1.0",
84
+ type: "plot",
85
+ attrs: {
86
+ style: "timeseries"
87
+ }
88
+ },
89
+ errorMessage: "",
90
+ hasFinalisedUi: false,
91
+ hasValidSimulationUiInfo: false,
92
+ isMounted: false,
93
+ isSimulationValid: true,
94
+ layout: [],
95
+ name: name,
96
+ perfectScollbarOptions: {
97
+ suppressScrollX: true,
98
+ },
99
+ showUserMessage: false,
100
+ simulationData: [],
101
+ simulationDataId: {},
102
+ simulationUiInfo: {},
103
+ userMessage: "",
104
+ ui: null,
105
+ uuid: uuid,
106
+ };
107
+ },
108
+ methods: {
109
+ retrieveAndBuildSimulationUi(simulationUiInfo) {
110
+ // Keep track of the simulation UI information.
111
+
112
+ this.simulationUiInfo = simulationUiInfo;
113
+
114
+ // Make sure that the simulation UI information is valid.
115
+
116
+ this.hasValidSimulationUiInfo = validJson(this.simulationUiInfo);
117
+
118
+ if (!this.hasValidSimulationUiInfo) {
119
+ return;
120
+ }
121
+
122
+ // Initialise our UI.
123
+
124
+ initialiseUi(this);
125
+
126
+ // Finalise our UI.
127
+ // Note: we try both here and in the mounted() function since we have no
128
+ // idea how long it's going to take to retrieve the simulation UI
129
+ // information.
130
+
131
+ this.$nextTick(() => {
132
+ finaliseUi(this);
133
+ });
134
+ },
135
+ runOnOsparc() {
136
+ window.open(`https://osparc.io/study/${this.uuid}`, "_blank");
137
+ },
138
+ viewDataset() {
139
+ window.open(`https://sparc.science/datasets/${this.id}?type=dataset`, "_blank");
140
+ },
141
+ retrieveRequest(request) {
142
+ // Settings specific to OpenCOR/oSPARC.
143
+
144
+ let isOpencorSimulation = request.solver.name === OPENCOR_SOLVER_NAME;
145
+
146
+ if (isOpencorSimulation) {
147
+ request.opencor = {
148
+ model_url: this.simulationUiInfo.simulation.opencor.resource,
149
+ json_config: {},
150
+ };
151
+ } else {
152
+ request.osparc = {};
153
+ }
154
+
155
+ // Specify the ending point and point interval, if we have some.
156
+
157
+ if ( isOpencorSimulation
158
+ && (this.simulationUiInfo.simulation.opencor.endingPoint !== undefined)
159
+ && (this.simulationUiInfo.simulation.opencor.pointInterval !== undefined)) {
160
+ request.opencor.json_config.simulation = {
161
+ "Ending point": this.simulationUiInfo.simulation.opencor.endingPoint,
162
+ "Point interval": this.simulationUiInfo.simulation.opencor.pointInterval,
163
+ };
164
+ }
165
+
166
+ // Specify the parameters, if any.
167
+
168
+ if (this.simulationUiInfo.parameters !== undefined) {
169
+ let parameters = {};
170
+
171
+ this.simulationUiInfo.parameters.forEach((parameter) => {
172
+ parameters[parameter.name] = evaluateValue(this, parameter.value);
173
+ });
174
+
175
+ if (isOpencorSimulation) {
176
+ request.opencor.json_config.parameters = parameters;
177
+ } else {
178
+ request.osparc.job_inputs = parameters;
179
+ }
180
+ }
181
+
182
+ // Specify what we want to retrieve, if anything.
183
+
184
+ if (isOpencorSimulation && (this.simulationUiInfo.output.data !== undefined)) {
185
+ let index = -1;
186
+
187
+ request.opencor.json_config.output = [];
188
+
189
+ this.simulationUiInfo.output.data.forEach((outputData) => {
190
+ request.opencor.json_config.output[++index] = outputData.name;
191
+ });
192
+ }
193
+
194
+ return request;
195
+ },
196
+ processSimulationResults(results) {
197
+ // Convert, if needed, the results to a JSON format that is compatible
198
+ // with our OpenCOR results.
199
+
200
+ if (typeof(results) === "string") {
201
+ const SPACES = /[ \t]+/g;
202
+
203
+ let lines = results.trim().split("\n");
204
+ let iMax = lines[0].trim().split(SPACES).length;
205
+
206
+ results = {};
207
+
208
+ for (let i = 0; i < iMax; ++i) {
209
+ results[i] = [];
210
+ }
211
+
212
+ let i = -1;
213
+
214
+ lines.forEach((line) => {
215
+ ++i;
216
+
217
+ let j = -1;
218
+ let values = line.trim().split(SPACES);
219
+
220
+ values.forEach((value) => {
221
+ results[++j][i] = Number(value);
222
+ });
223
+ });
224
+ }
225
+
226
+ // Get the results ready for plotting.
227
+
228
+ let index = -1;
229
+ let iMax = results[this.simulationDataId[Object.keys(this.simulationDataId)[0]]].length;
230
+
231
+ this.simulationUiInfo.output.plots.forEach((outputPlot) => {
232
+ let xValue = [];
233
+ let yValue = [];
234
+
235
+ for (let i = 0; i < iMax; ++i) {
236
+ xValue[i] = evaluateSimulationValue(this, results, outputPlot.xValue, i);
237
+ yValue[i] = evaluateSimulationValue(this, results, outputPlot.yValue, i);
238
+ }
239
+
240
+ this.simulationData[++index] = [
241
+ {
242
+ x: xValue,
243
+ y: yValue,
244
+ type: "scatter",
245
+ },
246
+ ];
247
+ });
248
+ },
249
+ checkSimulation(data) {
250
+ // Check the simulation.
251
+
252
+ let xmlhttp = new XMLHttpRequest();
253
+
254
+ xmlhttp.open("POST", this.apiLocation + "/check_simulation", true);
255
+ xmlhttp.setRequestHeader("Content-type", "application/json");
256
+ xmlhttp.onreadystatechange = () => {
257
+ if (xmlhttp.readyState === 4) {
258
+ if (xmlhttp.status === 200) {
259
+ let response = JSON.parse(xmlhttp.responseText);
260
+
261
+ this.isSimulationValid = response.status === "ok";
262
+
263
+ if (this.isSimulationValid) {
264
+ if (response.results !== undefined) {
265
+ // The simulation is finished, so process its results.
266
+
267
+ this.showUserMessage = false;
268
+
269
+ this.processSimulationResults(response.results);
270
+ } else {
271
+ // The simulation is not yet finished, so check again in a
272
+ // second.
273
+
274
+ let that = this;
275
+
276
+ setTimeout(function() {
277
+ that.checkSimulation(data);
278
+ }, 1000);
279
+ }
280
+ } else {
281
+ this.showUserMessage = false;
282
+ this.errorMessage = response.description;
283
+ }
284
+ } else {
285
+ this.isSimulationValid = false;
286
+ this.showUserMessage = false;
287
+ this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
288
+ }
289
+ }
290
+ };
291
+ xmlhttp.send(JSON.stringify(data));
292
+ },
293
+ startSimulation() {
294
+ // Retrieve the solver to be used for the simulation.
295
+
296
+ let solver = undefined;
297
+
298
+ this.simulationUiInfo.simulation.solvers.forEach((crtSolver) => {
299
+ if ((crtSolver.if === undefined) || evaluateValue(this, crtSolver.if)) {
300
+ solver = crtSolver;
301
+ }
302
+ });
303
+
304
+ if (solver === undefined) {
305
+ console.warn("SIMULATION: no solver name and/or solver version specified.");
306
+
307
+ return;
308
+ }
309
+
310
+ // Start the simulation (after resetting our previous simulation data, in
311
+ // case there were sonme).
312
+ // Note: we use this.$nextTick() so that the user message is shown before
313
+ // we get to post our HTTP request.
314
+
315
+ this.userMessage = "Loading simulation results...";
316
+ this.showUserMessage = true;
317
+
318
+ this.$nextTick(() => {
319
+ this.simulationData = [];
320
+
321
+ let xmlhttp = new XMLHttpRequest();
322
+
323
+ xmlhttp.open("POST", this.apiLocation + "/start_simulation", true);
324
+ xmlhttp.setRequestHeader("Content-type", "application/json");
325
+ xmlhttp.onreadystatechange = () => {
326
+ if (xmlhttp.readyState === 4) {
327
+ if (xmlhttp.status === 200) {
328
+ let response = JSON.parse(xmlhttp.responseText);
329
+
330
+ this.isSimulationValid = response.status === "ok";
331
+
332
+ if (this.isSimulationValid) {
333
+ this.checkSimulation(response.data);
334
+ } else {
335
+ this.showUserMessage = false;
336
+ this.errorMessage = response.description;
337
+ }
338
+ } else {
339
+ this.isSimulationValid = false;
340
+ this.showUserMessage = false;
341
+ this.errorMessage = xmlhttp.statusText.toLowerCase() + " (<a href='https://httpstatuses.com/" + xmlhttp.status + "/' target='_blank'>" + xmlhttp.status + "</a>)";
342
+ }
343
+ }
344
+ };
345
+ xmlhttp.send(JSON.stringify(this.retrieveRequest({
346
+ solver: solver
347
+ })));
348
+ });
349
+ },
350
+ },
351
+ created: function() {
352
+ // Try to retrieve the UI information, but only if we have a name.
353
+
354
+ if (this.name !== undefined) {
355
+ this.userMessage = "Retrieving UI information...";
356
+ this.showUserMessage = true;
357
+
358
+ // Retrieve and build the simulation UI.
359
+ // Note: we use this.$nextTick() so that the user message is shown before
360
+ // we get to post our HTTP request.
361
+
362
+ this.$nextTick(() => {
363
+ let xmlhttp = new XMLHttpRequest();
364
+
365
+ xmlhttp.open("GET", this.apiLocation + "/simulation_ui_file/" + this.id, true);
366
+ xmlhttp.setRequestHeader("Content-type", "application/json");
367
+ xmlhttp.onreadystatechange = () => {
368
+ if (xmlhttp.readyState === 4) {
369
+ this.showUserMessage = false;
370
+
371
+ if (xmlhttp.status === 200) {
372
+ this.retrieveAndBuildSimulationUi(JSON.parse(xmlhttp.responseText));
373
+ }
374
+ }
375
+ };
376
+ xmlhttp.send();
377
+ });
378
+ }
379
+ },
380
+ mounted: function() {
381
+ // Finalise our UI.
382
+ // Note: we try both here and in the created() function since we have no
383
+ // idea how long it's going to take to retrieve the simulation UI
384
+ // information.
385
+
386
+ this.isMounted = true;
387
+
388
+ finaliseUi(this);
389
+ },
390
+ };
391
+ </script>
392
+
393
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
394
+ <style scoped lang="scss">
395
+
396
+
397
+ :deep( .el-button:hover) {
398
+ box-shadow: -3px 2px 4px #00000040;
399
+ }
400
+ :deep( .el-divider) {
401
+ margin: -8px 0 8px 0 !important;
402
+ width: 210px;
403
+ }
404
+ :deep( .el-loading-spinner) {
405
+ .path {
406
+ stroke: #8300BF;
407
+ }
408
+ i, .el-loading-text {
409
+ color: #8300BF;
410
+ }
411
+ }
412
+ div.input {
413
+ border: 1px solid #dcdfe6;
414
+ padding: 4px;
415
+ height: 300px;
416
+ }
417
+ div.main {
418
+ display: grid;
419
+ --mainLeftWidth: 243px;
420
+ grid-template-columns: var(--mainLeftWidth) calc(100% - var(--mainLeftWidth));
421
+ height: 100%;
422
+ }
423
+ div.main-left {
424
+ border-right: 1px solid #dcdfe6;
425
+ padding: 12px 20px 12px 12px;
426
+ height: 100%;
427
+ overflow: auto;
428
+ }
429
+ div.main-right.x1 {
430
+ height: 100%;
431
+ }
432
+ div.main-right.x2 {
433
+ height: 50%;
434
+ }
435
+ div.main-right.x3 {
436
+ height: 33.333%;
437
+ }
438
+ div.main-right.x4 {
439
+ height: 25%;
440
+ }
441
+ div.main-right.x5 {
442
+ height: 20%;
443
+ }
444
+ div.main-right.x6 {
445
+ height: 16.667%;
446
+ }
447
+ div.main-right.x7 {
448
+ height: 14.286%;
449
+ }
450
+ div.main-right.x8 {
451
+ height: 12.5%;
452
+ }
453
+ div.main-right.x9 {
454
+ height: 11.111%;
455
+ }
456
+ :deep( div.main-right div.controls) {
457
+ height: 0;
458
+ }
459
+ div.primary-button,
460
+ div.secondary-button {
461
+ display: flex;
462
+ justify-content: flex-end;
463
+ width: 210px;
464
+ }
465
+ div.primary-button {
466
+ margin-top: 14px;
467
+ }
468
+ div.secondary-button {
469
+ margin-top: 8px;
470
+ }
471
+ div.primary-button .el-button,
472
+ div.secondary-button .el-button,
473
+ div.primary-button .el-button:hover,
474
+ div.secondary-button .el-button:hover {
475
+ width: 121px;
476
+ border-color: #8300bf;
477
+ }
478
+ div.primary-button .el-button,
479
+ div.primary-button .el-button:hover {
480
+ background-color: #8300bf;
481
+ }
482
+ div.secondary-button .el-button,
483
+ div.secondary-button .el-button:hover {
484
+ background-color: #f9f2fc;
485
+ color: #8300bf;
486
+ }
487
+ div.scrollbar {
488
+ overflow-y: scroll;
489
+ scrollbar-width: thin;
490
+ }
491
+ div.scrollbar::-webkit-scrollbar {
492
+ width: 8px;
493
+ right: -8px;
494
+ background-color: #f5f5f5;
495
+ }
496
+ div.scrollbar::-webkit-scrollbar-thumb {
497
+ border-radius: 4px;
498
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
499
+ background-color: #979797;
500
+ }
501
+ div.scrollbar::-webkit-scrollbar-track {
502
+ border-radius: 10px;
503
+ background-color: #f5f5f5;
504
+ }
505
+ div.simulation-vuer {
506
+ height: 100%;
507
+ }
508
+ p.default {
509
+ font-family: Asap, sans-serif;
510
+ letter-spacing: 0;
511
+ margin: 16px 0;
512
+ text-align: start;
513
+ }
514
+ p.error {
515
+ margin-left: 16px;
516
+ }
517
+ p.input-parameters {
518
+ margin-bottom: 8px;
519
+ }
520
+ p.name,
521
+ p.input-parameters {
522
+ margin-top: 0;
523
+ font-weight: 500 /* Medium */;
524
+ }
525
+ p.name {
526
+ line-height: 20px;
527
+ }
528
+ p.note {
529
+ font-size: 12px;
530
+ line-height: 16px;
531
+ }
532
+ span.error {
533
+ font-weight: 500 /* Medium */;
534
+ }
535
+ </style>