hpcflow-new2 0.2.0a179__py3-none-any.whl → 0.2.0a180__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/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/sdk/__init__.py +4 -1
- hpcflow/sdk/app.py +160 -15
- hpcflow/sdk/cli.py +14 -0
- hpcflow/sdk/cli_common.py +83 -0
- hpcflow/sdk/config/__init__.py +4 -0
- hpcflow/sdk/config/callbacks.py +25 -2
- hpcflow/sdk/config/cli.py +4 -1
- hpcflow/sdk/config/config.py +188 -14
- hpcflow/sdk/config/config_file.py +91 -3
- hpcflow/sdk/config/errors.py +33 -0
- hpcflow/sdk/core/__init__.py +2 -0
- hpcflow/sdk/core/actions.py +492 -35
- hpcflow/sdk/core/cache.py +22 -0
- hpcflow/sdk/core/command_files.py +221 -5
- hpcflow/sdk/core/commands.py +57 -0
- hpcflow/sdk/core/element.py +407 -8
- hpcflow/sdk/core/environment.py +92 -0
- hpcflow/sdk/core/errors.py +245 -61
- hpcflow/sdk/core/json_like.py +72 -14
- hpcflow/sdk/core/loop.py +122 -21
- hpcflow/sdk/core/loop_cache.py +34 -9
- hpcflow/sdk/core/object_list.py +172 -26
- hpcflow/sdk/core/parallel.py +14 -0
- hpcflow/sdk/core/parameters.py +478 -25
- hpcflow/sdk/core/rule.py +31 -1
- hpcflow/sdk/core/run_dir_files.py +12 -2
- hpcflow/sdk/core/task.py +407 -80
- hpcflow/sdk/core/task_schema.py +70 -9
- hpcflow/sdk/core/test_utils.py +35 -0
- hpcflow/sdk/core/utils.py +101 -4
- hpcflow/sdk/core/validation.py +13 -1
- hpcflow/sdk/core/workflow.py +316 -96
- hpcflow/sdk/core/zarr_io.py +23 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +9 -0
- hpcflow/sdk/helper/helper.py +28 -0
- hpcflow/sdk/helper/watcher.py +33 -0
- hpcflow/sdk/log.py +40 -0
- hpcflow/sdk/persistence/__init__.py +14 -4
- hpcflow/sdk/persistence/base.py +289 -23
- hpcflow/sdk/persistence/json.py +29 -0
- hpcflow/sdk/persistence/pending.py +217 -107
- hpcflow/sdk/persistence/store_resource.py +58 -2
- hpcflow/sdk/persistence/utils.py +8 -0
- hpcflow/sdk/persistence/zarr.py +68 -1
- hpcflow/sdk/runtime.py +52 -10
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/jobscript.py +198 -9
- hpcflow/sdk/submission/jobscript_info.py +13 -0
- hpcflow/sdk/submission/schedulers/__init__.py +60 -0
- hpcflow/sdk/submission/schedulers/direct.py +53 -0
- hpcflow/sdk/submission/schedulers/sge.py +45 -7
- hpcflow/sdk/submission/schedulers/slurm.py +45 -8
- hpcflow/sdk/submission/schedulers/utils.py +4 -0
- hpcflow/sdk/submission/shells/__init__.py +11 -1
- hpcflow/sdk/submission/shells/base.py +32 -1
- hpcflow/sdk/submission/shells/bash.py +36 -1
- hpcflow/sdk/submission/shells/os_version.py +18 -6
- hpcflow/sdk/submission/shells/powershell.py +22 -0
- hpcflow/sdk/submission/submission.py +88 -3
- hpcflow/sdk/typing.py +10 -1
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/METADATA +1 -1
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/RECORD +70 -70
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a179.dist-info → hpcflow_new2-0.2.0a180.dist-info}/entry_points.txt +0 -0
hpcflow/sdk/core/element.py
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
"""
|
2
|
+
Elements are components of tasks.
|
3
|
+
"""
|
4
|
+
|
1
5
|
from __future__ import annotations
|
2
6
|
import copy
|
3
7
|
from dataclasses import dataclass, field
|
@@ -89,7 +93,8 @@ class _ElementPrefixedParameter:
|
|
89
93
|
|
90
94
|
@property
|
91
95
|
def prefixed_names_unlabelled(self) -> Dict[str, List[str]]:
|
92
|
-
"""
|
96
|
+
"""
|
97
|
+
A mapping between input types and associated labels.
|
93
98
|
|
94
99
|
If the schema input for a given input type has `multiple=False` (even if a label
|
95
100
|
is defined), the values for that input type will be an empty list.
|
@@ -101,6 +106,9 @@ class _ElementPrefixedParameter:
|
|
101
106
|
|
102
107
|
@property
|
103
108
|
def prefixed_names_unlabelled_str(self):
|
109
|
+
"""
|
110
|
+
A description of the prefixed names.
|
111
|
+
"""
|
104
112
|
return ", ".join(i for i in self.prefixed_names_unlabelled)
|
105
113
|
|
106
114
|
def __repr__(self):
|
@@ -137,6 +145,19 @@ class _ElementPrefixedParameter:
|
|
137
145
|
|
138
146
|
|
139
147
|
class ElementInputs(_ElementPrefixedParameter):
|
148
|
+
"""
|
149
|
+
The inputs to an element.
|
150
|
+
|
151
|
+
Parameters
|
152
|
+
----------
|
153
|
+
element_iteration: ElementIteration
|
154
|
+
Which iteration does this refer to?
|
155
|
+
element_action: ~hpcflow.app.ElementAction
|
156
|
+
Which action does this refer to?
|
157
|
+
element_action_run: ~hpcflow.app.ElementActionRun
|
158
|
+
Which EAR does this refer to?
|
159
|
+
"""
|
160
|
+
|
140
161
|
def __init__(
|
141
162
|
self,
|
142
163
|
element_iteration: Optional[app.ElementIteration] = None,
|
@@ -147,6 +168,19 @@ class ElementInputs(_ElementPrefixedParameter):
|
|
147
168
|
|
148
169
|
|
149
170
|
class ElementOutputs(_ElementPrefixedParameter):
|
171
|
+
"""
|
172
|
+
The outputs from an element.
|
173
|
+
|
174
|
+
Parameters
|
175
|
+
----------
|
176
|
+
element_iteration: ElementIteration
|
177
|
+
Which iteration does this refer to?
|
178
|
+
element_action: ~hpcflow.app.ElementAction
|
179
|
+
Which action does this refer to?
|
180
|
+
element_action_run: ~hpcflow.app.ElementActionRun
|
181
|
+
Which EAR does this refer to?
|
182
|
+
"""
|
183
|
+
|
150
184
|
def __init__(
|
151
185
|
self,
|
152
186
|
element_iteration: Optional[app.ElementIteration] = None,
|
@@ -157,6 +191,19 @@ class ElementOutputs(_ElementPrefixedParameter):
|
|
157
191
|
|
158
192
|
|
159
193
|
class ElementInputFiles(_ElementPrefixedParameter):
|
194
|
+
"""
|
195
|
+
The input files to an element.
|
196
|
+
|
197
|
+
Parameters
|
198
|
+
----------
|
199
|
+
element_iteration: ElementIteration
|
200
|
+
Which iteration does this refer to?
|
201
|
+
element_action: ~hpcflow.app.ElementAction
|
202
|
+
Which action does this refer to?
|
203
|
+
element_action_run: ~hpcflow.app.ElementActionRun
|
204
|
+
Which EAR does this refer to?
|
205
|
+
"""
|
206
|
+
|
160
207
|
def __init__(
|
161
208
|
self,
|
162
209
|
element_iteration: Optional[app.ElementIteration] = None,
|
@@ -169,6 +216,19 @@ class ElementInputFiles(_ElementPrefixedParameter):
|
|
169
216
|
|
170
217
|
|
171
218
|
class ElementOutputFiles(_ElementPrefixedParameter):
|
219
|
+
"""
|
220
|
+
The output files from an element.
|
221
|
+
|
222
|
+
Parameters
|
223
|
+
----------
|
224
|
+
element_iteration: ElementIteration
|
225
|
+
Which iteration does this refer to?
|
226
|
+
element_action: ~hpcflow.app.ElementAction
|
227
|
+
Which action does this refer to?
|
228
|
+
element_action_run: ~hpcflow.app.ElementActionRun
|
229
|
+
Which EAR does this refer to?
|
230
|
+
"""
|
231
|
+
|
172
232
|
def __init__(
|
173
233
|
self,
|
174
234
|
element_iteration: Optional[app.ElementIteration] = None,
|
@@ -182,33 +242,108 @@ class ElementOutputFiles(_ElementPrefixedParameter):
|
|
182
242
|
|
183
243
|
@dataclass
|
184
244
|
class ElementResources(JSONLike):
|
245
|
+
"""
|
246
|
+
The resources an element requires.
|
247
|
+
|
248
|
+
Note
|
249
|
+
----
|
250
|
+
It is common to leave most of these unspecified.
|
251
|
+
Many of them have complex interactions with each other.
|
252
|
+
|
253
|
+
Parameters
|
254
|
+
----------
|
255
|
+
scratch: str
|
256
|
+
Which scratch space to use.
|
257
|
+
parallel_mode: ParallelMode
|
258
|
+
Which parallel mode to use.
|
259
|
+
num_cores: int
|
260
|
+
How many cores to request.
|
261
|
+
num_cores_per_node: int
|
262
|
+
How many cores per compute node to request.
|
263
|
+
num_threads: int
|
264
|
+
How many threads to request.
|
265
|
+
num_nodes: int
|
266
|
+
How many compute nodes to request.
|
267
|
+
scheduler: str
|
268
|
+
Which scheduler to use.
|
269
|
+
shell: str
|
270
|
+
Which system shell to use.
|
271
|
+
use_job_array: bool
|
272
|
+
Whether to use array jobs.
|
273
|
+
max_array_items: int
|
274
|
+
If using array jobs, up to how many items should be in the job array.
|
275
|
+
time_limit: str
|
276
|
+
How long to run for.
|
277
|
+
scheduler_args: dict[str, Any]
|
278
|
+
Additional arguments to pass to the scheduler.
|
279
|
+
shell_args: dict[str, Any]
|
280
|
+
Additional arguments to pass to the shell.
|
281
|
+
os_name: str
|
282
|
+
Which OS to use.
|
283
|
+
environments: dict
|
284
|
+
Which execution environments to use.
|
285
|
+
SGE_parallel_env: str
|
286
|
+
Which SGE parallel environment to request.
|
287
|
+
SLURM_partition: str
|
288
|
+
Which SLURM partition to request.
|
289
|
+
SLURM_num_tasks: str
|
290
|
+
How many SLURM tasks to request.
|
291
|
+
SLURM_num_tasks_per_node: str
|
292
|
+
How many SLURM tasks per compute node to request.
|
293
|
+
SLURM_num_nodes: str
|
294
|
+
How many compute nodes to request.
|
295
|
+
SLURM_num_cpus_per_task: str
|
296
|
+
How many CPU cores to ask for per SLURM task.
|
297
|
+
"""
|
298
|
+
|
185
299
|
# TODO: how to specify e.g. high-memory requirement?
|
186
300
|
|
301
|
+
#: Which scratch space to use.
|
187
302
|
scratch: Optional[str] = None
|
303
|
+
#: Which parallel mode to use.
|
188
304
|
parallel_mode: Optional[ParallelMode] = None
|
305
|
+
#: How many cores to request.
|
189
306
|
num_cores: Optional[int] = None
|
307
|
+
#: How many cores per compute node to request.
|
190
308
|
num_cores_per_node: Optional[int] = None
|
309
|
+
#: How many threads to request.
|
191
310
|
num_threads: Optional[int] = None
|
311
|
+
#: How many compute nodes to request.
|
192
312
|
num_nodes: Optional[int] = None
|
313
|
+
|
314
|
+
#: Which scheduler to use.
|
193
315
|
scheduler: Optional[str] = None
|
316
|
+
#: Which system shell to use.
|
194
317
|
shell: Optional[str] = None
|
318
|
+
#: Whether to use array jobs.
|
195
319
|
use_job_array: Optional[bool] = None
|
320
|
+
#: If using array jobs, up to how many items should be in the job array.
|
196
321
|
max_array_items: Optional[int] = None
|
322
|
+
#: How long to run for.
|
197
323
|
time_limit: Optional[str] = None
|
198
|
-
|
324
|
+
#: Additional arguments to pass to the scheduler.
|
199
325
|
scheduler_args: Optional[Dict] = None
|
326
|
+
#: Additional arguments to pass to the shell.
|
200
327
|
shell_args: Optional[Dict] = None
|
328
|
+
#: Which OS to use.
|
201
329
|
os_name: Optional[str] = None
|
330
|
+
#: Which execution environments to use.
|
202
331
|
environments: Optional[Dict] = None
|
203
332
|
|
204
333
|
# SGE scheduler specific:
|
334
|
+
#: Which SGE parallel environment to request.
|
205
335
|
SGE_parallel_env: str = None
|
206
336
|
|
207
337
|
# SLURM scheduler specific:
|
338
|
+
#: Which SLURM partition to request.
|
208
339
|
SLURM_partition: str = None
|
340
|
+
#: How many SLURM tasks to request.
|
209
341
|
SLURM_num_tasks: str = None
|
342
|
+
#: How many SLURM tasks per compute node to request.
|
210
343
|
SLURM_num_tasks_per_node: str = None
|
344
|
+
#: How many compute nodes to request.
|
211
345
|
SLURM_num_nodes: str = None
|
346
|
+
#: How many CPU cores to ask for per SLURM task.
|
212
347
|
SLURM_num_cpus_per_task: str = None
|
213
348
|
|
214
349
|
def __post_init__(self):
|
@@ -291,20 +426,32 @@ class ElementResources(JSONLike):
|
|
291
426
|
|
292
427
|
@staticmethod
|
293
428
|
def get_default_os_name():
|
429
|
+
"""
|
430
|
+
Get the default value for OS name.
|
431
|
+
"""
|
294
432
|
return os.name
|
295
433
|
|
296
434
|
@classmethod
|
297
435
|
def get_default_shell(cls):
|
436
|
+
"""
|
437
|
+
Get the default value for name.
|
438
|
+
"""
|
298
439
|
return cls.app.config.default_shell
|
299
440
|
|
300
441
|
@classmethod
|
301
442
|
def get_default_scheduler(cls, os_name, shell_name):
|
443
|
+
"""
|
444
|
+
Get the default value for scheduler.
|
445
|
+
"""
|
302
446
|
if os_name == "nt" and "wsl" in shell_name:
|
303
447
|
# provide a "*_posix" default scheduler on windows if shell is WSL:
|
304
448
|
return "direct_posix"
|
305
449
|
return cls.app.config.default_scheduler
|
306
450
|
|
307
451
|
def set_defaults(self):
|
452
|
+
"""
|
453
|
+
Set defaults for unspecified values that need defaults.
|
454
|
+
"""
|
308
455
|
if self.os_name is None:
|
309
456
|
self.os_name = self.get_default_os_name()
|
310
457
|
if self.shell is None:
|
@@ -353,6 +500,33 @@ class ElementResources(JSONLike):
|
|
353
500
|
|
354
501
|
|
355
502
|
class ElementIteration:
|
503
|
+
"""
|
504
|
+
A particular iteration of an element.
|
505
|
+
|
506
|
+
Parameters
|
507
|
+
----------
|
508
|
+
id_ : int
|
509
|
+
The ID of this iteration.
|
510
|
+
is_pending: bool
|
511
|
+
Whether this iteration is pending execution.
|
512
|
+
index: int
|
513
|
+
The index of this iteration in its parent element.
|
514
|
+
element: Element
|
515
|
+
The element this is an iteration of.
|
516
|
+
data_idx: dict
|
517
|
+
The overall element iteration data index, before resolution of EARs.
|
518
|
+
EARs_initialised: bool
|
519
|
+
Whether EARs have been set up for the iteration.
|
520
|
+
EAR_IDs: dict[int, int]
|
521
|
+
Mapping from iteration number to EAR ID, where known.
|
522
|
+
EARs: list[dict]
|
523
|
+
Data about EARs.
|
524
|
+
schema_parameters: list[str]
|
525
|
+
Parameters from the schema.
|
526
|
+
loop_idx: dict[str, int]
|
527
|
+
Indexing information from the loop.
|
528
|
+
"""
|
529
|
+
|
356
530
|
_app_attr = "app"
|
357
531
|
|
358
532
|
def __init__(
|
@@ -406,46 +580,79 @@ class ElementIteration:
|
|
406
580
|
|
407
581
|
@property
|
408
582
|
def element(self):
|
583
|
+
"""
|
584
|
+
The element this is an iteration of.
|
585
|
+
"""
|
409
586
|
return self._element
|
410
587
|
|
411
588
|
@property
|
412
589
|
def index(self):
|
590
|
+
"""
|
591
|
+
The index of this iteration in its parent element.
|
592
|
+
"""
|
413
593
|
return self._index
|
414
594
|
|
415
595
|
@property
|
416
596
|
def id_(self) -> int:
|
597
|
+
"""
|
598
|
+
The ID of this iteration.
|
599
|
+
"""
|
417
600
|
return self._id
|
418
601
|
|
419
602
|
@property
|
420
603
|
def is_pending(self) -> bool:
|
604
|
+
"""
|
605
|
+
Whether this iteration is pending execution.
|
606
|
+
"""
|
421
607
|
return self._is_pending
|
422
608
|
|
423
609
|
@property
|
424
610
|
def task(self):
|
611
|
+
"""
|
612
|
+
The task this is an iteration of an element for.
|
613
|
+
"""
|
425
614
|
return self.element.task
|
426
615
|
|
427
616
|
@property
|
428
617
|
def workflow(self):
|
618
|
+
"""
|
619
|
+
The workflow this is a part of.
|
620
|
+
"""
|
429
621
|
return self.element.workflow
|
430
622
|
|
431
623
|
@property
|
432
624
|
def loop_idx(self) -> Dict[str, int]:
|
625
|
+
"""
|
626
|
+
Indexing information from the loop.
|
627
|
+
"""
|
433
628
|
return self._loop_idx
|
434
629
|
|
435
630
|
@property
|
436
631
|
def schema_parameters(self) -> List[str]:
|
632
|
+
"""
|
633
|
+
Parameters from the schema.
|
634
|
+
"""
|
437
635
|
return self._schema_parameters
|
438
636
|
|
439
637
|
@property
|
440
638
|
def EAR_IDs(self) -> Dict[int, int]:
|
639
|
+
"""
|
640
|
+
Mapping from iteration number to EAR ID, where known.
|
641
|
+
"""
|
441
642
|
return self._EAR_IDs
|
442
643
|
|
443
644
|
@property
|
444
645
|
def EAR_IDs_flat(self):
|
646
|
+
"""
|
647
|
+
The EAR IDs.
|
648
|
+
"""
|
445
649
|
return [j for i in self.EAR_IDs.values() for j in i]
|
446
650
|
|
447
651
|
@property
|
448
652
|
def actions(self) -> Dict[app.ElementAction]:
|
653
|
+
"""
|
654
|
+
The actions of this iteration.
|
655
|
+
"""
|
449
656
|
if self._action_objs is None:
|
450
657
|
self._action_objs = {
|
451
658
|
act_idx: self.app.ElementAction(
|
@@ -459,30 +666,44 @@ class ElementIteration:
|
|
459
666
|
|
460
667
|
@property
|
461
668
|
def action_runs(self) -> List[app.ElementActionRun]:
|
462
|
-
"""
|
463
|
-
element action
|
669
|
+
"""
|
670
|
+
A list of element action runs, where only the final run is taken for each
|
671
|
+
element action.
|
672
|
+
"""
|
464
673
|
return [i.runs[-1] for i in self.actions.values()]
|
465
674
|
|
466
675
|
@property
|
467
676
|
def inputs(self) -> app.ElementInputs:
|
677
|
+
"""
|
678
|
+
The inputs to this element.
|
679
|
+
"""
|
468
680
|
if not self._inputs:
|
469
681
|
self._inputs = self.app.ElementInputs(element_iteration=self)
|
470
682
|
return self._inputs
|
471
683
|
|
472
684
|
@property
|
473
685
|
def outputs(self) -> app.ElementOutputs:
|
686
|
+
"""
|
687
|
+
The outputs from this element.
|
688
|
+
"""
|
474
689
|
if not self._outputs:
|
475
690
|
self._outputs = self.app.ElementOutputs(element_iteration=self)
|
476
691
|
return self._outputs
|
477
692
|
|
478
693
|
@property
|
479
694
|
def input_files(self) -> app.ElementInputFiles:
|
695
|
+
"""
|
696
|
+
The input files to this element.
|
697
|
+
"""
|
480
698
|
if not self._input_files:
|
481
699
|
self._input_files = self.app.ElementInputFiles(element_iteration=self)
|
482
700
|
return self._input_files
|
483
701
|
|
484
702
|
@property
|
485
703
|
def output_files(self) -> app.ElementOutputFiles:
|
704
|
+
"""
|
705
|
+
The output files from this element.
|
706
|
+
"""
|
486
707
|
if not self._output_files:
|
487
708
|
self._output_files = self.app.ElementOutputFiles(element_iteration=self)
|
488
709
|
return self._output_files
|
@@ -521,10 +742,16 @@ class ElementIteration:
|
|
521
742
|
run_idx: int = -1,
|
522
743
|
) -> Dict[str, int]:
|
523
744
|
"""
|
745
|
+
Get the data index.
|
746
|
+
|
524
747
|
Parameters
|
525
748
|
----------
|
526
|
-
|
749
|
+
path:
|
750
|
+
If specified, filters the data indices to the ones relevant to this path.
|
751
|
+
action_idx:
|
527
752
|
The index of the action within the schema.
|
753
|
+
run_idx:
|
754
|
+
The index of the run within the action.
|
528
755
|
"""
|
529
756
|
|
530
757
|
if not self.actions:
|
@@ -563,6 +790,8 @@ class ElementIteration:
|
|
563
790
|
use_task_index: bool = False,
|
564
791
|
) -> Dict[str, Union[str, Dict[str, Any]]]:
|
565
792
|
"""
|
793
|
+
Get the origin of parameters.
|
794
|
+
|
566
795
|
Parameters
|
567
796
|
----------
|
568
797
|
use_task_index
|
@@ -938,10 +1167,39 @@ class ElementIteration:
|
|
938
1167
|
def get_resources_obj(
|
939
1168
|
self, action: app.Action, set_defaults: bool = False
|
940
1169
|
) -> app.ElementResources:
|
1170
|
+
"""
|
1171
|
+
Get the resources for an action (see :py:meth:`get_resources`)
|
1172
|
+
as a searchable model.
|
1173
|
+
"""
|
941
1174
|
return self.app.ElementResources(**self.get_resources(action, set_defaults))
|
942
1175
|
|
943
1176
|
|
944
1177
|
class Element:
|
1178
|
+
"""
|
1179
|
+
A basic component of a workflow. Elements are enactments of tasks.
|
1180
|
+
|
1181
|
+
Parameters
|
1182
|
+
----------
|
1183
|
+
id_ : int
|
1184
|
+
The ID of this element.
|
1185
|
+
is_pending: bool
|
1186
|
+
Whether this element is pending execution.
|
1187
|
+
task: ~hpcflow.app.WorkflowTask
|
1188
|
+
The task this is part of the enactment of.
|
1189
|
+
index: int
|
1190
|
+
The index of this element.
|
1191
|
+
es_idx: int
|
1192
|
+
The index within the task of the element set containing this element.
|
1193
|
+
seq_idx: dict[str, int]
|
1194
|
+
The sequence index IDs.
|
1195
|
+
src_idx: dict[str, int]
|
1196
|
+
The input source indices.
|
1197
|
+
iteration_IDs: list[int]
|
1198
|
+
The known IDs of iterations,
|
1199
|
+
iterations: list[dict]
|
1200
|
+
Data for creating iteration objects.
|
1201
|
+
"""
|
1202
|
+
|
945
1203
|
_app_attr = "app"
|
946
1204
|
|
947
1205
|
# TODO: use slots
|
@@ -984,14 +1242,23 @@ class Element:
|
|
984
1242
|
|
985
1243
|
@property
|
986
1244
|
def id_(self) -> int:
|
1245
|
+
"""
|
1246
|
+
The ID of this element.
|
1247
|
+
"""
|
987
1248
|
return self._id
|
988
1249
|
|
989
1250
|
@property
|
990
1251
|
def is_pending(self) -> bool:
|
1252
|
+
"""
|
1253
|
+
Whether this element is pending execution.
|
1254
|
+
"""
|
991
1255
|
return self._is_pending
|
992
1256
|
|
993
1257
|
@property
|
994
1258
|
def task(self) -> app.WorkflowTask:
|
1259
|
+
"""
|
1260
|
+
The task this is part of the enactment of.
|
1261
|
+
"""
|
995
1262
|
return self._task
|
996
1263
|
|
997
1264
|
@property
|
@@ -1005,22 +1272,37 @@ class Element:
|
|
1005
1272
|
|
1006
1273
|
@property
|
1007
1274
|
def element_set_idx(self) -> int:
|
1275
|
+
"""
|
1276
|
+
The index within the task of the element set containing this element.
|
1277
|
+
"""
|
1008
1278
|
return self._es_idx
|
1009
1279
|
|
1010
1280
|
@property
|
1011
1281
|
def element_set(self):
|
1282
|
+
"""
|
1283
|
+
The element set containing this element.
|
1284
|
+
"""
|
1012
1285
|
return self.task.template.element_sets[self.element_set_idx]
|
1013
1286
|
|
1014
1287
|
@property
|
1015
1288
|
def sequence_idx(self) -> Dict[str, int]:
|
1289
|
+
"""
|
1290
|
+
The sequence index IDs.
|
1291
|
+
"""
|
1016
1292
|
return self._seq_idx
|
1017
1293
|
|
1018
1294
|
@property
|
1019
1295
|
def input_source_idx(self) -> Dict[str, int]:
|
1296
|
+
"""
|
1297
|
+
The input source indices.
|
1298
|
+
"""
|
1020
1299
|
return self._src_idx
|
1021
1300
|
|
1022
1301
|
@property
|
1023
1302
|
def input_sources(self) -> Dict[str, app.InputSource]:
|
1303
|
+
"""
|
1304
|
+
The sources of the inputs to this element.
|
1305
|
+
"""
|
1024
1306
|
return {
|
1025
1307
|
k: self.element_set.input_sources[k.split("inputs.")[1]][v]
|
1026
1308
|
for k, v in self.input_source_idx.items()
|
@@ -1028,15 +1310,24 @@ class Element:
|
|
1028
1310
|
|
1029
1311
|
@property
|
1030
1312
|
def workflow(self) -> app.Workflow:
|
1313
|
+
"""
|
1314
|
+
The workflow containing this element.
|
1315
|
+
"""
|
1031
1316
|
return self.task.workflow
|
1032
1317
|
|
1033
1318
|
@property
|
1034
1319
|
def iteration_IDs(self) -> List[int]:
|
1320
|
+
"""
|
1321
|
+
The IDs of the iterations of this element.
|
1322
|
+
"""
|
1035
1323
|
return self._iteration_IDs
|
1036
1324
|
|
1037
1325
|
@property
|
1038
1326
|
@TimeIt.decorator
|
1039
|
-
def iterations(self) ->
|
1327
|
+
def iterations(self) -> List[app.ElementIteration]:
|
1328
|
+
"""
|
1329
|
+
The iterations of this element.
|
1330
|
+
"""
|
1040
1331
|
# TODO: fix this
|
1041
1332
|
if self._iteration_objs is None:
|
1042
1333
|
self._iteration_objs = [
|
@@ -1051,43 +1342,72 @@ class Element:
|
|
1051
1342
|
|
1052
1343
|
@property
|
1053
1344
|
def dir_name(self):
|
1345
|
+
"""
|
1346
|
+
The name of the directory for containing temporary files for this element.
|
1347
|
+
"""
|
1054
1348
|
return f"e_{self.index}"
|
1055
1349
|
|
1056
1350
|
@property
|
1057
1351
|
def latest_iteration(self):
|
1352
|
+
"""
|
1353
|
+
The most recent iteration of this element.
|
1354
|
+
"""
|
1058
1355
|
return self.iterations[-1]
|
1059
1356
|
|
1060
1357
|
@property
|
1061
1358
|
def inputs(self) -> app.ElementInputs:
|
1359
|
+
"""
|
1360
|
+
The inputs to this element (or its most recent iteration).
|
1361
|
+
"""
|
1062
1362
|
return self.latest_iteration.inputs
|
1063
1363
|
|
1064
1364
|
@property
|
1065
1365
|
def outputs(self) -> app.ElementOutputs:
|
1366
|
+
"""
|
1367
|
+
The outputs from this element (or its most recent iteration).
|
1368
|
+
"""
|
1066
1369
|
return self.latest_iteration.outputs
|
1067
1370
|
|
1068
1371
|
@property
|
1069
1372
|
def input_files(self) -> app.ElementInputFiles:
|
1373
|
+
"""
|
1374
|
+
The input files to this element (or its most recent iteration).
|
1375
|
+
"""
|
1070
1376
|
return self.latest_iteration.input_files
|
1071
1377
|
|
1072
1378
|
@property
|
1073
1379
|
def output_files(self) -> app.ElementOutputFiles:
|
1380
|
+
"""
|
1381
|
+
The output files from this element (or its most recent iteration).
|
1382
|
+
"""
|
1074
1383
|
return self.latest_iteration.output_files
|
1075
1384
|
|
1076
1385
|
@property
|
1077
1386
|
def schema_parameters(self) -> List[str]:
|
1387
|
+
"""
|
1388
|
+
The schema-defined parameters to this element (or its most recent iteration).
|
1389
|
+
"""
|
1078
1390
|
return self.latest_iteration.schema_parameters
|
1079
1391
|
|
1080
1392
|
@property
|
1081
1393
|
def actions(self) -> Dict[app.ElementAction]:
|
1394
|
+
"""
|
1395
|
+
The actions of this element (or its most recent iteration).
|
1396
|
+
"""
|
1082
1397
|
return self.latest_iteration.actions
|
1083
1398
|
|
1084
1399
|
@property
|
1085
1400
|
def action_runs(self) -> List[app.ElementActionRun]:
|
1086
|
-
"""
|
1087
|
-
|
1401
|
+
"""
|
1402
|
+
A list of element action runs from the latest iteration, where only the
|
1403
|
+
final run is taken for each element action.
|
1404
|
+
"""
|
1088
1405
|
return self.latest_iteration.action_runs
|
1089
1406
|
|
1090
1407
|
def init_loop_index(self, loop_name: str):
|
1408
|
+
"""
|
1409
|
+
Initialise the loop index if necessary.
|
1410
|
+
"""
|
1091
1411
|
pass
|
1092
1412
|
|
1093
1413
|
def to_element_set_data(self):
|
@@ -1117,6 +1437,9 @@ class Element:
|
|
1117
1437
|
return inputs, resources
|
1118
1438
|
|
1119
1439
|
def get_sequence_value(self, sequence_path: str) -> Any:
|
1440
|
+
"""
|
1441
|
+
Get the value of a sequence that applies.
|
1442
|
+
"""
|
1120
1443
|
seq = self.element_set.get_sequence_from_path(sequence_path)
|
1121
1444
|
if not seq:
|
1122
1445
|
raise ValueError(
|
@@ -1286,19 +1609,44 @@ class Element:
|
|
1286
1609
|
|
1287
1610
|
@dataclass
|
1288
1611
|
class ElementParameter:
|
1612
|
+
"""
|
1613
|
+
A parameter to an :py:class:`.Element`.
|
1614
|
+
|
1615
|
+
Parameters
|
1616
|
+
----------
|
1617
|
+
task: ~hpcflow.app.WorkflowTask
|
1618
|
+
The task that this is part of.
|
1619
|
+
path: str
|
1620
|
+
The path to this parameter.
|
1621
|
+
parent: Element | ~hpcflow.app.ElementAction | ~hpcflow.app.ElementActionRun | ~hpcflow.app.Parameters
|
1622
|
+
The entity that owns this parameter.
|
1623
|
+
element: Element
|
1624
|
+
The element that this is a parameter of.
|
1625
|
+
"""
|
1626
|
+
|
1289
1627
|
_app_attr = "app"
|
1290
1628
|
|
1629
|
+
#: The task that this is part of.
|
1291
1630
|
task: app.WorkflowTask
|
1631
|
+
#: The path to this parameter.
|
1292
1632
|
path: str
|
1633
|
+
#: The entity that owns this parameter.
|
1293
1634
|
parent: Union[Element, app.ElementAction, app.ElementActionRun, app.Parameters]
|
1635
|
+
#: The element that this is a parameter of.
|
1294
1636
|
element: Element
|
1295
1637
|
|
1296
1638
|
@property
|
1297
1639
|
def data_idx(self):
|
1640
|
+
"""
|
1641
|
+
The data indices associated with this parameter.
|
1642
|
+
"""
|
1298
1643
|
return self.parent.get_data_idx(path=self.path)
|
1299
1644
|
|
1300
1645
|
@property
|
1301
1646
|
def value(self) -> Any:
|
1647
|
+
"""
|
1648
|
+
The value of this parameter.
|
1649
|
+
"""
|
1302
1650
|
return self.parent.get(path=self.path)
|
1303
1651
|
|
1304
1652
|
def __repr__(self) -> str:
|
@@ -1312,27 +1660,49 @@ class ElementParameter:
|
|
1312
1660
|
|
1313
1661
|
@property
|
1314
1662
|
def data_idx_is_set(self):
|
1663
|
+
"""
|
1664
|
+
The associated data indices for which this is set.
|
1665
|
+
"""
|
1315
1666
|
return {
|
1316
1667
|
k: self.task.workflow.is_parameter_set(v) for k, v in self.data_idx.items()
|
1317
1668
|
}
|
1318
1669
|
|
1319
1670
|
@property
|
1320
1671
|
def is_set(self):
|
1672
|
+
"""
|
1673
|
+
Whether this parameter is set.
|
1674
|
+
"""
|
1321
1675
|
return all(self.data_idx_is_set.values())
|
1322
1676
|
|
1323
1677
|
def get_size(self, **store_kwargs):
|
1678
|
+
"""
|
1679
|
+
Get the size of the parameter.
|
1680
|
+
"""
|
1324
1681
|
raise NotImplementedError
|
1325
1682
|
|
1326
1683
|
|
1327
1684
|
@dataclass
|
1328
1685
|
class ElementFilter(JSONLike):
|
1686
|
+
"""
|
1687
|
+
A filter for iterations.
|
1688
|
+
|
1689
|
+
Parameters
|
1690
|
+
----------
|
1691
|
+
rules: list[~hpcflow.app.Rule]
|
1692
|
+
The filtering rules to use.
|
1693
|
+
"""
|
1694
|
+
|
1329
1695
|
_child_objects = (ChildObjectSpec(name="rules", is_multiple=True, class_name="Rule"),)
|
1330
1696
|
|
1697
|
+
#: The filtering rules to use.
|
1331
1698
|
rules: List[app.Rule] = field(default_factory=list)
|
1332
1699
|
|
1333
1700
|
def filter(
|
1334
1701
|
self, element_iters: List[app.ElementIteration]
|
1335
1702
|
) -> List[app.ElementIteration]:
|
1703
|
+
"""
|
1704
|
+
Apply the filter rules to select a subsequence of iterations.
|
1705
|
+
"""
|
1336
1706
|
out = []
|
1337
1707
|
for i in element_iters:
|
1338
1708
|
if all(rule_j.test(i) for rule_j in self.rules):
|
@@ -1342,8 +1712,24 @@ class ElementFilter(JSONLike):
|
|
1342
1712
|
|
1343
1713
|
@dataclass
|
1344
1714
|
class ElementGroup(JSONLike):
|
1715
|
+
"""
|
1716
|
+
A grouping rule for element iterations.
|
1717
|
+
|
1718
|
+
Parameters
|
1719
|
+
----------
|
1720
|
+
name:
|
1721
|
+
The name of the grouping rule.
|
1722
|
+
where:
|
1723
|
+
A filtering rule to select which iterations to use in the group.
|
1724
|
+
group_by_distinct:
|
1725
|
+
If specified, the name of the property to group iterations by.
|
1726
|
+
"""
|
1727
|
+
|
1728
|
+
#: The name of the grouping rule.
|
1345
1729
|
name: str
|
1730
|
+
#: A filtering rule to select which iterations to use in the group.
|
1346
1731
|
where: Optional[ElementFilter] = None
|
1732
|
+
#: If specified, the name of the property to group iterations by.
|
1347
1733
|
group_by_distinct: Optional[app.ParameterPath] = None
|
1348
1734
|
|
1349
1735
|
def __post_init__(self):
|
@@ -1352,5 +1738,18 @@ class ElementGroup(JSONLike):
|
|
1352
1738
|
|
1353
1739
|
@dataclass
|
1354
1740
|
class ElementRepeats:
|
1741
|
+
"""
|
1742
|
+
A repetition rule.
|
1743
|
+
|
1744
|
+
Parameters
|
1745
|
+
----------
|
1746
|
+
number:
|
1747
|
+
The number of times to repeat.
|
1748
|
+
where:
|
1749
|
+
A filtering rule for what to repeat.
|
1750
|
+
"""
|
1751
|
+
|
1752
|
+
#: The number of times to repeat.
|
1355
1753
|
number: int
|
1754
|
+
#: A filtering rule for what to repeat.
|
1356
1755
|
where: Optional[ElementFilter] = None
|