hpcflow-new2 0.2.0a50__py3-none-any.whl → 0.2.0a52__py3-none-any.whl
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.
- hpcflow/_version.py +1 -1
- hpcflow/sdk/__init__.py +1 -1
- hpcflow/sdk/api.py +1 -1
- hpcflow/sdk/app.py +20 -11
- hpcflow/sdk/cli.py +34 -59
- hpcflow/sdk/core/__init__.py +13 -1
- hpcflow/sdk/core/actions.py +235 -126
- hpcflow/sdk/core/command_files.py +32 -24
- hpcflow/sdk/core/element.py +110 -114
- hpcflow/sdk/core/errors.py +57 -0
- hpcflow/sdk/core/loop.py +18 -34
- hpcflow/sdk/core/parameters.py +5 -3
- hpcflow/sdk/core/task.py +135 -131
- hpcflow/sdk/core/task_schema.py +11 -4
- hpcflow/sdk/core/utils.py +110 -2
- hpcflow/sdk/core/workflow.py +964 -676
- hpcflow/sdk/data/template_components/environments.yaml +0 -44
- hpcflow/sdk/data/template_components/task_schemas.yaml +52 -10
- hpcflow/sdk/persistence/__init__.py +21 -33
- hpcflow/sdk/persistence/base.py +1340 -458
- hpcflow/sdk/persistence/json.py +424 -546
- hpcflow/sdk/persistence/pending.py +563 -0
- hpcflow/sdk/persistence/store_resource.py +131 -0
- hpcflow/sdk/persistence/utils.py +57 -0
- hpcflow/sdk/persistence/zarr.py +852 -841
- hpcflow/sdk/submission/jobscript.py +133 -112
- hpcflow/sdk/submission/shells/bash.py +62 -16
- hpcflow/sdk/submission/shells/powershell.py +87 -16
- hpcflow/sdk/submission/submission.py +59 -35
- hpcflow/tests/unit/test_element.py +4 -9
- hpcflow/tests/unit/test_persistence.py +218 -0
- hpcflow/tests/unit/test_task.py +11 -12
- hpcflow/tests/unit/test_utils.py +82 -0
- hpcflow/tests/unit/test_workflow.py +3 -1
- {hpcflow_new2-0.2.0a50.dist-info → hpcflow_new2-0.2.0a52.dist-info}/METADATA +3 -1
- {hpcflow_new2-0.2.0a50.dist-info → hpcflow_new2-0.2.0a52.dist-info}/RECORD +38 -34
- {hpcflow_new2-0.2.0a50.dist-info → hpcflow_new2-0.2.0a52.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a50.dist-info → hpcflow_new2-0.2.0a52.dist-info}/entry_points.txt +0 -0
@@ -124,6 +124,7 @@ class InputFileGenerator(JSONLike):
|
|
124
124
|
inputs: List[app.Parameter]
|
125
125
|
script: str = None
|
126
126
|
environment: app.Environment = None
|
127
|
+
abortable: Optional[bool] = False
|
127
128
|
|
128
129
|
def get_action_rule(self):
|
129
130
|
"""Get the rule that allows testing if this input file generator must be
|
@@ -150,14 +151,10 @@ class InputFileGenerator(JSONLike):
|
|
150
151
|
config_dir=r"{cfg_dir}",
|
151
152
|
config_invocation_key=r"{cfg_invoc_key}",
|
152
153
|
)
|
153
|
-
wk_path,
|
154
|
+
wk_path, EAR_ID = sys.argv[1:]
|
155
|
+
EAR_ID = int(EAR_ID)
|
154
156
|
wk = app.Workflow(wk_path)
|
155
|
-
|
156
|
-
submission_idx=int(sub_idx),
|
157
|
-
jobscript_idx=int(js_idx),
|
158
|
-
JS_element_idx=int(js_elem_idx),
|
159
|
-
JS_action_idx=int(js_act_idx),
|
160
|
-
)
|
157
|
+
EAR = wk.get_EARs_from_IDs([EAR_ID])[0]
|
161
158
|
{script_main_func}(path=Path({file_path!r}), **EAR.get_IFG_input_values())
|
162
159
|
"""
|
163
160
|
)
|
@@ -210,6 +207,16 @@ class OutputFileParser(JSONLike):
|
|
210
207
|
environment: Environment = None
|
211
208
|
inputs: List[str] = None
|
212
209
|
options: Dict = None
|
210
|
+
abortable: Optional[bool] = False
|
211
|
+
save_files: Union[List[str], bool] = True
|
212
|
+
|
213
|
+
def __post_init__(self):
|
214
|
+
if not self.save_files:
|
215
|
+
# save no files
|
216
|
+
self.save_files = []
|
217
|
+
elif self.save_files is True:
|
218
|
+
# save all files
|
219
|
+
self.save_files = [i.label for i in self.output_files]
|
213
220
|
|
214
221
|
def compose_source(self) -> str:
|
215
222
|
"""Generate the file contents of this output file parser source."""
|
@@ -230,23 +237,15 @@ class OutputFileParser(JSONLike):
|
|
230
237
|
config_dir=r"{cfg_dir}",
|
231
238
|
config_invocation_key=r"{cfg_invoc_key}",
|
232
239
|
)
|
233
|
-
wk_path,
|
240
|
+
wk_path, EAR_ID = sys.argv[1:]
|
241
|
+
EAR_ID = int(EAR_ID)
|
234
242
|
wk = app.Workflow(wk_path)
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
JS_action_idx=int(js_act_idx),
|
240
|
-
)
|
241
|
-
value = {script_main_func}(**EAR.get_OFP_output_files())
|
242
|
-
wk.save_parameter(
|
243
|
-
name="{param_name}",
|
244
|
-
value=value,
|
245
|
-
submission_idx=int(sub_idx),
|
246
|
-
jobscript_idx=int(js_idx),
|
247
|
-
JS_element_idx=int(js_elem_idx),
|
248
|
-
JS_action_idx=int(js_act_idx),
|
243
|
+
EAR = wk.get_EARs_from_IDs([EAR_ID])[0]
|
244
|
+
value = {script_main_func}(
|
245
|
+
**EAR.get_OFP_output_files(),
|
246
|
+
**EAR.get_OFP_inputs(),
|
249
247
|
)
|
248
|
+
wk.save_parameter(name="{param_name}", value=value, EAR_ID=EAR_ID)
|
250
249
|
|
251
250
|
"""
|
252
251
|
)
|
@@ -366,10 +365,18 @@ class _FileContentsSpecifier(JSONLike):
|
|
366
365
|
)
|
367
366
|
# TODO: log if already persistent.
|
368
367
|
else:
|
369
|
-
data_ref = workflow.
|
370
|
-
|
368
|
+
data_ref = workflow._add_file(
|
369
|
+
store_contents=self.store_contents,
|
370
|
+
is_input=True,
|
371
371
|
source=source,
|
372
|
+
path=self.path,
|
373
|
+
contents=self.contents,
|
374
|
+
filename=self.file.name.name,
|
372
375
|
)
|
376
|
+
# data_ref = workflow._add_parameter_data(
|
377
|
+
# data=self._get_members(ensure_contents=True, use_file_label=True),
|
378
|
+
# source=source,
|
379
|
+
# )
|
373
380
|
is_new = True
|
374
381
|
self._value_group_idx = data_ref
|
375
382
|
self._workflow = workflow
|
@@ -381,6 +388,7 @@ class _FileContentsSpecifier(JSONLike):
|
|
381
388
|
return (self.normalised_path, [data_ref], is_new)
|
382
389
|
|
383
390
|
def _get_value(self, value_name=None):
|
391
|
+
# TODO: fix
|
384
392
|
if self._value_group_idx is not None:
|
385
393
|
grp = self.workflow.get_zarr_parameter_group(self._value_group_idx)
|
386
394
|
val = zarr_decode(grp)
|
hpcflow/sdk/core/element.py
CHANGED
@@ -6,10 +6,8 @@ from typing import Any, Dict, List, Optional, Union
|
|
6
6
|
from valida.conditions import ConditionLike
|
7
7
|
|
8
8
|
from hpcflow.sdk import app
|
9
|
-
from hpcflow.sdk.core.actions import EAR_ID, ElementID, IterationID
|
10
9
|
from hpcflow.sdk.core.json_like import JSONLike
|
11
10
|
from hpcflow.sdk.core.utils import check_valid_py_identifier
|
12
|
-
from hpcflow.sdk.typing import E_idx_type, EAR_idx_type, EI_idx_type
|
13
11
|
|
14
12
|
|
15
13
|
class _ElementPrefixedParameter:
|
@@ -174,23 +172,25 @@ class ElementIteration:
|
|
174
172
|
|
175
173
|
def __init__(
|
176
174
|
self,
|
175
|
+
id_: int,
|
176
|
+
is_pending: bool,
|
177
177
|
index: int,
|
178
178
|
element: app.Element,
|
179
179
|
data_idx: Dict,
|
180
|
-
|
181
|
-
|
182
|
-
global_idx: int,
|
180
|
+
EAR_IDs: Dict[int, int],
|
181
|
+
EARs: Union[List[Dict], None],
|
183
182
|
schema_parameters: List[str],
|
184
183
|
loop_idx: Dict,
|
185
184
|
):
|
185
|
+
self._id = id_
|
186
|
+
self._is_pending = is_pending
|
186
187
|
self._index = index
|
187
188
|
self._element = element
|
188
189
|
self._data_idx = data_idx
|
189
|
-
self._EARs_initialised = EARs_initialised
|
190
|
-
self._global_idx = global_idx
|
191
190
|
self._loop_idx = loop_idx
|
192
191
|
self._schema_parameters = schema_parameters
|
193
|
-
self.
|
192
|
+
self._EARs = EARs
|
193
|
+
self._EAR_IDs = EAR_IDs
|
194
194
|
|
195
195
|
# assigned on first access of corresponding properties:
|
196
196
|
self._inputs = None
|
@@ -201,8 +201,8 @@ class ElementIteration:
|
|
201
201
|
|
202
202
|
def __repr__(self):
|
203
203
|
return (
|
204
|
-
f"{self.__class__.__name__}("
|
205
|
-
f"
|
204
|
+
f"{self.__class__.__name__}(id={self.id_!r}, "
|
205
|
+
f"index={self.index!r}, element={self.element!r}, "
|
206
206
|
f"EARs_initialised={self.EARs_initialised!r}"
|
207
207
|
f")"
|
208
208
|
)
|
@@ -215,7 +215,7 @@ class ElementIteration:
|
|
215
215
|
@property
|
216
216
|
def EARs_initialised(self):
|
217
217
|
"""Whether or not the EARs have been initialised."""
|
218
|
-
return self.
|
218
|
+
return self._EARs is not None
|
219
219
|
|
220
220
|
@property
|
221
221
|
def element(self):
|
@@ -226,16 +226,12 @@ class ElementIteration:
|
|
226
226
|
return self._index
|
227
227
|
|
228
228
|
@property
|
229
|
-
def
|
230
|
-
return
|
231
|
-
task_insert_ID=self.task.insert_ID,
|
232
|
-
element_idx=self.element.index,
|
233
|
-
iteration_idx=self.index,
|
234
|
-
)
|
229
|
+
def id_(self) -> int:
|
230
|
+
return self._id
|
235
231
|
|
236
232
|
@property
|
237
|
-
def
|
238
|
-
return self.
|
233
|
+
def is_pending(self) -> bool:
|
234
|
+
return self._is_pending
|
239
235
|
|
240
236
|
@property
|
241
237
|
def task(self):
|
@@ -253,6 +249,14 @@ class ElementIteration:
|
|
253
249
|
def schema_parameters(self) -> List[str]:
|
254
250
|
return self._schema_parameters
|
255
251
|
|
252
|
+
@property
|
253
|
+
def EAR_IDs(self) -> Dict[int, int]:
|
254
|
+
return self._EAR_IDs
|
255
|
+
|
256
|
+
@property
|
257
|
+
def EAR_IDs_flat(self):
|
258
|
+
return [j for i in self.EAR_IDs.values() for j in i]
|
259
|
+
|
256
260
|
@property
|
257
261
|
def actions(self) -> Dict[app.ElementAction]:
|
258
262
|
if self._action_objs is None:
|
@@ -262,7 +266,7 @@ class ElementIteration:
|
|
262
266
|
action_idx=act_idx,
|
263
267
|
runs=runs,
|
264
268
|
)
|
265
|
-
for act_idx, runs in self.
|
269
|
+
for act_idx, runs in self._EARs.items()
|
266
270
|
}
|
267
271
|
return self._action_objs
|
268
272
|
|
@@ -416,20 +420,16 @@ class ElementIteration:
|
|
416
420
|
def get_EAR_dependencies(
|
417
421
|
self,
|
418
422
|
as_objects: Optional[bool] = False,
|
419
|
-
) -> List[Union[
|
423
|
+
) -> List[Union[int, app.ElementActionRun]]:
|
420
424
|
"""Get EARs that this element iteration depends on (excluding EARs of this element
|
421
425
|
iteration)."""
|
422
426
|
# TODO: test this includes EARs of upstream iterations of this iteration's element
|
423
427
|
out = sorted(
|
424
428
|
set(
|
425
|
-
|
429
|
+
EAR_ID
|
426
430
|
for i in self.action_runs
|
427
|
-
for
|
428
|
-
if not
|
429
|
-
_EAR_ID.task_insert_ID == self.task.insert_ID
|
430
|
-
and _EAR_ID.element_idx == self.element.index
|
431
|
-
and _EAR_ID.iteration_idx == self.index
|
432
|
-
)
|
431
|
+
for EAR_ID in i.get_EAR_dependencies(as_objects=False)
|
432
|
+
if not EAR_ID in self.EAR_IDs_flat
|
433
433
|
)
|
434
434
|
)
|
435
435
|
if as_objects:
|
@@ -438,15 +438,11 @@ class ElementIteration:
|
|
438
438
|
|
439
439
|
def get_element_iteration_dependencies(
|
440
440
|
self, as_objects: bool = False
|
441
|
-
) -> List[Union[
|
441
|
+
) -> List[Union[int, app.ElementIteration]]:
|
442
442
|
"""Get element iterations that this element iteration depends on."""
|
443
443
|
# TODO: test this includes previous iterations of this iteration's element
|
444
|
-
|
445
|
-
|
446
|
-
_EAR_ID.get_iteration_ID()
|
447
|
-
for _EAR_ID in self.get_EAR_dependencies(as_objects=False)
|
448
|
-
)
|
449
|
-
)
|
444
|
+
EAR_IDs = self.get_EAR_dependencies(as_objects=False)
|
445
|
+
out = sorted(set(self.workflow.get_element_iteration_IDs_from_EAR_IDs(EAR_IDs)))
|
450
446
|
if as_objects:
|
451
447
|
out = self.workflow.get_element_iterations_from_IDs(out)
|
452
448
|
return out
|
@@ -454,15 +450,11 @@ class ElementIteration:
|
|
454
450
|
def get_element_dependencies(
|
455
451
|
self,
|
456
452
|
as_objects: Optional[bool] = False,
|
457
|
-
) -> List[Union[
|
453
|
+
) -> List[Union[int, app.Element]]:
|
458
454
|
"""Get elements that this element iteration depends on."""
|
459
455
|
# TODO: this will be used in viz.
|
460
|
-
|
461
|
-
|
462
|
-
_EAR_ID.get_element_ID()
|
463
|
-
for _EAR_ID in self.get_EAR_dependencies(as_objects=False)
|
464
|
-
)
|
465
|
-
)
|
456
|
+
EAR_IDs = self.get_EAR_dependencies(as_objects=False)
|
457
|
+
out = sorted(set(self.workflow.get_element_IDs_from_EAR_IDs(EAR_IDs)))
|
466
458
|
if as_objects:
|
467
459
|
out = self.workflow.get_elements_from_IDs(out)
|
468
460
|
return out
|
@@ -489,10 +481,9 @@ class ElementIteration:
|
|
489
481
|
Dependencies may come from either elements from upstream tasks, or from locally
|
490
482
|
defined inputs/sequences/defaults from upstream tasks."""
|
491
483
|
|
492
|
-
out =
|
493
|
-
|
494
|
-
|
495
|
-
|
484
|
+
out = self.workflow.get_task_IDs_from_element_IDs(
|
485
|
+
self.get_element_dependencies(as_objects=False)
|
486
|
+
)
|
496
487
|
for i in self.get_input_dependencies().values():
|
497
488
|
out.append(i["task_insert_ID"])
|
498
489
|
|
@@ -505,25 +496,22 @@ class ElementIteration:
|
|
505
496
|
|
506
497
|
def get_dependent_EARs(
|
507
498
|
self, as_objects: bool = False
|
508
|
-
) -> List[Union[
|
499
|
+
) -> List[Union[int, app.ElementActionRun]]:
|
509
500
|
"""Get EARs of downstream iterations and tasks that depend on this element
|
510
501
|
iteration."""
|
511
502
|
# TODO: test this includes EARs of downstream iterations of this iteration's element
|
512
503
|
deps = []
|
513
|
-
for task in self.
|
514
|
-
for
|
515
|
-
for
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
):
|
525
|
-
deps.append(dependent_EAR)
|
526
|
-
|
504
|
+
for task in self.workflow.tasks[self.task.index :]:
|
505
|
+
for elem in task.elements[:]:
|
506
|
+
for iter_ in elem.iterations:
|
507
|
+
if iter_.id_ == self.id_:
|
508
|
+
# don't include EARs of this iteration
|
509
|
+
continue
|
510
|
+
for run in iter_.action_runs:
|
511
|
+
for dep_EAR_i in run.get_EAR_dependencies(as_objects=True):
|
512
|
+
# does dep_EAR_i belong to self?
|
513
|
+
if dep_EAR_i.id_ in self.EAR_IDs_flat and run.id_ not in deps:
|
514
|
+
deps.append(run.id_)
|
527
515
|
deps = sorted(deps)
|
528
516
|
if as_objects:
|
529
517
|
deps = self.workflow.get_EARs_from_IDs(deps)
|
@@ -532,26 +520,21 @@ class ElementIteration:
|
|
532
520
|
|
533
521
|
def get_dependent_element_iterations(
|
534
522
|
self, as_objects: bool = False
|
535
|
-
) -> List[Union[
|
523
|
+
) -> List[Union[int, app.ElementIteration]]:
|
536
524
|
"""Get elements iterations of downstream iterations and tasks that depend on this
|
537
525
|
element iteration."""
|
538
526
|
# TODO: test this includes downstream iterations of this iteration's element?
|
539
527
|
deps = []
|
540
|
-
for task in self.
|
541
|
-
for
|
542
|
-
for iter_i in
|
543
|
-
|
544
|
-
|
545
|
-
|
528
|
+
for task in self.workflow.tasks[self.task.index :]:
|
529
|
+
for elem in task.elements[:]:
|
530
|
+
for iter_i in elem.iterations:
|
531
|
+
if iter_i.id_ == self.id_:
|
532
|
+
continue
|
533
|
+
for dep_iter_i in iter_i.get_element_iteration_dependencies(
|
534
|
+
as_objects=True
|
546
535
|
):
|
547
|
-
if
|
548
|
-
|
549
|
-
and iter_ID.element_idx == self.element.index
|
550
|
-
and iter_ID.iteration_idx == self.index
|
551
|
-
and dependent_elem_iter not in deps
|
552
|
-
):
|
553
|
-
deps.append(dependent_elem_iter)
|
554
|
-
|
536
|
+
if dep_iter_i.id_ == self.id_ and iter_i.id_ not in deps:
|
537
|
+
deps.append(iter_i.id_)
|
555
538
|
deps = sorted(deps)
|
556
539
|
if as_objects:
|
557
540
|
deps = self.workflow.get_element_iterations_from_IDs(deps)
|
@@ -561,20 +544,17 @@ class ElementIteration:
|
|
561
544
|
def get_dependent_elements(
|
562
545
|
self,
|
563
546
|
as_objects: bool = False,
|
564
|
-
) -> List[Union[
|
547
|
+
) -> List[Union[int, app.Element]]:
|
565
548
|
"""Get elements of downstream tasks that depend on this element iteration."""
|
566
549
|
deps = []
|
567
550
|
for task in self.task.downstream_tasks:
|
568
|
-
for element in task.elements:
|
569
|
-
dependent_elem = element.element_ID
|
551
|
+
for element in task.elements[:]:
|
570
552
|
for iter_i in element.iterations:
|
571
|
-
for
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
):
|
577
|
-
deps.append(dependent_elem)
|
553
|
+
for dep_iter_i in iter_i.get_element_iteration_dependencies(
|
554
|
+
as_objects=True
|
555
|
+
):
|
556
|
+
if dep_iter_i.id_ == self.id_ and element.id_ not in deps:
|
557
|
+
deps.append(element.id_)
|
578
558
|
|
579
559
|
deps = sorted(deps)
|
580
560
|
if as_objects:
|
@@ -589,14 +569,13 @@ class ElementIteration:
|
|
589
569
|
"""Get downstream tasks that depend on this element iteration."""
|
590
570
|
deps = []
|
591
571
|
for task in self.task.downstream_tasks:
|
592
|
-
for
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
572
|
+
for element in task.elements[:]:
|
573
|
+
for iter_i in element.iterations:
|
574
|
+
for dep_iter_i in iter_i.get_element_iteration_dependencies(
|
575
|
+
as_objects=True
|
576
|
+
):
|
577
|
+
if dep_iter_i.id_ == self.id_ and task.insert_ID not in deps:
|
578
|
+
deps.append(task.insert_ID)
|
600
579
|
deps = sorted(deps)
|
601
580
|
if as_objects:
|
602
581
|
deps = [self.workflow.tasks.get(insert_ID=i) for i in deps]
|
@@ -614,19 +593,25 @@ class Element:
|
|
614
593
|
|
615
594
|
def __init__(
|
616
595
|
self,
|
596
|
+
id_: int,
|
597
|
+
is_pending: bool,
|
617
598
|
task: app.WorkflowTask,
|
618
599
|
index: int,
|
619
600
|
es_idx: int,
|
620
601
|
seq_idx: Dict[str, int],
|
621
602
|
src_idx: Dict[str, int],
|
622
|
-
|
603
|
+
iteration_IDs: List[int],
|
604
|
+
iterations: List[Dict],
|
623
605
|
) -> None:
|
606
|
+
self._id = id_
|
607
|
+
self._is_pending = is_pending
|
624
608
|
self._task = task
|
625
609
|
self._index = index
|
626
610
|
self._es_idx = es_idx
|
627
611
|
self._seq_idx = seq_idx
|
628
612
|
self._src_idx = src_idx
|
629
613
|
|
614
|
+
self._iteration_IDs = iteration_IDs
|
630
615
|
self._iterations = iterations
|
631
616
|
|
632
617
|
# assigned on first access:
|
@@ -634,11 +619,19 @@ class Element:
|
|
634
619
|
|
635
620
|
def __repr__(self):
|
636
621
|
return (
|
637
|
-
f"{self.__class__.__name__}("
|
638
|
-
f"
|
622
|
+
f"{self.__class__.__name__}(id={self.id_!r}, "
|
623
|
+
f"index={self.index!r}, task={self.task.unique_name!r}"
|
639
624
|
f")"
|
640
625
|
)
|
641
626
|
|
627
|
+
@property
|
628
|
+
def id_(self) -> int:
|
629
|
+
return self._id
|
630
|
+
|
631
|
+
@property
|
632
|
+
def is_pending(self) -> bool:
|
633
|
+
return self._is_pending
|
634
|
+
|
642
635
|
@property
|
643
636
|
def task(self) -> app.WorkflowTask:
|
644
637
|
return self._task
|
@@ -652,13 +645,6 @@ class Element:
|
|
652
645
|
|
653
646
|
return self._index
|
654
647
|
|
655
|
-
@property
|
656
|
-
def element_ID(self):
|
657
|
-
return ElementID(
|
658
|
-
task_insert_ID=self.task.insert_ID,
|
659
|
-
element_idx=self.index,
|
660
|
-
)
|
661
|
-
|
662
648
|
@property
|
663
649
|
def element_set_idx(self) -> int:
|
664
650
|
return self._es_idx
|
@@ -686,12 +672,21 @@ class Element:
|
|
686
672
|
def workflow(self) -> app.Workflow:
|
687
673
|
return self.task.workflow
|
688
674
|
|
675
|
+
@property
|
676
|
+
def iteration_IDs(self) -> List[int]:
|
677
|
+
return self._iteration_IDs
|
678
|
+
|
689
679
|
@property
|
690
680
|
def iterations(self) -> Dict[app.ElementAction]:
|
681
|
+
# TODO: fix this
|
691
682
|
if self._iteration_objs is None:
|
692
683
|
self._iteration_objs = [
|
693
|
-
self.app.ElementIteration(
|
694
|
-
|
684
|
+
self.app.ElementIteration(
|
685
|
+
element=self,
|
686
|
+
index=idx,
|
687
|
+
**{k: v for k, v in iter_i.items() if k != "element_ID"},
|
688
|
+
)
|
689
|
+
for idx, iter_i in enumerate(self._iterations)
|
695
690
|
]
|
696
691
|
return self._iteration_objs
|
697
692
|
|
@@ -835,13 +830,13 @@ class Element:
|
|
835
830
|
|
836
831
|
def get_EAR_dependencies(
|
837
832
|
self, as_objects: bool = False
|
838
|
-
) -> List[Union[
|
833
|
+
) -> List[Union[int, app.ElementActionRun]]:
|
839
834
|
"""Get EARs that the most recent iteration of this element depends on."""
|
840
835
|
return self.latest_iteration.get_EAR_dependencies(as_objects=as_objects)
|
841
836
|
|
842
837
|
def get_element_iteration_dependencies(
|
843
838
|
self, as_objects: bool = False
|
844
|
-
) -> List[Union[
|
839
|
+
) -> List[Union[int, app.ElementIteration]]:
|
845
840
|
"""Get element iterations that the most recent iteration of this element depends
|
846
841
|
on."""
|
847
842
|
return self.latest_iteration.get_element_iteration_dependencies(
|
@@ -850,7 +845,7 @@ class Element:
|
|
850
845
|
|
851
846
|
def get_element_dependencies(
|
852
847
|
self, as_objects: bool = False
|
853
|
-
) -> List[Union[
|
848
|
+
) -> List[Union[int, app.Element]]:
|
854
849
|
"""Get elements that the most recent iteration of this element depends on."""
|
855
850
|
return self.latest_iteration.get_element_dependencies(as_objects=as_objects)
|
856
851
|
|
@@ -871,13 +866,13 @@ class Element:
|
|
871
866
|
|
872
867
|
def get_dependent_EARs(
|
873
868
|
self, as_objects: bool = False
|
874
|
-
) -> List[Union[
|
869
|
+
) -> List[Union[int, app.ElementActionRun]]:
|
875
870
|
"""Get EARs that depend on the most recent iteration of this element."""
|
876
871
|
return self.latest_iteration.get_dependent_EARs(as_objects=as_objects)
|
877
872
|
|
878
873
|
def get_dependent_element_iterations(
|
879
874
|
self, as_objects: bool = False
|
880
|
-
) -> List[Union[
|
875
|
+
) -> List[Union[int, app.ElementIteration]]:
|
881
876
|
"""Get element iterations that depend on the most recent iteration of this
|
882
877
|
element."""
|
883
878
|
return self.latest_iteration.get_dependent_element_iterations(
|
@@ -886,7 +881,7 @@ class Element:
|
|
886
881
|
|
887
882
|
def get_dependent_elements(
|
888
883
|
self, as_objects: bool = False
|
889
|
-
) -> List[Union[
|
884
|
+
) -> List[Union[int, app.Element]]:
|
890
885
|
"""Get elements that depend on the most recent iteration of this element."""
|
891
886
|
return self.latest_iteration.get_dependent_elements(as_objects=as_objects)
|
892
887
|
|
@@ -901,7 +896,7 @@ class Element:
|
|
901
896
|
dependencies.
|
902
897
|
|
903
898
|
Dependencies are resolved using the initial iteration only. This method is used to
|
904
|
-
identify from which element in the previous iteration
|
899
|
+
identify from which element in the previous iteration a new iteration should be
|
905
900
|
parametrised.
|
906
901
|
|
907
902
|
Parameters
|
@@ -921,7 +916,8 @@ class Element:
|
|
921
916
|
all_deps = get_deps(self)
|
922
917
|
|
923
918
|
if task_insert_ID is not None:
|
924
|
-
|
919
|
+
elem_ID_subset = self.workflow.tasks.get(insert_ID=task_insert_ID).element_IDs
|
920
|
+
all_deps = [i for i in all_deps if i in elem_ID_subset]
|
925
921
|
|
926
922
|
return self.workflow.get_elements_from_IDs(sorted(all_deps))
|
927
923
|
|
hpcflow/sdk/core/errors.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
from typing import Iterable
|
2
|
+
|
3
|
+
|
1
4
|
class InputValueDuplicateSequenceAddress(ValueError):
|
2
5
|
pass
|
3
6
|
|
@@ -188,3 +191,57 @@ class UnsupportedShellError(ValueError):
|
|
188
191
|
"""We don't support this shell on this OS."""
|
189
192
|
|
190
193
|
pass
|
194
|
+
|
195
|
+
|
196
|
+
class _MissingStoreItemError(ValueError):
|
197
|
+
def __init__(self, id_lst: Iterable[int], item_type: str) -> None:
|
198
|
+
message = (
|
199
|
+
f"Store {item_type}s with the following IDs do not all exist: {id_lst!r}"
|
200
|
+
)
|
201
|
+
super().__init__(message)
|
202
|
+
self.id_lst = id_lst
|
203
|
+
|
204
|
+
|
205
|
+
class MissingStoreTaskError(_MissingStoreItemError):
|
206
|
+
"""Some task IDs do not exist."""
|
207
|
+
|
208
|
+
_item_type = "task"
|
209
|
+
|
210
|
+
def __init__(self, id_lst: Iterable[int]) -> None:
|
211
|
+
super().__init__(id_lst, self._item_type)
|
212
|
+
|
213
|
+
|
214
|
+
class MissingStoreElementError(_MissingStoreItemError):
|
215
|
+
"""Some element IDs do not exist."""
|
216
|
+
|
217
|
+
_item_type = "element"
|
218
|
+
|
219
|
+
def __init__(self, id_lst: Iterable[int]) -> None:
|
220
|
+
super().__init__(id_lst, self._item_type)
|
221
|
+
|
222
|
+
|
223
|
+
class MissingStoreElementIterationError(_MissingStoreItemError):
|
224
|
+
"""Some element iteration IDs do not exist."""
|
225
|
+
|
226
|
+
_item_type = "element iteration"
|
227
|
+
|
228
|
+
def __init__(self, id_lst: Iterable[int]) -> None:
|
229
|
+
super().__init__(id_lst, self._item_type)
|
230
|
+
|
231
|
+
|
232
|
+
class MissingStoreEARError(_MissingStoreItemError):
|
233
|
+
"""Some EAR IDs do not exist."""
|
234
|
+
|
235
|
+
_item_type = "EAR"
|
236
|
+
|
237
|
+
def __init__(self, id_lst: Iterable[int]) -> None:
|
238
|
+
super().__init__(id_lst, self._item_type)
|
239
|
+
|
240
|
+
|
241
|
+
class MissingParameterData(_MissingStoreItemError):
|
242
|
+
"""Some parameter IDs do not exist"""
|
243
|
+
|
244
|
+
_item_type = "parameter"
|
245
|
+
|
246
|
+
def __init__(self, id_lst: Iterable[int]) -> None:
|
247
|
+
super().__init__(id_lst, self._item_type)
|