hpcflow 0.1.9__py3-none-any.whl → 0.2.0a271__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/__init__.py +2 -11
- hpcflow/__pyinstaller/__init__.py +5 -0
- hpcflow/__pyinstaller/hook-hpcflow.py +40 -0
- hpcflow/_version.py +1 -1
- hpcflow/app.py +43 -0
- hpcflow/cli.py +2 -462
- hpcflow/data/demo_data_manifest/__init__.py +3 -0
- hpcflow/data/demo_data_manifest/demo_data_manifest.json +6 -0
- hpcflow/data/jinja_templates/test/test_template.txt +8 -0
- hpcflow/data/programs/hello_world/README.md +1 -0
- hpcflow/data/programs/hello_world/hello_world.c +87 -0
- hpcflow/data/programs/hello_world/linux/hello_world +0 -0
- hpcflow/data/programs/hello_world/macos/hello_world +0 -0
- hpcflow/data/programs/hello_world/win/hello_world.exe +0 -0
- hpcflow/data/scripts/__init__.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +8 -0
- hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +8 -0
- hpcflow/data/scripts/demo_task_1_parse_p3.py +7 -0
- hpcflow/data/scripts/do_nothing.py +2 -0
- hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
- hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/generate_t1_file_01.py +7 -0
- hpcflow/data/scripts/import_future_script.py +7 -0
- hpcflow/data/scripts/input_file_generator_basic.py +3 -0
- hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
- hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +15 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +11 -0
- hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +17 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out.py +14 -0
- hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_json_in_obj.py +12 -0
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_json_out_obj.py +10 -0
- hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +16 -0
- hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
- hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
- hpcflow/data/scripts/output_file_parser_basic.py +3 -0
- hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
- hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/parse_t1_file_01.py +4 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/__init__.py +1 -0
- hpcflow/data/template_components/command_files.yaml +26 -0
- hpcflow/data/template_components/environments.yaml +13 -0
- hpcflow/data/template_components/parameters.yaml +14 -0
- hpcflow/data/template_components/task_schemas.yaml +139 -0
- hpcflow/data/workflows/workflow_1.yaml +5 -0
- hpcflow/examples.ipynb +1037 -0
- hpcflow/sdk/__init__.py +149 -0
- hpcflow/sdk/app.py +4266 -0
- hpcflow/sdk/cli.py +1479 -0
- hpcflow/sdk/cli_common.py +385 -0
- hpcflow/sdk/config/__init__.py +5 -0
- hpcflow/sdk/config/callbacks.py +246 -0
- hpcflow/sdk/config/cli.py +388 -0
- hpcflow/sdk/config/config.py +1410 -0
- hpcflow/sdk/config/config_file.py +501 -0
- hpcflow/sdk/config/errors.py +272 -0
- hpcflow/sdk/config/types.py +150 -0
- hpcflow/sdk/core/__init__.py +38 -0
- hpcflow/sdk/core/actions.py +3857 -0
- hpcflow/sdk/core/app_aware.py +25 -0
- hpcflow/sdk/core/cache.py +224 -0
- hpcflow/sdk/core/command_files.py +814 -0
- hpcflow/sdk/core/commands.py +424 -0
- hpcflow/sdk/core/element.py +2071 -0
- hpcflow/sdk/core/enums.py +221 -0
- hpcflow/sdk/core/environment.py +256 -0
- hpcflow/sdk/core/errors.py +1043 -0
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +809 -0
- hpcflow/sdk/core/loop.py +1320 -0
- hpcflow/sdk/core/loop_cache.py +282 -0
- hpcflow/sdk/core/object_list.py +933 -0
- hpcflow/sdk/core/parameters.py +3371 -0
- hpcflow/sdk/core/rule.py +196 -0
- hpcflow/sdk/core/run_dir_files.py +57 -0
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +3792 -0
- hpcflow/sdk/core/task_schema.py +993 -0
- hpcflow/sdk/core/test_utils.py +538 -0
- hpcflow/sdk/core/types.py +447 -0
- hpcflow/sdk/core/utils.py +1207 -0
- hpcflow/sdk/core/validation.py +87 -0
- hpcflow/sdk/core/values.py +477 -0
- hpcflow/sdk/core/workflow.py +4820 -0
- hpcflow/sdk/core/zarr_io.py +206 -0
- hpcflow/sdk/data/__init__.py +13 -0
- hpcflow/sdk/data/config_file_schema.yaml +34 -0
- hpcflow/sdk/data/config_schema.yaml +260 -0
- hpcflow/sdk/data/environments_spec_schema.yaml +21 -0
- hpcflow/sdk/data/files_spec_schema.yaml +5 -0
- hpcflow/sdk/data/parameters_spec_schema.yaml +7 -0
- hpcflow/sdk/data/task_schema_spec_schema.yaml +3 -0
- hpcflow/sdk/data/workflow_spec_schema.yaml +22 -0
- hpcflow/sdk/demo/__init__.py +3 -0
- hpcflow/sdk/demo/cli.py +242 -0
- hpcflow/sdk/helper/__init__.py +3 -0
- hpcflow/sdk/helper/cli.py +137 -0
- hpcflow/sdk/helper/helper.py +300 -0
- hpcflow/sdk/helper/watcher.py +192 -0
- hpcflow/sdk/log.py +288 -0
- hpcflow/sdk/persistence/__init__.py +18 -0
- hpcflow/sdk/persistence/base.py +2817 -0
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +39 -0
- hpcflow/sdk/persistence/json.py +954 -0
- hpcflow/sdk/persistence/pending.py +948 -0
- hpcflow/sdk/persistence/store_resource.py +203 -0
- hpcflow/sdk/persistence/types.py +309 -0
- hpcflow/sdk/persistence/utils.py +73 -0
- hpcflow/sdk/persistence/zarr.py +2388 -0
- hpcflow/sdk/runtime.py +320 -0
- hpcflow/sdk/submission/__init__.py +3 -0
- hpcflow/sdk/submission/enums.py +70 -0
- hpcflow/sdk/submission/jobscript.py +2379 -0
- hpcflow/sdk/submission/schedulers/__init__.py +281 -0
- hpcflow/sdk/submission/schedulers/direct.py +233 -0
- hpcflow/sdk/submission/schedulers/sge.py +376 -0
- hpcflow/sdk/submission/schedulers/slurm.py +598 -0
- hpcflow/sdk/submission/schedulers/utils.py +25 -0
- hpcflow/sdk/submission/shells/__init__.py +52 -0
- hpcflow/sdk/submission/shells/base.py +229 -0
- hpcflow/sdk/submission/shells/bash.py +504 -0
- hpcflow/sdk/submission/shells/os_version.py +115 -0
- hpcflow/sdk/submission/shells/powershell.py +352 -0
- hpcflow/sdk/submission/submission.py +1402 -0
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +194 -0
- hpcflow/sdk/utils/arrays.py +69 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +31 -0
- hpcflow/sdk/utils/strings.py +69 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +123 -0
- hpcflow/tests/data/__init__.py +0 -0
- hpcflow/tests/data/benchmark_N_elements.yaml +6 -0
- hpcflow/tests/data/benchmark_script_runner.yaml +26 -0
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_1.json +10 -0
- hpcflow/tests/data/workflow_1.yaml +5 -0
- hpcflow/tests/data/workflow_1_slurm.yaml +8 -0
- hpcflow/tests/data/workflow_1_wsl.yaml +8 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +42 -0
- hpcflow/tests/jinja_templates/test_jinja_templates.py +161 -0
- hpcflow/tests/programs/test_programs.py +180 -0
- hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +12 -0
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +14 -0
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +1361 -0
- hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
- hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
- hpcflow/tests/shells/wsl/test_wsl_submission.py +14 -0
- hpcflow/tests/unit/test_action.py +1066 -0
- hpcflow/tests/unit/test_action_rule.py +24 -0
- hpcflow/tests/unit/test_app.py +132 -0
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +172 -0
- hpcflow/tests/unit/test_command.py +377 -0
- hpcflow/tests/unit/test_config.py +195 -0
- hpcflow/tests/unit/test_config_file.py +162 -0
- hpcflow/tests/unit/test_element.py +666 -0
- hpcflow/tests/unit/test_element_iteration.py +88 -0
- hpcflow/tests/unit/test_element_set.py +158 -0
- hpcflow/tests/unit/test_group.py +115 -0
- hpcflow/tests/unit/test_input_source.py +1479 -0
- hpcflow/tests/unit/test_input_value.py +398 -0
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +1247 -0
- hpcflow/tests/unit/test_loop.py +2674 -0
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +259 -0
- hpcflow/tests/unit/test_object_list.py +116 -0
- hpcflow/tests/unit/test_parameter.py +243 -0
- hpcflow/tests/unit/test_persistence.py +664 -0
- hpcflow/tests/unit/test_resources.py +243 -0
- hpcflow/tests/unit/test_run.py +286 -0
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +9 -0
- hpcflow/tests/unit/test_schema_input.py +372 -0
- hpcflow/tests/unit/test_shell.py +129 -0
- hpcflow/tests/unit/test_slurm.py +39 -0
- hpcflow/tests/unit/test_submission.py +502 -0
- hpcflow/tests/unit/test_task.py +2560 -0
- hpcflow/tests/unit/test_task_schema.py +182 -0
- hpcflow/tests/unit/test_utils.py +616 -0
- hpcflow/tests/unit/test_value_sequence.py +549 -0
- hpcflow/tests/unit/test_values.py +91 -0
- hpcflow/tests/unit/test_workflow.py +827 -0
- hpcflow/tests/unit/test_workflow_template.py +186 -0
- hpcflow/tests/unit/utils/test_arrays.py +40 -0
- hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
- hpcflow/tests/unit/utils/test_hashing.py +65 -0
- hpcflow/tests/unit/utils/test_patches.py +5 -0
- hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
- hpcflow/tests/unit/utils/test_strings.py +97 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +355 -0
- hpcflow/tests/workflows/test_run_status.py +198 -0
- hpcflow/tests/workflows/test_skip_downstream.py +696 -0
- hpcflow/tests/workflows/test_submission.py +140 -0
- hpcflow/tests/workflows/test_workflows.py +564 -0
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6794 -0
- hpcflow-0.2.0a271.dist-info/LICENSE +375 -0
- hpcflow-0.2.0a271.dist-info/METADATA +65 -0
- hpcflow-0.2.0a271.dist-info/RECORD +237 -0
- {hpcflow-0.1.9.dist-info → hpcflow-0.2.0a271.dist-info}/WHEEL +4 -5
- hpcflow-0.2.0a271.dist-info/entry_points.txt +6 -0
- hpcflow/api.py +0 -458
- hpcflow/archive/archive.py +0 -308
- hpcflow/archive/cloud/cloud.py +0 -47
- hpcflow/archive/cloud/errors.py +0 -9
- hpcflow/archive/cloud/providers/dropbox.py +0 -432
- hpcflow/archive/errors.py +0 -5
- hpcflow/base_db.py +0 -4
- hpcflow/config.py +0 -232
- hpcflow/copytree.py +0 -66
- hpcflow/data/examples/_config.yml +0 -14
- hpcflow/data/examples/damask/demo/1.run.yml +0 -4
- hpcflow/data/examples/damask/demo/2.process.yml +0 -29
- hpcflow/data/examples/damask/demo/geom.geom +0 -2052
- hpcflow/data/examples/damask/demo/load.load +0 -1
- hpcflow/data/examples/damask/demo/material.config +0 -185
- hpcflow/data/examples/damask/inputs/geom.geom +0 -2052
- hpcflow/data/examples/damask/inputs/load.load +0 -1
- hpcflow/data/examples/damask/inputs/material.config +0 -185
- hpcflow/data/examples/damask/profiles/_variable_lookup.yml +0 -21
- hpcflow/data/examples/damask/profiles/damask.yml +0 -4
- hpcflow/data/examples/damask/profiles/damask_process.yml +0 -8
- hpcflow/data/examples/damask/profiles/damask_run.yml +0 -5
- hpcflow/data/examples/damask/profiles/default.yml +0 -6
- hpcflow/data/examples/thinking.yml +0 -177
- hpcflow/errors.py +0 -2
- hpcflow/init_db.py +0 -37
- hpcflow/models.py +0 -2549
- hpcflow/nesting.py +0 -9
- hpcflow/profiles.py +0 -455
- hpcflow/project.py +0 -81
- hpcflow/scheduler.py +0 -323
- hpcflow/utils.py +0 -103
- hpcflow/validation.py +0 -167
- hpcflow/variables.py +0 -544
- hpcflow-0.1.9.dist-info/METADATA +0 -168
- hpcflow-0.1.9.dist-info/RECORD +0 -45
- hpcflow-0.1.9.dist-info/entry_points.txt +0 -8
- hpcflow-0.1.9.dist-info/top_level.txt +0 -1
- /hpcflow/{archive → data/jinja_templates}/__init__.py +0 -0
- /hpcflow/{archive/cloud → data/programs}/__init__.py +0 -0
- /hpcflow/{archive/cloud/providers → data/workflows}/__init__.py +0 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cache of loop statuses.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
from typing_extensions import Generic, TypeVar
|
|
10
|
+
|
|
11
|
+
from hpcflow.sdk.core.utils import nth_key
|
|
12
|
+
from hpcflow.sdk.log import TimeIt
|
|
13
|
+
from hpcflow.sdk.core.cache import ObjectCache
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Mapping, Sequence
|
|
17
|
+
from typing_extensions import Self
|
|
18
|
+
from ..typing import DataIndex
|
|
19
|
+
from .loop import Loop
|
|
20
|
+
from .task import WorkflowTask
|
|
21
|
+
from .types import DependentDescriptor, ElementDescriptor
|
|
22
|
+
from .workflow import Workflow
|
|
23
|
+
|
|
24
|
+
K = TypeVar("K")
|
|
25
|
+
V = TypeVar("V")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _LoopIndexError(TypeError):
|
|
29
|
+
"""
|
|
30
|
+
A type error special to loop indices.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, loop_index: LoopIndex) -> None:
|
|
34
|
+
super().__init__(
|
|
35
|
+
f"{loop_index.__class__.__name__} does not support item assignment"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LoopIndex(dict[K, V], Generic[K, V]):
|
|
40
|
+
"""
|
|
41
|
+
Hashable dict implementation, suitable for use as a key into
|
|
42
|
+
other dicts. Once used as a key, becomes immutable.
|
|
43
|
+
|
|
44
|
+
Example
|
|
45
|
+
-------
|
|
46
|
+
|
|
47
|
+
>>> h1 = LoopIndex({"apples": 1, "bananas":2})
|
|
48
|
+
>>> h2 = LoopIndex({"bananas": 3, "mangoes": 5})
|
|
49
|
+
>>> h1+h2
|
|
50
|
+
LoopIndex(apples=1, bananas=3, mangoes=5)
|
|
51
|
+
>>> d1 = {}
|
|
52
|
+
>>> d1[h1] = "salad"
|
|
53
|
+
>>> d1[h1]
|
|
54
|
+
'salad'
|
|
55
|
+
>>> d1[h2]
|
|
56
|
+
Traceback (most recent call last):
|
|
57
|
+
...
|
|
58
|
+
KeyError: LoopIndex(bananas=3, mangoes=5)
|
|
59
|
+
|
|
60
|
+
Notes
|
|
61
|
+
-----
|
|
62
|
+
* Based on answers from
|
|
63
|
+
http://stackoverflow.com/questions/1151658/python-hashable-dicts
|
|
64
|
+
* Assumes both keys and values are hashable. True in practice.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self, map: Mapping[K, V] | None = None) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Make an instance from another dictionary.
|
|
70
|
+
This object will be mutable until it is used as a key.
|
|
71
|
+
"""
|
|
72
|
+
super().__init__(map or {})
|
|
73
|
+
self.__hash: int | None = None
|
|
74
|
+
|
|
75
|
+
def __repr__(self):
|
|
76
|
+
return f"""{self.__class__.__name__}({
|
|
77
|
+
', '.join(f'{k!r}={v!r}' for k, v in self.items())
|
|
78
|
+
})"""
|
|
79
|
+
|
|
80
|
+
def __hash__(self):
|
|
81
|
+
if self.__hash is None:
|
|
82
|
+
self.__hash = hash(frozenset(self.items()))
|
|
83
|
+
return self.__hash
|
|
84
|
+
|
|
85
|
+
def _validate_update(self) -> None:
|
|
86
|
+
if self.__hash is not None:
|
|
87
|
+
raise _LoopIndexError(self)
|
|
88
|
+
|
|
89
|
+
def __setitem__(self, key: K, value: V) -> None:
|
|
90
|
+
self._validate_update()
|
|
91
|
+
super().__setitem__(key, value)
|
|
92
|
+
|
|
93
|
+
def __delitem__(self, key: K) -> None:
|
|
94
|
+
self._validate_update()
|
|
95
|
+
super().__delitem__(key)
|
|
96
|
+
|
|
97
|
+
def clear(self) -> None:
|
|
98
|
+
self._validate_update()
|
|
99
|
+
super().clear()
|
|
100
|
+
|
|
101
|
+
def pop(self, *args, **kwargs) -> V:
|
|
102
|
+
self._validate_update()
|
|
103
|
+
return super().pop(*args, **kwargs)
|
|
104
|
+
|
|
105
|
+
def popitem(self) -> tuple[K, V]:
|
|
106
|
+
self._validate_update()
|
|
107
|
+
return super().popitem()
|
|
108
|
+
|
|
109
|
+
def setdefault(self, key: K, default: V) -> V:
|
|
110
|
+
self._validate_update()
|
|
111
|
+
return super().setdefault(key, default)
|
|
112
|
+
|
|
113
|
+
def update(self, *args, **kwargs) -> None:
|
|
114
|
+
self._validate_update()
|
|
115
|
+
super().update(*args, **kwargs)
|
|
116
|
+
|
|
117
|
+
def __add__(self, right: Mapping[K, V]) -> Self:
|
|
118
|
+
result = self.__class__(self)
|
|
119
|
+
result.update(right)
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@dataclass
|
|
124
|
+
class LoopCache:
|
|
125
|
+
"""Class to store a cache for use in :py:meth:`.Workflow.add_empty_loop` and
|
|
126
|
+
:py:meth:`.WorkflowLoop.add_iterations`. Use :py:meth:`build` to get a new instance.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
element_dependents:
|
|
131
|
+
Keys are element IDs, values are dicts whose keys are element IDs that depend on
|
|
132
|
+
the key element ID (via `Element.get_dependent_elements_recursively`), and whose
|
|
133
|
+
values are dicts with keys: `group_names`, which is a tuple of the string group
|
|
134
|
+
names associated with the dependent element's element set.
|
|
135
|
+
elements:
|
|
136
|
+
Keys are element IDs, values are dicts with keys: `input_statuses`,
|
|
137
|
+
`input_sources`, and `task_insert_ID`.
|
|
138
|
+
zeroth_iters:
|
|
139
|
+
Keys are element IDs, values are data associated with the zeroth iteration of that
|
|
140
|
+
element, namely a tuple of iteration ID and `ElementIteration.data_idx`.
|
|
141
|
+
data_idx:
|
|
142
|
+
Keys are element IDs, values are data associated with all iterations of that
|
|
143
|
+
element, namely a dict whose keys are the iteration loop index as a tuple, and
|
|
144
|
+
whose values are data indices via `ElementIteration.get_data_idx()`.
|
|
145
|
+
iterations:
|
|
146
|
+
Keys are iteration IDs, values are tuples of element ID and iteration index within
|
|
147
|
+
that element.
|
|
148
|
+
task_iterations:
|
|
149
|
+
Keys are task insert IDs, values are list of all iteration IDs associated with
|
|
150
|
+
that task.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
#: Keys are element IDs, values are dicts whose keys are element IDs that depend on
|
|
154
|
+
#: the key element ID (via `Element.get_dependent_elements_recursively`), and whose
|
|
155
|
+
#: values are dicts with keys: `group_names`, which is a tuple of the string group
|
|
156
|
+
#: names associated with the dependent element's element set.
|
|
157
|
+
element_dependents: dict[int, dict[int, DependentDescriptor]]
|
|
158
|
+
#: Keys are element IDs, values are dicts with keys: `input_statuses`,
|
|
159
|
+
#: `input_sources`, and `task_insert_ID`.
|
|
160
|
+
elements: dict[int, ElementDescriptor]
|
|
161
|
+
#: Keys are element IDs, values are data associated with the zeroth iteration of that
|
|
162
|
+
#: element, namely a tuple of iteration ID and `ElementIteration.data_idx`.
|
|
163
|
+
zeroth_iters: dict[int, tuple[int, DataIndex]]
|
|
164
|
+
#: Keys are element IDs, values are data associated with all iterations of that
|
|
165
|
+
#: element, namely a dict whose keys are the iteration loop index as a tuple, and
|
|
166
|
+
#: whose values are data indices via `ElementIteration.get_data_idx()`.
|
|
167
|
+
data_idx: dict[int, dict[LoopIndex[str, int], DataIndex]]
|
|
168
|
+
#: Keys are iteration IDs, values are tuples of element ID and iteration index within
|
|
169
|
+
#: that element.
|
|
170
|
+
iterations: dict[int, tuple[int, int]]
|
|
171
|
+
#: Keys are task insert IDs, values are list of all iteration IDs associated with
|
|
172
|
+
#: that task.
|
|
173
|
+
task_iterations: dict[int, list[int]]
|
|
174
|
+
|
|
175
|
+
@TimeIt.decorator
|
|
176
|
+
def get_iter_IDs(self, loop: Loop) -> list[int]:
|
|
177
|
+
"""Retrieve a list of iteration IDs belonging to a given loop."""
|
|
178
|
+
return [
|
|
179
|
+
i_id for t_id in loop.task_insert_IDs for i_id in self.task_iterations[t_id]
|
|
180
|
+
]
|
|
181
|
+
|
|
182
|
+
@TimeIt.decorator
|
|
183
|
+
def get_iter_loop_indices(self, iter_IDs: list[int]) -> Sequence[Mapping[str, int]]:
|
|
184
|
+
"""
|
|
185
|
+
Retrieve the mapping from element to loop index for each given iteration.
|
|
186
|
+
"""
|
|
187
|
+
iter_loop_idx: list[LoopIndex[str, int]] = []
|
|
188
|
+
for id_ in iter_IDs:
|
|
189
|
+
elem_id, idx = self.iterations[id_]
|
|
190
|
+
iter_loop_idx.append(nth_key(self.data_idx[elem_id], idx))
|
|
191
|
+
return iter_loop_idx
|
|
192
|
+
|
|
193
|
+
@TimeIt.decorator
|
|
194
|
+
def update_loop_indices(self, new_loop_name: str, iter_IDs: list[int]) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Set the loop indices for a named loop to the given list of iteration IDs.
|
|
197
|
+
"""
|
|
198
|
+
elem_ids = {e_ids[0] for k, e_ids in self.iterations.items() if k in iter_IDs}
|
|
199
|
+
new_loop_entry = {new_loop_name: 0}
|
|
200
|
+
for id_ in elem_ids:
|
|
201
|
+
self.data_idx[id_] = {
|
|
202
|
+
k + new_loop_entry: v for k, v in self.data_idx[id_].items()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@TimeIt.decorator
|
|
206
|
+
def add_iteration(
|
|
207
|
+
self,
|
|
208
|
+
iter_ID: int,
|
|
209
|
+
task_insert_ID: int,
|
|
210
|
+
element_ID: int,
|
|
211
|
+
loop_idx: LoopIndex[str, int],
|
|
212
|
+
data_idx: DataIndex,
|
|
213
|
+
):
|
|
214
|
+
"""Update the cache to include a newly added iteration."""
|
|
215
|
+
self.task_iterations[task_insert_ID].append(iter_ID)
|
|
216
|
+
new_iter_idx = len(self.data_idx[element_ID])
|
|
217
|
+
self.data_idx[element_ID][loop_idx] = data_idx
|
|
218
|
+
self.iterations[iter_ID] = (element_ID, new_iter_idx)
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
@TimeIt.decorator
|
|
222
|
+
def build(cls, workflow: Workflow, loops: list[Loop] | None = None) -> Self:
|
|
223
|
+
"""Build a cache of data for use in adding loops and iterations."""
|
|
224
|
+
|
|
225
|
+
deps_cache = ObjectCache.build(workflow, dependencies=True, elements=True)
|
|
226
|
+
|
|
227
|
+
loops = [*workflow.template.loops, *(loops or ())]
|
|
228
|
+
task_iIDs = {t_id for loop in loops for t_id in loop.task_insert_IDs}
|
|
229
|
+
tasks: list[WorkflowTask] = [
|
|
230
|
+
workflow.tasks.get(insert_ID=t_id) for t_id in sorted(task_iIDs)
|
|
231
|
+
]
|
|
232
|
+
elem_deps: dict[int, dict[int, DependentDescriptor]] = {}
|
|
233
|
+
|
|
234
|
+
# keys: element IDs, values: dict with keys: tuple(loop_idx), values: data index
|
|
235
|
+
data_idx_cache: dict[int, dict[LoopIndex[str, int], DataIndex]] = {}
|
|
236
|
+
|
|
237
|
+
# keys: iteration IDs, values: tuple of (element ID, integer index into values
|
|
238
|
+
# dict in `data_idx_cache` [accessed via `.keys()[index]`])
|
|
239
|
+
iters: dict[int, tuple[int, int]] = {}
|
|
240
|
+
|
|
241
|
+
# keys: element IDs, values: dict with keys: "input_statues", "input_sources",
|
|
242
|
+
# "task_insert_ID":
|
|
243
|
+
elements: dict[int, ElementDescriptor] = {}
|
|
244
|
+
|
|
245
|
+
zeroth_iters: dict[int, tuple[int, DataIndex]] = {}
|
|
246
|
+
task_iterations = defaultdict(list)
|
|
247
|
+
for task in tasks:
|
|
248
|
+
for elem_id in task.element_IDs:
|
|
249
|
+
element = deps_cache.elements[elem_id]
|
|
250
|
+
inp_statuses = task.template.get_input_statuses(element.element_set)
|
|
251
|
+
elements[element.id_] = {
|
|
252
|
+
"input_statuses": inp_statuses,
|
|
253
|
+
"input_sources": element.input_sources,
|
|
254
|
+
"task_insert_ID": task.insert_ID,
|
|
255
|
+
}
|
|
256
|
+
elem_deps[element.id_] = {
|
|
257
|
+
de_id: {
|
|
258
|
+
"group_names": tuple(
|
|
259
|
+
grp.name
|
|
260
|
+
for grp in deps_cache.elements[de_id].element_set.groups
|
|
261
|
+
),
|
|
262
|
+
}
|
|
263
|
+
for de_id in deps_cache.elem_elem_dependents_rec[element.id_]
|
|
264
|
+
}
|
|
265
|
+
elem_iters: dict[LoopIndex[str, int], DataIndex] = {}
|
|
266
|
+
for idx, iter_i in enumerate(element.iterations):
|
|
267
|
+
if idx == 0:
|
|
268
|
+
zeroth_iters[element.id_] = (iter_i.id_, iter_i.data_idx)
|
|
269
|
+
elem_iters[iter_i.loop_idx] = iter_i.get_data_idx()
|
|
270
|
+
task_iterations[task.insert_ID].append(iter_i.id_)
|
|
271
|
+
iters[iter_i.id_] = (element.id_, idx)
|
|
272
|
+
data_idx_cache[element.id_] = elem_iters
|
|
273
|
+
|
|
274
|
+
task_iterations.default_factory = None
|
|
275
|
+
return cls(
|
|
276
|
+
element_dependents=elem_deps,
|
|
277
|
+
elements=elements,
|
|
278
|
+
zeroth_iters=zeroth_iters,
|
|
279
|
+
data_idx=data_idx_cache,
|
|
280
|
+
iterations=iters,
|
|
281
|
+
task_iterations=task_iterations,
|
|
282
|
+
)
|