idmtools-platform-comps 0.0.0.dev0__py3-none-any.whl → 0.0.2__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.
- idmtools_platform_comps/__init__.py +25 -8
- idmtools_platform_comps/cli/__init__.py +4 -0
- idmtools_platform_comps/cli/cli_functions.py +50 -0
- idmtools_platform_comps/cli/comps.py +492 -0
- idmtools_platform_comps/comps_cli.py +48 -0
- idmtools_platform_comps/comps_operations/__init__.py +6 -0
- idmtools_platform_comps/comps_operations/asset_collection_operations.py +263 -0
- idmtools_platform_comps/comps_operations/experiment_operations.py +569 -0
- idmtools_platform_comps/comps_operations/simulation_operations.py +678 -0
- idmtools_platform_comps/comps_operations/suite_operations.py +228 -0
- idmtools_platform_comps/comps_operations/workflow_item_operations.py +269 -0
- idmtools_platform_comps/comps_platform.py +309 -0
- idmtools_platform_comps/plugin_info.py +168 -0
- idmtools_platform_comps/ssmt_operations/__init__.py +6 -0
- idmtools_platform_comps/ssmt_operations/simulation_operations.py +77 -0
- idmtools_platform_comps/ssmt_operations/workflow_item_operations.py +73 -0
- idmtools_platform_comps/ssmt_platform.py +44 -0
- idmtools_platform_comps/ssmt_work_items/__init__.py +4 -0
- idmtools_platform_comps/ssmt_work_items/comps_work_order_task.py +29 -0
- idmtools_platform_comps/ssmt_work_items/comps_workitems.py +113 -0
- idmtools_platform_comps/ssmt_work_items/icomps_workflowitem.py +71 -0
- idmtools_platform_comps/ssmt_work_items/work_order.py +54 -0
- idmtools_platform_comps/utils/__init__.py +4 -0
- idmtools_platform_comps/utils/assetize_output/__init__.py +4 -0
- idmtools_platform_comps/utils/assetize_output/assetize_output.py +125 -0
- idmtools_platform_comps/utils/assetize_output/assetize_ssmt_script.py +144 -0
- idmtools_platform_comps/utils/base_singularity_work_order.json +6 -0
- idmtools_platform_comps/utils/download/__init__.py +4 -0
- idmtools_platform_comps/utils/download/download.py +178 -0
- idmtools_platform_comps/utils/download/download_ssmt.py +81 -0
- idmtools_platform_comps/utils/download_experiment.py +116 -0
- idmtools_platform_comps/utils/file_filter_workitem.py +519 -0
- idmtools_platform_comps/utils/general.py +358 -0
- idmtools_platform_comps/utils/linux_mounts.py +73 -0
- idmtools_platform_comps/utils/lookups.py +123 -0
- idmtools_platform_comps/utils/package_version.py +489 -0
- idmtools_platform_comps/utils/python_requirements_ac/__init__.py +4 -0
- idmtools_platform_comps/utils/python_requirements_ac/create_asset_collection.py +155 -0
- idmtools_platform_comps/utils/python_requirements_ac/install_requirements.py +109 -0
- idmtools_platform_comps/utils/python_requirements_ac/requirements_to_asset_collection.py +374 -0
- idmtools_platform_comps/utils/python_version.py +40 -0
- idmtools_platform_comps/utils/scheduling.py +154 -0
- idmtools_platform_comps/utils/singularity_build.py +491 -0
- idmtools_platform_comps/utils/spatial_output.py +76 -0
- idmtools_platform_comps/utils/ssmt_utils/__init__.py +6 -0
- idmtools_platform_comps/utils/ssmt_utils/common.py +70 -0
- idmtools_platform_comps/utils/ssmt_utils/file_filter.py +568 -0
- idmtools_platform_comps/utils/sweeping.py +162 -0
- idmtools_platform_comps-0.0.2.dist-info/METADATA +100 -0
- idmtools_platform_comps-0.0.2.dist-info/RECORD +62 -0
- idmtools_platform_comps-0.0.2.dist-info/entry_points.txt +9 -0
- idmtools_platform_comps-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
- {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.2.dist-info}/top_level.txt +1 -0
- ssmt_image/Dockerfile +52 -0
- ssmt_image/Makefile +21 -0
- ssmt_image/__init__.py +6 -0
- ssmt_image/bootstrap.sh +30 -0
- ssmt_image/build_docker_image.py +161 -0
- ssmt_image/pip.conf +3 -0
- ssmt_image/push_docker_image.py +49 -0
- ssmt_image/requirements.txt +9 -0
- idmtools_platform_comps-0.0.0.dev0.dist-info/METADATA +0 -41
- idmtools_platform_comps-0.0.0.dev0.dist-info/RECORD +0 -5
- {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""idmtools COMPSPlatform.
|
|
2
|
+
|
|
3
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
4
|
+
"""
|
|
5
|
+
# flake8: noqa E402
|
|
6
|
+
import copy
|
|
7
|
+
import logging
|
|
8
|
+
from idmtools.entities import Suite
|
|
9
|
+
from idmtools.entities.experiment import Experiment
|
|
10
|
+
from idmtools.entities.simulation import Simulation
|
|
11
|
+
|
|
12
|
+
HANDLERS = copy.copy(logging.getLogger().handlers)
|
|
13
|
+
LEVEL = logging.getLogger().level
|
|
14
|
+
from COMPS import Client
|
|
15
|
+
|
|
16
|
+
comps_logger = logging.getLogger('COMPS')
|
|
17
|
+
logging.root.handlers = HANDLERS
|
|
18
|
+
logging.getLogger().setLevel(LEVEL)
|
|
19
|
+
comps_logger.propagate = False
|
|
20
|
+
comps_logger.handlers = [h for h in comps_logger.handlers if isinstance(h, logging.FileHandler)]
|
|
21
|
+
from COMPS.Data import Simulation as COMPSSimulation
|
|
22
|
+
from COMPS.Data import WorkItem as COMPSWorkItem
|
|
23
|
+
from COMPS.Data import AssetCollection as COMPSAssetCollection
|
|
24
|
+
from COMPS.Data import Experiment as COMPSExperiment
|
|
25
|
+
from COMPS.Data import Suite as COMPSSuite
|
|
26
|
+
from COMPS.Data.Simulation import SimulationState
|
|
27
|
+
from COMPS.Data.WorkItem import WorkItemState
|
|
28
|
+
from idmtools.assets.asset_collection import AssetCollection
|
|
29
|
+
from idmtools.core.interfaces.ientity import IEntity
|
|
30
|
+
from idmtools.entities.iplatform_default import AnalyzerManagerPlatformDefault, IPlatformDefault
|
|
31
|
+
from idmtools.entities.iworkflow_item import IWorkflowItem
|
|
32
|
+
from dataclasses import dataclass, field
|
|
33
|
+
from typing import Union, Dict, Set
|
|
34
|
+
from functools import partial
|
|
35
|
+
from typing import List
|
|
36
|
+
from enum import Enum
|
|
37
|
+
from idmtools.core import CacheEnabled, ItemType, EntityStatus
|
|
38
|
+
from idmtools.entities.iplatform import IPlatform
|
|
39
|
+
from idmtools.entities.platform_requirements import PlatformRequirements
|
|
40
|
+
from idmtools_platform_comps.comps_operations.asset_collection_operations import CompsPlatformAssetCollectionOperations
|
|
41
|
+
from idmtools_platform_comps.comps_operations.experiment_operations import CompsPlatformExperimentOperations
|
|
42
|
+
from idmtools_platform_comps.comps_operations.simulation_operations import CompsPlatformSimulationOperations
|
|
43
|
+
from idmtools_platform_comps.comps_operations.suite_operations import CompsPlatformSuiteOperations
|
|
44
|
+
from idmtools_platform_comps.comps_operations.workflow_item_operations import CompsPlatformWorkflowItemOperations
|
|
45
|
+
from idmtools_platform_comps.cli.cli_functions import environment_list, validate_range
|
|
46
|
+
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class COMPSPriority(Enum):
|
|
51
|
+
Lowest = "Lowest"
|
|
52
|
+
BelowNormal = "BelowNormal"
|
|
53
|
+
Normal = "Normal"
|
|
54
|
+
AboveNormal = "AboveNormal"
|
|
55
|
+
Highest = "Highest"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
op_defaults = dict(default=None, compare=False, metadata=dict(pickle_ignore=True))
|
|
59
|
+
|
|
60
|
+
# We use this to track os. It would be nice to do that in server
|
|
61
|
+
SLURM_ENVS = ['calculon', 'slurmstage', "slurmdev", "nibbler"]
|
|
62
|
+
supported_types = [PlatformRequirements.PYTHON, PlatformRequirements.SHELL, PlatformRequirements.NativeBinary]
|
|
63
|
+
PLATFORM_DEFAULTS = [AnalyzerManagerPlatformDefault(max_workers=24)]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(repr=False)
|
|
67
|
+
class COMPSPlatform(IPlatform, CacheEnabled):
|
|
68
|
+
"""
|
|
69
|
+
Represents the platform allowing to run simulations on COMPS.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
MAX_SUBDIRECTORY_LENGTH = 35 # avoid maxpath issues on COMPS
|
|
73
|
+
|
|
74
|
+
endpoint: str = field(default="https://comps.idmod.org", metadata={"help": "URL of the COMPS endpoint to use"})
|
|
75
|
+
environment: str = field(default="Calculon",
|
|
76
|
+
metadata=dict(help="Name of the COMPS environment to target", callback=environment_list))
|
|
77
|
+
priority: str = field(default=COMPSPriority.Lowest.value,
|
|
78
|
+
metadata=dict(help="Priority of the job", choices=[p.value for p in COMPSPriority]))
|
|
79
|
+
simulation_root: str = field(default="$COMPS_PATH(USER)\\output", metadata=dict(help="Location of the outputs"))
|
|
80
|
+
node_group: str = field(default=None, metadata=dict(help="Node group to target"))
|
|
81
|
+
num_retries: int = field(default=0, metadata=dict(help="How retries if the simulation fails",
|
|
82
|
+
validate=partial(validate_range, min=0, max=10)))
|
|
83
|
+
num_cores: int = field(default=1, metadata=dict(help="How many cores per simulation",
|
|
84
|
+
validate=partial(validate_range, min=1, max=32)))
|
|
85
|
+
max_workers: int = field(default=16, metadata=dict(help="How many processes to spawn locally",
|
|
86
|
+
validate=partial(validate_range, min=1, max=32)))
|
|
87
|
+
batch_size: int = field(default=10, metadata=dict(help="How many simulations per batch",
|
|
88
|
+
validate=partial(validate_range, min=1, max=100)))
|
|
89
|
+
min_time_between_commissions: int = field(default=15, metadata=dict(
|
|
90
|
+
help="How many seconds between commission calls on an experiment",
|
|
91
|
+
validate=partial(validate_range, min=10, max=300)))
|
|
92
|
+
exclusive: bool = field(default=False,
|
|
93
|
+
metadata=dict(help="Enable exclusive mode? (one simulation per node on the cluster)"))
|
|
94
|
+
docker_image: str = field(default=None, metadata={"help": "Docker image to use for simulations"})
|
|
95
|
+
|
|
96
|
+
_platform_supports: List[PlatformRequirements] = field(default_factory=lambda: copy.deepcopy(supported_types),
|
|
97
|
+
repr=False, init=False)
|
|
98
|
+
_platform_defaults: List[IPlatformDefault] = field(default_factory=lambda: copy.deepcopy(PLATFORM_DEFAULTS))
|
|
99
|
+
|
|
100
|
+
_experiments: CompsPlatformExperimentOperations = field(**op_defaults, repr=False, init=False)
|
|
101
|
+
_simulations: CompsPlatformSimulationOperations = field(**op_defaults, repr=False, init=False)
|
|
102
|
+
_suites: CompsPlatformSuiteOperations = field(**op_defaults, repr=False, init=False)
|
|
103
|
+
_workflow_items: CompsPlatformWorkflowItemOperations = field(**op_defaults, repr=False, init=False)
|
|
104
|
+
_assets: CompsPlatformAssetCollectionOperations = field(**op_defaults, repr=False, init=False)
|
|
105
|
+
_skip_login: bool = field(default=False, repr=False)
|
|
106
|
+
|
|
107
|
+
def __post_init__(self):
|
|
108
|
+
self.__init_interfaces()
|
|
109
|
+
self.supported_types = {ItemType.EXPERIMENT, ItemType.SIMULATION, ItemType.SUITE, ItemType.ASSETCOLLECTION,
|
|
110
|
+
ItemType.WORKFLOW_ITEM}
|
|
111
|
+
super().__post_init__()
|
|
112
|
+
# set platform requirements based on environment
|
|
113
|
+
if self.environment.lower() in SLURM_ENVS:
|
|
114
|
+
self._platform_supports.append(PlatformRequirements.LINUX)
|
|
115
|
+
else:
|
|
116
|
+
self._platform_supports.append(PlatformRequirements.WINDOWS)
|
|
117
|
+
|
|
118
|
+
def __init_interfaces(self):
|
|
119
|
+
if not self._skip_login:
|
|
120
|
+
self._login()
|
|
121
|
+
self._experiments = CompsPlatformExperimentOperations(platform=self)
|
|
122
|
+
self._simulations = CompsPlatformSimulationOperations(platform=self)
|
|
123
|
+
self._suites = CompsPlatformSuiteOperations(platform=self)
|
|
124
|
+
self._workflow_items = CompsPlatformWorkflowItemOperations(platform=self)
|
|
125
|
+
self._assets = CompsPlatformAssetCollectionOperations(platform=self)
|
|
126
|
+
|
|
127
|
+
def _login(self):
|
|
128
|
+
# ensure logging is initialized
|
|
129
|
+
from idmtools.core.logging import exclude_logging_classes
|
|
130
|
+
exclude_logging_classes()
|
|
131
|
+
Client.login(self.endpoint)
|
|
132
|
+
|
|
133
|
+
def post_setstate(self):
|
|
134
|
+
self.__init_interfaces()
|
|
135
|
+
|
|
136
|
+
def get_workitem_link(self, work_item: IWorkflowItem):
|
|
137
|
+
return f"{self.endpoint}/#explore/WorkItems?filters=Id={work_item.uid}"
|
|
138
|
+
|
|
139
|
+
def get_asset_collection_link(self, asset_collection: AssetCollection):
|
|
140
|
+
return f"{self.endpoint}/#explore/AssetCollections?filters=Id={asset_collection.uid}"
|
|
141
|
+
|
|
142
|
+
def get_username(self):
|
|
143
|
+
return Client.auth_manager()._username
|
|
144
|
+
|
|
145
|
+
def is_windows_platform(self, item: IEntity = None) -> bool:
|
|
146
|
+
if isinstance(item, IWorkflowItem):
|
|
147
|
+
return False
|
|
148
|
+
return super().is_windows_platform(item)
|
|
149
|
+
|
|
150
|
+
def validate_item_for_analysis(self, item: object, analyze_failed_items=False):
|
|
151
|
+
"""
|
|
152
|
+
Check if item is valid for analysis.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
item: which item to flatten
|
|
156
|
+
analyze_failed_items: bool
|
|
157
|
+
|
|
158
|
+
Returns: bool
|
|
159
|
+
"""
|
|
160
|
+
result = False
|
|
161
|
+
if isinstance(item, COMPSSimulation):
|
|
162
|
+
if item.state == SimulationState.Succeeded:
|
|
163
|
+
result = True
|
|
164
|
+
else:
|
|
165
|
+
if analyze_failed_items and item.state == SimulationState.Failed:
|
|
166
|
+
result = True
|
|
167
|
+
elif isinstance(item, COMPSWorkItem):
|
|
168
|
+
if item.state == WorkItemState.Succeeded:
|
|
169
|
+
result = True
|
|
170
|
+
else:
|
|
171
|
+
if analyze_failed_items and item.state == WorkItemState.Failed:
|
|
172
|
+
result = True
|
|
173
|
+
elif isinstance(item, (Simulation, IWorkflowItem)):
|
|
174
|
+
if item.succeeded:
|
|
175
|
+
result = True
|
|
176
|
+
else:
|
|
177
|
+
if analyze_failed_items and item.status == EntityStatus.FAILED:
|
|
178
|
+
result = True
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
def get_files(self, item: IEntity, files: Union[Set[str], List[str]], output: str = None, **kwargs) -> \
|
|
183
|
+
Union[Dict[str, Dict[str, bytearray]], Dict[str, bytearray]]:
|
|
184
|
+
"""
|
|
185
|
+
Get files for a platform entity.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
item: Item to fetch files for
|
|
189
|
+
files: List of asset filenames to retrieve from each simulation
|
|
190
|
+
output: save files to
|
|
191
|
+
kwargs: Platform arguments
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
For simulations, this returns a dictionary with filename as key and values being binary data from file or a
|
|
195
|
+
dict.
|
|
196
|
+
|
|
197
|
+
For experiments, this returns a dictionary with key as sim id and then the values as a dict of the
|
|
198
|
+
simulations described above
|
|
199
|
+
"""
|
|
200
|
+
file_data = super().get_files(self._normalized_item_fields(item), files, output, **kwargs)
|
|
201
|
+
return file_data
|
|
202
|
+
|
|
203
|
+
def flatten_item(self, item: object, raw: bool = False, **kwargs) -> List[object]:
|
|
204
|
+
"""
|
|
205
|
+
Flatten an item: resolve the children until getting to the leaves.
|
|
206
|
+
|
|
207
|
+
For example, for an experiment, will return all the simulations.
|
|
208
|
+
For a suite, will return all the simulations contained in the suites experiments.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
item: Which item to flatten
|
|
212
|
+
raw: If True, returns raw platform objects, False, return local objects
|
|
213
|
+
kwargs: Extra parameters for conversion
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
List of leaf items, which can be from either the local platform or a COMPS server:
|
|
217
|
+
- Simulations (either local Simulation or COMPSSimulation),
|
|
218
|
+
- WorkItems (local or COMPSWorkItem),
|
|
219
|
+
- or AssetCollections (local or COMPSAssetCollection).
|
|
220
|
+
"""
|
|
221
|
+
if not isinstance(item, (Simulation, IWorkflowItem, AssetCollection, COMPSSuite, COMPSExperiment,
|
|
222
|
+
COMPSSimulation, COMPSWorkItem, COMPSAssetCollection, Suite, Experiment)):
|
|
223
|
+
raise Exception(f'Item Type: {type(item)} is not supported!')
|
|
224
|
+
|
|
225
|
+
# Return directly if item is already in leaf and raw = False
|
|
226
|
+
if not raw and isinstance(item, (Simulation, IWorkflowItem, AssetCollection)):
|
|
227
|
+
return [item]
|
|
228
|
+
|
|
229
|
+
# Handle platform object conversion if needed
|
|
230
|
+
if not isinstance(item, (COMPSSuite, COMPSExperiment, COMPSSimulation,
|
|
231
|
+
COMPSWorkItem, COMPSAssetCollection)):
|
|
232
|
+
return self.flatten_item(item.get_platform_object(), raw=raw, **kwargs)
|
|
233
|
+
|
|
234
|
+
# Process type COMPSSuite
|
|
235
|
+
if isinstance(item, COMPSSuite):
|
|
236
|
+
children = self._get_children_for_platform_item(item, children=["tags", "configuration"])
|
|
237
|
+
# Handle leaf types
|
|
238
|
+
return [leaf
|
|
239
|
+
for child in children
|
|
240
|
+
for leaf in self.flatten_item(child, raw=raw, **kwargs)]
|
|
241
|
+
# Process type COMPSExperiment
|
|
242
|
+
elif isinstance(item, COMPSExperiment):
|
|
243
|
+
if type(self) is COMPSPlatform:
|
|
244
|
+
children = self._get_children_for_platform_item(item, children=["tags", "configuration"])
|
|
245
|
+
else:
|
|
246
|
+
children = self._get_children_for_platform_item(item,
|
|
247
|
+
children=["tags", "configuration", "hpc_jobs"])
|
|
248
|
+
# Assign server experiment to child.experiment to avoid recreating child's parent
|
|
249
|
+
item = self._normalized_item_fields(item)
|
|
250
|
+
for child in children:
|
|
251
|
+
child.experiment = item
|
|
252
|
+
# Handle leaf types
|
|
253
|
+
return [leaf
|
|
254
|
+
for child in children
|
|
255
|
+
for leaf in self.flatten_item(child, raw=raw, **kwargs)]
|
|
256
|
+
|
|
257
|
+
# Handle leaf types
|
|
258
|
+
elif isinstance(item, (COMPSSimulation, COMPSWorkItem, COMPSAssetCollection)):
|
|
259
|
+
item = self._normalized_item_fields(item)
|
|
260
|
+
|
|
261
|
+
if not raw:
|
|
262
|
+
parent = item.experiment if isinstance(item, COMPSSimulation) else None
|
|
263
|
+
item = self._convert_platform_item_to_entity(item, parent=parent, **kwargs)
|
|
264
|
+
|
|
265
|
+
return [item]
|
|
266
|
+
|
|
267
|
+
def _ensure_simulation_experiment(self, simulation: COMPSSimulation) -> None:
|
|
268
|
+
"""
|
|
269
|
+
Ensure the given simulation has a valid experiment attached.
|
|
270
|
+
|
|
271
|
+
If the simulation's 'experiment' attribute is missing or uninitialized,
|
|
272
|
+
fetch the experiment from the server using its ID and normalize it.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
simulation (COMPSSimulation): The simulation object to validate.
|
|
276
|
+
Raises:
|
|
277
|
+
ValueError: If 'experiment_id' is missing or invalid.
|
|
278
|
+
"""
|
|
279
|
+
experiment = getattr(simulation, "experiment", None)
|
|
280
|
+
|
|
281
|
+
if experiment is None or getattr(experiment, "configuration", None) is None:
|
|
282
|
+
if not hasattr(simulation, "experiment_id") or simulation.experiment_id is None:
|
|
283
|
+
raise ValueError("simulation.experiment_id is missing or None; cannot retrieve experiment.")
|
|
284
|
+
|
|
285
|
+
experiment = self.get_item(simulation.experiment_id,
|
|
286
|
+
item_type=ItemType.EXPERIMENT,
|
|
287
|
+
raw=True)
|
|
288
|
+
simulation.experiment = self._normalized_item_fields(experiment)
|
|
289
|
+
|
|
290
|
+
def _normalized_item_fields(self, item):
|
|
291
|
+
"""
|
|
292
|
+
Add extra fields to item.
|
|
293
|
+
Args:
|
|
294
|
+
item: Item (COMPS item)
|
|
295
|
+
"""
|
|
296
|
+
if not isinstance(item, (COMPSSuite, COMPSExperiment, COMPSSimulation,
|
|
297
|
+
COMPSWorkItem, COMPSAssetCollection)):
|
|
298
|
+
item = item.get_platform_object()
|
|
299
|
+
if isinstance(item, COMPSSimulation):
|
|
300
|
+
self._ensure_simulation_experiment(item)
|
|
301
|
+
item.uid = item.id
|
|
302
|
+
item._id = str(item.id)
|
|
303
|
+
if type(item).__name__ == "WorkItem":
|
|
304
|
+
item.item_type = ItemType.WORKFLOW_ITEM
|
|
305
|
+
elif type(item).__name__ == "AssetCollection":
|
|
306
|
+
item.item_type = ItemType.ASSETCOLLECTION
|
|
307
|
+
else:
|
|
308
|
+
item.item_type = ItemType(type(item).__name__)
|
|
309
|
+
return item
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""idmtools comps platform plugin definition.
|
|
2
|
+
|
|
3
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
4
|
+
"""
|
|
5
|
+
from logging import getLogger
|
|
6
|
+
from typing import Type, List, Dict
|
|
7
|
+
from idmtools.registry.platform_specification import example_configuration_impl, get_platform_impl, \
|
|
8
|
+
get_platform_type_impl, PlatformSpecification
|
|
9
|
+
from idmtools.registry.plugin_specification import get_description_impl
|
|
10
|
+
from idmtools_platform_comps.comps_platform import COMPSPlatform
|
|
11
|
+
from idmtools_platform_comps.ssmt_platform import SSMTPlatform
|
|
12
|
+
|
|
13
|
+
COMPS_EXAMPLE_CONFIG = """
|
|
14
|
+
[COMPSPLATFORM]
|
|
15
|
+
endpoint = https://comps2.idmod.org
|
|
16
|
+
environment = Bayesian
|
|
17
|
+
priority = Lowest
|
|
18
|
+
simulation_root = $COMPS_PATH(USER)\\output
|
|
19
|
+
node_group = emod_abcd
|
|
20
|
+
num_retries = 0
|
|
21
|
+
num_cores = 1
|
|
22
|
+
max_workers = 16
|
|
23
|
+
batch_size = 10
|
|
24
|
+
min_time_between_commissions = 20
|
|
25
|
+
exclusive = False
|
|
26
|
+
# Optional config option. It is recommended you only use this in advanced scenarios. Otherwise
|
|
27
|
+
# leave it unset
|
|
28
|
+
docker_image = docker-staging.packages.idmod.org/idmtools/comps_ssmt_worker:1.0.0
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
logger = getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class COMPSPlatformSpecification(PlatformSpecification):
|
|
35
|
+
"""Provide the plugin definition for COMPSPlatform."""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
"""Constructor."""
|
|
39
|
+
logger.debug("Initializing COMPS Specification")
|
|
40
|
+
|
|
41
|
+
@get_description_impl
|
|
42
|
+
def get_description(self) -> str:
|
|
43
|
+
"""Get description."""
|
|
44
|
+
return "Provides access to the COMPS Platform to idmtools"
|
|
45
|
+
|
|
46
|
+
@get_platform_impl
|
|
47
|
+
def get(self, **configuration) -> COMPSPlatform:
|
|
48
|
+
"""Get COMPSPlatform object with configuration."""
|
|
49
|
+
return COMPSPlatform(**configuration)
|
|
50
|
+
|
|
51
|
+
@example_configuration_impl
|
|
52
|
+
def example_configuration(self):
|
|
53
|
+
"""Get example config."""
|
|
54
|
+
return COMPS_EXAMPLE_CONFIG
|
|
55
|
+
|
|
56
|
+
@get_platform_type_impl
|
|
57
|
+
def get_type(self) -> Type[COMPSPlatform]:
|
|
58
|
+
"""Get COMPSPlatform type."""
|
|
59
|
+
return COMPSPlatform
|
|
60
|
+
|
|
61
|
+
def get_example_urls(self) -> List[str]:
|
|
62
|
+
"""Get Comps examples."""
|
|
63
|
+
from idmtools_platform_comps import __version__
|
|
64
|
+
examples = [f'examples/{example}' for example in ['ssmt', 'workitem', 'vistools']]
|
|
65
|
+
return [self.get_version_url(f'v{__version__}', x) for x in examples]
|
|
66
|
+
|
|
67
|
+
def get_version(self) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Returns the version of the plugin.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Plugin Version
|
|
73
|
+
"""
|
|
74
|
+
from idmtools_platform_comps import __version__
|
|
75
|
+
return __version__
|
|
76
|
+
|
|
77
|
+
def get_configuration_aliases(self) -> Dict[str, Dict]:
|
|
78
|
+
"""Provides configuration aliases that exist in COMPS."""
|
|
79
|
+
config_aliases = dict(
|
|
80
|
+
CALCULON=dict(
|
|
81
|
+
endpoint="https://comps.idmod.org",
|
|
82
|
+
environment="Calculon"
|
|
83
|
+
),
|
|
84
|
+
IDMCLOUD=dict(
|
|
85
|
+
endpoint="https://comps.idmod.org",
|
|
86
|
+
environment="IDMcloud"
|
|
87
|
+
),
|
|
88
|
+
NDCLOUD=dict(
|
|
89
|
+
endpoint="https://comps.idmod.org",
|
|
90
|
+
environment="NDcloud"
|
|
91
|
+
),
|
|
92
|
+
BMGF_IPMCLOUD=dict(
|
|
93
|
+
endpoint="https://comps.idmod.org",
|
|
94
|
+
environment="BMGF_IPMcloud"
|
|
95
|
+
),
|
|
96
|
+
QSTART=dict(
|
|
97
|
+
endpoint="https://comps.idmod.org",
|
|
98
|
+
environment="Qstart"
|
|
99
|
+
),
|
|
100
|
+
NIBBLER=dict(
|
|
101
|
+
endpoint="https://comps.idmod.org",
|
|
102
|
+
environment="Nibbler"
|
|
103
|
+
),
|
|
104
|
+
SLURMSTAGE=dict(
|
|
105
|
+
endpoint="https://comps2.idmod.org",
|
|
106
|
+
environment="SlurmStage"
|
|
107
|
+
),
|
|
108
|
+
CUMULUS=dict(
|
|
109
|
+
endpoint="https://comps2.idmod.org",
|
|
110
|
+
environment="Cumulus"
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
config_aliases['SLURM'] = config_aliases['CALCULON']
|
|
114
|
+
config_aliases['SLURM2'] = config_aliases['SLURMSTAGE']
|
|
115
|
+
# Friendly names for dev/staging environments from @clorton
|
|
116
|
+
config_aliases['BOXY'] = config_aliases['SLURMSTAGE']
|
|
117
|
+
return config_aliases
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class SSMTPlatformSpecification(COMPSPlatformSpecification):
|
|
121
|
+
"""Provides the plugic spec for SSMTPlatform."""
|
|
122
|
+
|
|
123
|
+
def __init__(self):
|
|
124
|
+
"""Constructor."""
|
|
125
|
+
logger.debug("Initializing SSMT Specification")
|
|
126
|
+
|
|
127
|
+
@get_description_impl
|
|
128
|
+
def get_description(self) -> str:
|
|
129
|
+
"""Provide description of SSMT plugin."""
|
|
130
|
+
return "Provides access to the SSMT Platform to idmtools"
|
|
131
|
+
|
|
132
|
+
@get_platform_impl
|
|
133
|
+
def get(self, **configuration) -> SSMTPlatform:
|
|
134
|
+
"""Get an instance of SSMTPlatform using the configuration."""
|
|
135
|
+
return SSMTPlatform(**configuration)
|
|
136
|
+
|
|
137
|
+
@example_configuration_impl
|
|
138
|
+
def example_configuration(self):
|
|
139
|
+
"""Get example config."""
|
|
140
|
+
# TODO determine different config and how we handle remotely
|
|
141
|
+
return COMPS_EXAMPLE_CONFIG
|
|
142
|
+
|
|
143
|
+
@get_platform_type_impl
|
|
144
|
+
def get_type(self) -> Type[SSMTPlatform]:
|
|
145
|
+
"""Get SSMT type."""
|
|
146
|
+
return SSMTPlatform
|
|
147
|
+
|
|
148
|
+
def get_example_urls(self) -> List[str]:
|
|
149
|
+
"""Get SSMT example urls."""
|
|
150
|
+
from idmtools_platform_comps import __version__
|
|
151
|
+
examples = [f'examples/{example}' for example in ['ssmt', 'vistools']]
|
|
152
|
+
return [self.get_version_url(f'v{__version__}', x) for x in examples]
|
|
153
|
+
|
|
154
|
+
def get_version(self) -> str:
|
|
155
|
+
"""
|
|
156
|
+
Returns the version of the plugin.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Plugin Version
|
|
160
|
+
"""
|
|
161
|
+
from idmtools_platform_comps import __version__
|
|
162
|
+
return __version__
|
|
163
|
+
|
|
164
|
+
def get_configuration_aliases(self) -> Dict[str, Dict]:
|
|
165
|
+
"""Provides configuration aliases that exist in COMPS."""
|
|
166
|
+
config_aliases = super().get_configuration_aliases()
|
|
167
|
+
ssmt_config_aliases = {f"{a}_SSMT": p for a, p in config_aliases.items()}
|
|
168
|
+
return ssmt_config_aliases
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""idmtools simulation operations for ssmt.
|
|
2
|
+
|
|
3
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
from typing import List, Dict, Optional
|
|
8
|
+
from idmtools.entities.simulation import Simulation
|
|
9
|
+
from idmtools_platform_comps.comps_operations.simulation_operations import CompsPlatformSimulationOperations
|
|
10
|
+
from COMPS.Data.Simulation import Simulation as COMPSSimulation
|
|
11
|
+
from COMPS.Data import QueryCriteria
|
|
12
|
+
from logging import getLogger, DEBUG
|
|
13
|
+
|
|
14
|
+
logger = getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SSMTPlatformSimulationOperations(CompsPlatformSimulationOperations):
|
|
18
|
+
"""
|
|
19
|
+
SSMTPlatformSimulationOperations provides Simulation operations to SSMT.
|
|
20
|
+
|
|
21
|
+
In this case, we only have to redefine get_assets to optimize file usage.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def get(self, simulation_id: UUID, columns: Optional[List[str]] = None, load_children: Optional[List[str]] = None,
|
|
25
|
+
query_criteria: Optional[QueryCriteria] = None, **kwargs) -> COMPSSimulation:
|
|
26
|
+
"""
|
|
27
|
+
Get Simulation from Comps.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
simulation_id: ID
|
|
31
|
+
columns: Optional list of columns to load. Defaults to "id", "name", "experiment_id", "state"
|
|
32
|
+
load_children: Optional children to load. Defaults to "tags", "configuration"
|
|
33
|
+
query_criteria: Optional query_criteria object to use your own custom criteria object
|
|
34
|
+
**kwargs:
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
COMPSSimulation
|
|
38
|
+
"""
|
|
39
|
+
# ensure hpc_jobs is in children
|
|
40
|
+
if load_children:
|
|
41
|
+
load_children.append('hpc_jobs')
|
|
42
|
+
# when query criteria is specified, we need to ensure our desired criteria are followed
|
|
43
|
+
elif query_criteria is not None:
|
|
44
|
+
if 'hpc_jobs' not in query_criteria._children:
|
|
45
|
+
query_criteria._children.append('hpc_jobs')
|
|
46
|
+
else:
|
|
47
|
+
load_children = ['hpc_jobs']
|
|
48
|
+
|
|
49
|
+
return super().get(simulation_id, load_children=load_children, query_criteria=query_criteria)
|
|
50
|
+
|
|
51
|
+
def get_assets(self, simulation: Simulation, files: List[str], **kwargs) -> Dict[str, bytearray]:
|
|
52
|
+
"""
|
|
53
|
+
Get assets for Simulation.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
simulation: Simulation to fetch
|
|
57
|
+
files: Files to get
|
|
58
|
+
**kwargs: Any keyword arguments
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Files fetched
|
|
62
|
+
"""
|
|
63
|
+
files = [f.replace("\\", '/') for f in files]
|
|
64
|
+
working_directory = simulation.hpc_jobs[0].working_directory
|
|
65
|
+
results = dict()
|
|
66
|
+
for file in files:
|
|
67
|
+
full_path = os.path.join(working_directory, file)
|
|
68
|
+
full_path = full_path.replace("\\", '/')
|
|
69
|
+
if not os.path.exists(full_path):
|
|
70
|
+
msg = f"Cannot find the file {file} at {full_path}"
|
|
71
|
+
logger.error(msg)
|
|
72
|
+
raise FileNotFoundError(msg)
|
|
73
|
+
if logger.isEnabledFor(DEBUG):
|
|
74
|
+
logger.debug(full_path)
|
|
75
|
+
with open(full_path, 'rb') as fin:
|
|
76
|
+
results[file] = fin.read()
|
|
77
|
+
return results
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""idmtools workflow item operations for ssmt.
|
|
2
|
+
|
|
3
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
from typing import List, Dict, Optional
|
|
9
|
+
from idmtools.entities.iworkflow_item import IWorkflowItem
|
|
10
|
+
from idmtools_platform_comps.comps_operations.workflow_item_operations import CompsPlatformWorkflowItemOperations
|
|
11
|
+
from COMPS.Data.WorkItem import WorkItem as COMPSWorkItem
|
|
12
|
+
from COMPS.Data import QueryCriteria
|
|
13
|
+
from logging import getLogger, DEBUG
|
|
14
|
+
|
|
15
|
+
logger = getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class SSMTPlatformWorkflowItemOperations(CompsPlatformWorkflowItemOperations):
|
|
20
|
+
"""SSMTPlatformWorkflowItemOperations provides IWorkflowItem actions for SSMT Platform.
|
|
21
|
+
|
|
22
|
+
In IWorkflowItem's case, we just need to change how get_assets works.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def get(self, workflow_item_id: UUID, columns: Optional[List[str]] = None, load_children: Optional[List[str]] = None,
|
|
26
|
+
query_criteria: Optional[QueryCriteria] = None, **kwargs) -> \
|
|
27
|
+
COMPSWorkItem:
|
|
28
|
+
"""
|
|
29
|
+
Get COMPSWorkItem.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
workflow_item_id: Item id
|
|
33
|
+
columns: Optional columns to load. Defaults to "id", "name", "state", "environment_name", "working_directory"
|
|
34
|
+
load_children: Optional list of COMPS Children objects to load. Defaults to "Tags"
|
|
35
|
+
query_criteria: Optional QueryCriteria
|
|
36
|
+
**kwargs:
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
COMPSWorkItem
|
|
40
|
+
"""
|
|
41
|
+
columns = columns or ["id", "name", "state", "environment_name", "working_directory"]
|
|
42
|
+
if "working_directory" not in columns:
|
|
43
|
+
columns.append("working_directory")
|
|
44
|
+
|
|
45
|
+
return super().get(workflow_item_id, columns=columns, load_children=load_children, query_criteria=query_criteria)
|
|
46
|
+
|
|
47
|
+
def get_assets(self, workflow_item: IWorkflowItem, files: List[str], **kwargs) -> Dict[str, bytearray]:
|
|
48
|
+
"""
|
|
49
|
+
Get Assets for workflow_item.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
workflow_item: WorkflowItem
|
|
53
|
+
files: Files to get
|
|
54
|
+
**kwargs:
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Files requested
|
|
58
|
+
"""
|
|
59
|
+
files = [f.replace("\\", '/') for f in files]
|
|
60
|
+
working_directory = workflow_item.working_directory
|
|
61
|
+
results = dict()
|
|
62
|
+
for file in files:
|
|
63
|
+
full_path = os.path.join(working_directory, file)
|
|
64
|
+
full_path = full_path.replace("\\", '/')
|
|
65
|
+
if not os.path.exists(full_path):
|
|
66
|
+
msg = f"Cannot find the file {file} at {full_path}"
|
|
67
|
+
logger.error(msg)
|
|
68
|
+
raise FileNotFoundError(msg)
|
|
69
|
+
if logger.isEnabledFor(DEBUG):
|
|
70
|
+
logger.debug(full_path)
|
|
71
|
+
with open(full_path, 'rb') as fin:
|
|
72
|
+
results[file] = fin.read()
|
|
73
|
+
return results
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""define the ssmt platform.
|
|
2
|
+
|
|
3
|
+
SSMT platform is the same as the COMPS platform but file access is local.
|
|
4
|
+
|
|
5
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
6
|
+
"""
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from idmtools_platform_comps.comps_operations.asset_collection_operations import CompsPlatformAssetCollectionOperations
|
|
9
|
+
from idmtools_platform_comps.comps_operations.experiment_operations import CompsPlatformExperimentOperations
|
|
10
|
+
from idmtools_platform_comps.comps_operations.suite_operations import CompsPlatformSuiteOperations
|
|
11
|
+
from idmtools_platform_comps.comps_platform import COMPSPlatform, op_defaults
|
|
12
|
+
from idmtools_platform_comps.ssmt_operations.simulation_operations import SSMTPlatformSimulationOperations
|
|
13
|
+
from idmtools_platform_comps.ssmt_operations.workflow_item_operations import SSMTPlatformWorkflowItemOperations
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class SSMTPlatform(COMPSPlatform):
|
|
18
|
+
"""
|
|
19
|
+
Represents the platform allowing to run simulations on SSMT.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
_simulations: SSMTPlatformSimulationOperations = field(**op_defaults)
|
|
23
|
+
_workflow_items: SSMTPlatformWorkflowItemOperations = field(**op_defaults)
|
|
24
|
+
|
|
25
|
+
def __post_init__(self):
|
|
26
|
+
"""
|
|
27
|
+
Post method after SSMTPlatform creation.
|
|
28
|
+
|
|
29
|
+
Returns: None
|
|
30
|
+
"""
|
|
31
|
+
super().__post_init__()
|
|
32
|
+
self.__init_interfaces()
|
|
33
|
+
|
|
34
|
+
def __init_interfaces(self):
|
|
35
|
+
"""
|
|
36
|
+
Initialize intefaces.
|
|
37
|
+
|
|
38
|
+
Returns: None
|
|
39
|
+
"""
|
|
40
|
+
self._experiments = CompsPlatformExperimentOperations(platform=self)
|
|
41
|
+
self._simulations = SSMTPlatformSimulationOperations(platform=self)
|
|
42
|
+
self._suites = CompsPlatformSuiteOperations(platform=self)
|
|
43
|
+
self._workflow_items = SSMTPlatformWorkflowItemOperations(platform=self)
|
|
44
|
+
self._assets = CompsPlatformAssetCollectionOperations(platform=self)
|