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,569 @@
|
|
|
1
|
+
"""idmtools comps experiment operations.
|
|
2
|
+
|
|
3
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
4
|
+
"""
|
|
5
|
+
import copy
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from itertools import tee
|
|
9
|
+
from logging import getLogger, DEBUG
|
|
10
|
+
from typing import List, Dict, Type, Generator, NoReturn, Optional, TYPE_CHECKING
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
from COMPS.Data import Experiment as COMPSExperiment, QueryCriteria, Configuration, Suite as COMPSSuite, \
|
|
13
|
+
Simulation as COMPSSimulation
|
|
14
|
+
from COMPS.Data.Simulation import SimulationState
|
|
15
|
+
from idmtools import IdmConfigParser
|
|
16
|
+
from idmtools.assets import AssetCollection, Asset
|
|
17
|
+
from idmtools.core import ItemType, EntityStatus
|
|
18
|
+
from idmtools.core.experiment_factory import experiment_factory
|
|
19
|
+
from idmtools.core.logging import SUCCESS
|
|
20
|
+
from idmtools.entities import CommandLine
|
|
21
|
+
from idmtools.entities.experiment import Experiment
|
|
22
|
+
from idmtools.entities.iplatform_ops.iplatform_experiment_operations import IPlatformExperimentOperations
|
|
23
|
+
from idmtools.entities.templated_simulation import TemplatedSimulations
|
|
24
|
+
from idmtools.utils.collections import ExperimentParentIterator
|
|
25
|
+
from idmtools.utils.info import get_doc_base_url
|
|
26
|
+
from idmtools.utils.time import timestamp
|
|
27
|
+
from idmtools_platform_comps.utils.general import clean_experiment_name, convert_comps_status
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
30
|
+
from idmtools_platform_comps.comps_platform import COMPSPlatform
|
|
31
|
+
|
|
32
|
+
logger = getLogger(__name__)
|
|
33
|
+
user_logger = getLogger('user')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class CompsPlatformExperimentOperations(IPlatformExperimentOperations):
|
|
38
|
+
"""
|
|
39
|
+
Provides Experiment operations to the COMPSPlatform.
|
|
40
|
+
"""
|
|
41
|
+
platform: 'COMPSPlatform' # noqa F821
|
|
42
|
+
platform_type: Type = field(default=COMPSExperiment)
|
|
43
|
+
|
|
44
|
+
def get(self, experiment_id: UUID, columns: Optional[List[str]] = None, load_children: Optional[List[str]] = None,
|
|
45
|
+
query_criteria: Optional[QueryCriteria] = None, **kwargs) -> COMPSExperiment:
|
|
46
|
+
"""
|
|
47
|
+
Fetch experiments from COMPS.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
experiment_id: Experiment ID
|
|
51
|
+
columns: Optional Columns. If not provided, id, name, and suite_id are fetched
|
|
52
|
+
load_children: Optional Children. If not provided, tags and configuration are specified
|
|
53
|
+
query_criteria: Optional QueryCriteria
|
|
54
|
+
**kwargs:
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
COMPSExperiment with items
|
|
58
|
+
"""
|
|
59
|
+
columns = columns or ["id", "name", "suite_id"]
|
|
60
|
+
comps_children = load_children if load_children is not None else ["tags", "configuration"]
|
|
61
|
+
query_criteria = query_criteria or QueryCriteria().select(columns).select_children(comps_children)
|
|
62
|
+
try:
|
|
63
|
+
result = COMPSExperiment.get(
|
|
64
|
+
id=experiment_id,
|
|
65
|
+
query_criteria=query_criteria
|
|
66
|
+
)
|
|
67
|
+
except AttributeError as e:
|
|
68
|
+
user_logger.error(f"The id {experiment_id} could not be converted to an UUID. Please verify your id")
|
|
69
|
+
raise e
|
|
70
|
+
|
|
71
|
+
return result
|
|
72
|
+
|
|
73
|
+
def pre_create(self, experiment: Experiment, **kwargs) -> NoReturn:
|
|
74
|
+
"""
|
|
75
|
+
Pre-create for Experiment. At moment, validation related to COMPS is all that is done.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
experiment: Experiment to run pre-create for
|
|
79
|
+
**kwargs:
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
None
|
|
83
|
+
"""
|
|
84
|
+
if experiment.name is None:
|
|
85
|
+
raise ValueError("Experiment name is required on COMPS")
|
|
86
|
+
super().pre_create(experiment, **kwargs)
|
|
87
|
+
|
|
88
|
+
def platform_create(self, experiment: Experiment, num_cores: Optional[int] = None,
|
|
89
|
+
executable_path: Optional[str] = None,
|
|
90
|
+
command_arg: Optional[str] = None, priority: Optional[str] = None,
|
|
91
|
+
check_command: bool = True, use_short_path: bool = False, **kwargs) -> COMPSExperiment:
|
|
92
|
+
"""
|
|
93
|
+
Create Experiment on the COMPS Platform.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
experiment: IDMTools Experiment to create
|
|
97
|
+
num_cores: Optional num of cores to allocate using MPI
|
|
98
|
+
executable_path: Executable path
|
|
99
|
+
command_arg: Command Argument
|
|
100
|
+
priority: Priority of command
|
|
101
|
+
check_command: Run task hooks on item
|
|
102
|
+
use_short_path: When set to true, simulation roots will be set to "$COMPS_PATH(USER)
|
|
103
|
+
**kwargs: Keyword arguments used to expand functionality. At moment these are usually not used
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
COMPSExperiment that was created
|
|
107
|
+
"""
|
|
108
|
+
# TODO check experiment task supported
|
|
109
|
+
|
|
110
|
+
# Cleanup the name
|
|
111
|
+
experiment.name = clean_experiment_name(experiment.name)
|
|
112
|
+
|
|
113
|
+
# Define the subdirectory
|
|
114
|
+
subdirectory = experiment.name[0:self.platform.MAX_SUBDIRECTORY_LENGTH] + '_' + timestamp()
|
|
115
|
+
|
|
116
|
+
if use_short_path:
|
|
117
|
+
logger.debug("Setting Simulation Root to $COMPS_PATH(USER)")
|
|
118
|
+
simulation_root = "$COMPS_PATH(USER)"
|
|
119
|
+
subdirectory = 'rac' + '_' + timestamp() # also shorten subdirectory
|
|
120
|
+
else:
|
|
121
|
+
simulation_root = self.platform.simulation_root
|
|
122
|
+
|
|
123
|
+
# Get the experiment command line
|
|
124
|
+
exp_command: CommandLine = self._get_experiment_command_line(check_command, experiment)
|
|
125
|
+
|
|
126
|
+
if command_arg is None and exp_command is not None:
|
|
127
|
+
command_arg = exp_command.arguments + " " + exp_command.options
|
|
128
|
+
|
|
129
|
+
if executable_path is None and exp_command is not None:
|
|
130
|
+
executable_path = exp_command.executable
|
|
131
|
+
|
|
132
|
+
# create initial configuration object
|
|
133
|
+
comps_config = dict(
|
|
134
|
+
environment_name=self.platform.environment,
|
|
135
|
+
simulation_input_args=command_arg.strip() if command_arg is not None else None,
|
|
136
|
+
working_directory_root=os.path.join(simulation_root, subdirectory).replace('\\', '/'),
|
|
137
|
+
executable_path=executable_path,
|
|
138
|
+
node_group_name=self.platform.node_group,
|
|
139
|
+
maximum_number_of_retries=self.platform.num_retries,
|
|
140
|
+
priority=self.platform.priority if priority is None else priority,
|
|
141
|
+
min_cores=self.platform.num_cores if num_cores is None else num_cores,
|
|
142
|
+
max_cores=self.platform.num_cores if num_cores is None else num_cores,
|
|
143
|
+
exclusive=self.platform.exclusive
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if isinstance(experiment.simulations.items, TemplatedSimulations):
|
|
147
|
+
scheduling = getattr(experiment.simulations.items.base_simulation, 'scheduling', False)
|
|
148
|
+
elif isinstance(experiment.simulations.items, List):
|
|
149
|
+
scheduling = getattr(experiment.simulations.items[0], 'scheduling', False)
|
|
150
|
+
else:
|
|
151
|
+
scheduling = False
|
|
152
|
+
|
|
153
|
+
# kwargs.get("scheduling", False) case is for adding work_order file during sweeping call back which is too late
|
|
154
|
+
# to detect the scheduling flag from the simulation
|
|
155
|
+
if scheduling or kwargs.get("scheduling", False):
|
|
156
|
+
import copy
|
|
157
|
+
# save a copy of default config
|
|
158
|
+
setattr(self.platform, 'comps_config', copy.deepcopy(comps_config))
|
|
159
|
+
# clear some not-supported parameters
|
|
160
|
+
comps_config.update(executable_path=None, node_group_name=None, min_cores=None, max_cores=None,
|
|
161
|
+
exclusive=None, simulation_input_args=None)
|
|
162
|
+
|
|
163
|
+
if logger.isEnabledFor(DEBUG):
|
|
164
|
+
logger.debug(f'COMPS Experiment Configs: {str(comps_config)}')
|
|
165
|
+
|
|
166
|
+
config = Configuration(**comps_config)
|
|
167
|
+
|
|
168
|
+
e = COMPSExperiment(name=experiment.name,
|
|
169
|
+
configuration=config,
|
|
170
|
+
suite_id=experiment.parent_id)
|
|
171
|
+
|
|
172
|
+
# Add tags if present
|
|
173
|
+
if experiment.tags:
|
|
174
|
+
e.set_tags(experiment.tags)
|
|
175
|
+
|
|
176
|
+
# Save the experiment
|
|
177
|
+
e.save()
|
|
178
|
+
|
|
179
|
+
# Set the ID back in the object
|
|
180
|
+
experiment.uid = e.id
|
|
181
|
+
|
|
182
|
+
# Send the assets for the experiment
|
|
183
|
+
self.send_assets(experiment)
|
|
184
|
+
return e
|
|
185
|
+
|
|
186
|
+
def platform_modify_experiment(self, experiment: Experiment, regather_common_assets: bool = False,
|
|
187
|
+
**kwargs) -> Experiment:
|
|
188
|
+
"""
|
|
189
|
+
Executed when an Experiment is being ran that is already in Created, Done, In Progress, or Failed State.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
experiment: Experiment to modify
|
|
193
|
+
regather_common_assets: Triggers a new AC to be associated with experiment.
|
|
194
|
+
It is important to note that when using this feature, ensure the previous simulations have finished provisioning.
|
|
195
|
+
Failure to do so can lead to unexpected behaviour.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Modified experiment.
|
|
199
|
+
"""
|
|
200
|
+
if logger.isEnabledFor(DEBUG):
|
|
201
|
+
logger.debug(
|
|
202
|
+
f"Experiment Status: {experiment.status}. "
|
|
203
|
+
f"Modifying experiment: {experiment.id}. "
|
|
204
|
+
f"Asset Editable: {experiment.assets.is_editable()}. "
|
|
205
|
+
f"Regather assets: {regather_common_assets}."
|
|
206
|
+
)
|
|
207
|
+
if experiment.status is not None and experiment.assets.is_editable() and regather_common_assets:
|
|
208
|
+
experiment.pre_creation(self.platform, gather_assets=regather_common_assets)
|
|
209
|
+
self.send_assets(experiment)
|
|
210
|
+
else:
|
|
211
|
+
user_logger.warning(
|
|
212
|
+
f"Not gathering common assets again since experiment exists on platform. "
|
|
213
|
+
f"If you need to add additional common assets, see "
|
|
214
|
+
f"{get_doc_base_url()}cookbook/asset_collections.html#modifying-asset-collection"
|
|
215
|
+
)
|
|
216
|
+
return experiment
|
|
217
|
+
|
|
218
|
+
def _get_experiment_command_line(self, check_command: bool, experiment: Experiment) -> CommandLine:
|
|
219
|
+
"""
|
|
220
|
+
Get the command line for COMPS.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
check_command: Should we run the platform task hooks on comps?
|
|
224
|
+
experiment: Experiment to get command line for
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Command line for Experiment
|
|
228
|
+
"""
|
|
229
|
+
from idmtools_platform_comps.utils.python_version import platform_task_hooks
|
|
230
|
+
|
|
231
|
+
if isinstance(experiment.simulations, Generator):
|
|
232
|
+
if logger.isEnabledFor(DEBUG):
|
|
233
|
+
logger.debug("Simulations generator detected. Copying generator and using first task as command")
|
|
234
|
+
sim_gen1, sim_gen2 = tee(experiment.simulations)
|
|
235
|
+
experiment.simulations = sim_gen2
|
|
236
|
+
sim = next(sim_gen1)
|
|
237
|
+
if check_command:
|
|
238
|
+
task = platform_task_hooks(sim.task, self.platform)
|
|
239
|
+
# run pre-creation in case task use it to produce the command line dynamically
|
|
240
|
+
task.pre_creation(sim, self.platform)
|
|
241
|
+
exp_command = task.command
|
|
242
|
+
elif isinstance(experiment.simulations, ExperimentParentIterator) and isinstance(experiment.simulations.items,
|
|
243
|
+
TemplatedSimulations):
|
|
244
|
+
if logger.isEnabledFor(DEBUG):
|
|
245
|
+
logger.debug("ParentIterator/TemplatedSimulations detected. Using base_task for command")
|
|
246
|
+
from idmtools.entities.simulation import Simulation
|
|
247
|
+
task = experiment.simulations.items.base_task
|
|
248
|
+
if check_command:
|
|
249
|
+
task = platform_task_hooks(task, self.platform)
|
|
250
|
+
# run pre-creation in case task use it to produce the command line dynamically
|
|
251
|
+
task.pre_creation(Simulation(task=task), self.platform)
|
|
252
|
+
exp_command = task.command
|
|
253
|
+
else:
|
|
254
|
+
if logger.isEnabledFor(DEBUG):
|
|
255
|
+
logger.debug("List of simulations detected. Using base_task for command")
|
|
256
|
+
task = experiment.simulations[0].task
|
|
257
|
+
if check_command:
|
|
258
|
+
task = platform_task_hooks(task, self.platform)
|
|
259
|
+
# run pre-creation in case task use it to produce the command line dynamically
|
|
260
|
+
task.pre_creation(experiment.simulations[0], self.platform)
|
|
261
|
+
exp_command = task.command
|
|
262
|
+
return exp_command
|
|
263
|
+
|
|
264
|
+
def post_create(self, experiment: Experiment, **kwargs) -> NoReturn:
|
|
265
|
+
"""
|
|
266
|
+
Post create of experiment.
|
|
267
|
+
|
|
268
|
+
The default behaviour is to display the experiment url if output is enabled.
|
|
269
|
+
"""
|
|
270
|
+
if IdmConfigParser.is_output_enabled():
|
|
271
|
+
user_logger.log(SUCCESS, f"\nThe created experiment can be viewed at {self.platform.endpoint}/#explore/"
|
|
272
|
+
f"Simulations?filters=ExperimentId={experiment.uid}\nSimulations are still being created\n"
|
|
273
|
+
)
|
|
274
|
+
super().post_create(experiment, **kwargs)
|
|
275
|
+
|
|
276
|
+
def post_run_item(self, experiment: Experiment, **kwargs):
|
|
277
|
+
"""
|
|
278
|
+
Ran after experiment. Nothing is done on comps other that alerting the user to the item.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
experiment: Experiment to run post run item
|
|
282
|
+
**kwargs:
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
None
|
|
286
|
+
"""
|
|
287
|
+
super().post_run_item(experiment, **kwargs)
|
|
288
|
+
|
|
289
|
+
def get_children(self, experiment: COMPSExperiment, columns: Optional[List[str]] = None,
|
|
290
|
+
children: Optional[List[str]] = None, **kwargs) -> List[COMPSSimulation]:
|
|
291
|
+
"""
|
|
292
|
+
Get children for a COMPSExperiment.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
experiment: Experiment to get children of Comps Experiment
|
|
296
|
+
columns: Columns to fetch. If not provided, id, name, experiment_id, and state will be loaded
|
|
297
|
+
children: Children to load. If not provided, Tags will be loaded
|
|
298
|
+
**kwargs:
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
Simulations belonging to the Experiment
|
|
302
|
+
"""
|
|
303
|
+
columns = columns or ["id", "name", "experiment_id", "state"]
|
|
304
|
+
children = children if children is not None else ["tags", "configuration", "files"]
|
|
305
|
+
|
|
306
|
+
children = experiment.get_simulations(query_criteria=QueryCriteria().select(columns).select_children(children))
|
|
307
|
+
return children
|
|
308
|
+
|
|
309
|
+
def get_parent(self, experiment: COMPSExperiment, **kwargs) -> COMPSSuite:
|
|
310
|
+
"""
|
|
311
|
+
Get Parent of experiment.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
experiment: Experiment to get parent of
|
|
315
|
+
**kwargs:
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Suite of the experiment
|
|
319
|
+
"""
|
|
320
|
+
if experiment.suite_id is None:
|
|
321
|
+
return None
|
|
322
|
+
return self.platform._suites.get(experiment.suite_id, **kwargs)
|
|
323
|
+
|
|
324
|
+
def platform_run_item(self, experiment: Experiment, **kwargs):
|
|
325
|
+
"""
|
|
326
|
+
Run experiment on COMPS. Here we commission the experiment.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
experiment: Experiment to run
|
|
330
|
+
**kwargs:
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
None
|
|
334
|
+
"""
|
|
335
|
+
if logger.isEnabledFor(DEBUG):
|
|
336
|
+
logger.debug(f'Commissioning experiment: {experiment.uid}')
|
|
337
|
+
# commission only if rules we have items in created or none.
|
|
338
|
+
# TODO add new status to entity status to track commissioned as well instead of raw comps
|
|
339
|
+
if any([s.status in [None, EntityStatus.CREATED] for s in experiment.simulations]) and any(
|
|
340
|
+
[s.get_platform_object().state in [SimulationState.Created] for s in experiment.simulations]):
|
|
341
|
+
po = experiment.get_platform_object()
|
|
342
|
+
po.commission()
|
|
343
|
+
# for now, we update here in the comps objects to reflect the new state
|
|
344
|
+
for sim in experiment.simulations:
|
|
345
|
+
spo = sim.get_platform_object()
|
|
346
|
+
spo._state = SimulationState.CommissionRequested
|
|
347
|
+
|
|
348
|
+
def send_assets(self, experiment: Experiment, **kwargs):
|
|
349
|
+
"""
|
|
350
|
+
Send assets related to the experiment.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
experiment: Experiment to send assets for
|
|
354
|
+
|
|
355
|
+
**kwargs:
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
None
|
|
359
|
+
"""
|
|
360
|
+
if experiment.assets.count == 0:
|
|
361
|
+
logger.warning('Experiment has no assets to send')
|
|
362
|
+
return
|
|
363
|
+
ac = self.platform._assets.create(experiment.assets)
|
|
364
|
+
|
|
365
|
+
if logger.isEnabledFor(DEBUG):
|
|
366
|
+
logger.debug(f'Asset collection for experiment: {experiment.id} is: {ac.id}')
|
|
367
|
+
|
|
368
|
+
# associate the assets with the experiment in COMPS
|
|
369
|
+
e = COMPSExperiment.get(id=experiment.uid)
|
|
370
|
+
e.configuration = Configuration(asset_collection_id=ac.id)
|
|
371
|
+
e.save()
|
|
372
|
+
|
|
373
|
+
def refresh_status(self, experiment: Experiment, **kwargs):
|
|
374
|
+
"""
|
|
375
|
+
Reload status for experiment(load simulations).
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
experiment: Experiment to load status for
|
|
379
|
+
**kwargs:
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
None
|
|
383
|
+
"""
|
|
384
|
+
simulations = self.get_children(experiment.get_platform_object(), force=True, columns=["id", "state"],
|
|
385
|
+
load_children=[])
|
|
386
|
+
for s in simulations:
|
|
387
|
+
experiment.simulations.set_status_for_item(s.id, convert_comps_status(s.state))
|
|
388
|
+
|
|
389
|
+
def to_entity(self, experiment: COMPSExperiment, parent: Optional[COMPSSuite] = None, children: bool = True,
|
|
390
|
+
**kwargs) -> Experiment:
|
|
391
|
+
"""
|
|
392
|
+
Converts a COMPSExperiment to an idmtools Experiment.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
experiment: COMPS Experiment objet to convert
|
|
396
|
+
parent: Optional suite parent
|
|
397
|
+
children: Should we load children objects?
|
|
398
|
+
**kwargs:
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Experiment
|
|
402
|
+
"""
|
|
403
|
+
# Recreate the suite if needed
|
|
404
|
+
if experiment.suite_id is None:
|
|
405
|
+
suite = kwargs.get('suite')
|
|
406
|
+
else:
|
|
407
|
+
if parent:
|
|
408
|
+
suite = parent
|
|
409
|
+
else:
|
|
410
|
+
suite = kwargs.get('suite') or self.platform.get_item(experiment.suite_id, item_type=ItemType.SUITE)
|
|
411
|
+
|
|
412
|
+
# Create an experiment
|
|
413
|
+
obj = experiment_factory.create("idmtools.entities.experiment.Experiment", tags=experiment.tags, name=experiment.name,
|
|
414
|
+
fallback=Experiment)
|
|
415
|
+
obj.platform = self.platform
|
|
416
|
+
obj._platform_object = experiment
|
|
417
|
+
# Set parent
|
|
418
|
+
obj.parent = suite
|
|
419
|
+
# Set the correct attributes
|
|
420
|
+
obj.uid = experiment.id
|
|
421
|
+
obj.task_type = experiment.tags.get("task_type") if experiment.tags is not None else ""
|
|
422
|
+
# load assets first so children can access during their load
|
|
423
|
+
obj.assets = self.get_assets_from_comps_experiment(experiment)
|
|
424
|
+
if obj.assets is None:
|
|
425
|
+
obj.assets = AssetCollection()
|
|
426
|
+
|
|
427
|
+
# if we are loading the children, convert them
|
|
428
|
+
if children:
|
|
429
|
+
# Convert all simulations
|
|
430
|
+
comps_sims = experiment.get_simulations(
|
|
431
|
+
QueryCriteria().select(
|
|
432
|
+
["id", "name", "experiment_id", "state"]
|
|
433
|
+
).select_children(
|
|
434
|
+
["tags", "files", "configuration"]
|
|
435
|
+
)
|
|
436
|
+
)
|
|
437
|
+
obj.simulations = []
|
|
438
|
+
for s in comps_sims:
|
|
439
|
+
obj.simulations.append(
|
|
440
|
+
self.platform._simulations.to_entity(s, parent=obj, **kwargs)
|
|
441
|
+
)
|
|
442
|
+
return obj
|
|
443
|
+
|
|
444
|
+
def get_assets_from_comps_experiment(self, experiment: COMPSExperiment) -> Optional[AssetCollection]:
|
|
445
|
+
"""
|
|
446
|
+
Get assets for a comps experiment.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
experiment: Experiment to get asset collection for.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
AssetCollection if configuration is set and configuration.asset_collection_id is set.
|
|
453
|
+
"""
|
|
454
|
+
if experiment.configuration and experiment.configuration.asset_collection_id:
|
|
455
|
+
return self.platform.get_item(experiment.configuration.asset_collection_id, ItemType.ASSETCOLLECTION)
|
|
456
|
+
return None
|
|
457
|
+
|
|
458
|
+
def platform_list_asset(self, experiment: Experiment, **kwargs) -> List[Asset]:
|
|
459
|
+
"""
|
|
460
|
+
List assets for an experiment.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
experiment: Experiment to list assets for.
|
|
464
|
+
**kwargs:
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
List of assets
|
|
468
|
+
"""
|
|
469
|
+
assets = []
|
|
470
|
+
if experiment.assets is None:
|
|
471
|
+
po: COMPSExperiment = experiment.get_platform_object()
|
|
472
|
+
ac = self.get_assets_from_comps_experiment(po)
|
|
473
|
+
if ac:
|
|
474
|
+
assets = ac.assets
|
|
475
|
+
else:
|
|
476
|
+
assets = copy.deepcopy(experiment.assets.assets)
|
|
477
|
+
return assets
|
|
478
|
+
|
|
479
|
+
def create_sim_directory_map(self, experiment_id: str) -> Dict:
|
|
480
|
+
"""
|
|
481
|
+
Build simulation working directory mapping.
|
|
482
|
+
Args:
|
|
483
|
+
experiment_id: experiment id
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
Dict of simulation id as key and working dir as value
|
|
487
|
+
"""
|
|
488
|
+
from idmtools_platform_comps.utils.linux_mounts import set_linux_mounts, clear_linux_mounts
|
|
489
|
+
set_linux_mounts(self.platform)
|
|
490
|
+
comps_exp = self.platform.get_item(experiment_id, ItemType.EXPERIMENT, raw=True, force=True)
|
|
491
|
+
comps_sims = comps_exp.get_simulations(QueryCriteria().select(['id', 'state']).select_children('hpc_jobs'))
|
|
492
|
+
sim_map = {str(sim.id): sim.hpc_jobs[-1].working_directory for sim in comps_sims if sim.hpc_jobs}
|
|
493
|
+
clear_linux_mounts(self.platform)
|
|
494
|
+
return sim_map
|
|
495
|
+
|
|
496
|
+
def platform_delete(self, experiment_id: str) -> None:
|
|
497
|
+
"""
|
|
498
|
+
Delete platform experiment.
|
|
499
|
+
Args:
|
|
500
|
+
experiment_id: experiment id
|
|
501
|
+
Returns:
|
|
502
|
+
None
|
|
503
|
+
"""
|
|
504
|
+
comps_exp = self.platform.get_item(experiment_id, ItemType.EXPERIMENT, raw=True)
|
|
505
|
+
try:
|
|
506
|
+
comps_exp.delete()
|
|
507
|
+
except RuntimeError:
|
|
508
|
+
logger.info(f"Could not delete the experiment ({comps_exp.id})...")
|
|
509
|
+
return
|
|
510
|
+
|
|
511
|
+
def platform_cancel(self, experiment_id: str) -> None:
|
|
512
|
+
"""
|
|
513
|
+
Cancel platform experiment.
|
|
514
|
+
Args:
|
|
515
|
+
experiment_id: experiment id
|
|
516
|
+
Returns:
|
|
517
|
+
None
|
|
518
|
+
"""
|
|
519
|
+
|
|
520
|
+
def experiment_is_running(comps_exp):
|
|
521
|
+
from COMPS.Data.Simulation import SimulationState
|
|
522
|
+
for sim in comps_exp.get_simulations():
|
|
523
|
+
if sim.state not in (SimulationState.Succeeded, SimulationState.Failed,
|
|
524
|
+
SimulationState.Canceled, SimulationState.Created,
|
|
525
|
+
SimulationState.CancelRequested):
|
|
526
|
+
return True
|
|
527
|
+
return False
|
|
528
|
+
|
|
529
|
+
comps_experiment = self.platform.get_item(experiment_id, ItemType.EXPERIMENT, raw=True)
|
|
530
|
+
if comps_experiment and experiment_is_running(comps_experiment):
|
|
531
|
+
comps_experiment.cancel()
|
|
532
|
+
|
|
533
|
+
def get_assets(self, experiment: Experiment, files: List[str], include_experiment_assets: bool = True, **kwargs) -> Dict[str, bytearray]:
|
|
534
|
+
"""
|
|
535
|
+
Fetch the files associated with an experiment and its simulations.
|
|
536
|
+
|
|
537
|
+
Args:
|
|
538
|
+
experiment (Experiment): The experiment object.
|
|
539
|
+
files (List[str]): A list of filenames to retrieve.
|
|
540
|
+
include_experiment_assets (bool): Whether to include experiment-level assets. Defaults to True.
|
|
541
|
+
**kwargs: Additional platform-specific options.
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
Dict[str, Dict[str, Dict[str, Union[str, bytearray]]]]: A nested dictionary in the format::
|
|
545
|
+
|
|
546
|
+
{
|
|
547
|
+
"experiment_id": {
|
|
548
|
+
"simulation_id": {
|
|
549
|
+
"filename1": file_content,
|
|
550
|
+
"filename2": file_content,
|
|
551
|
+
...
|
|
552
|
+
},
|
|
553
|
+
...
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
File content may be either a decoded string or a bytearray.
|
|
558
|
+
"""
|
|
559
|
+
ret = dict()
|
|
560
|
+
if isinstance(experiment, COMPSExperiment):
|
|
561
|
+
comps_exp = experiment
|
|
562
|
+
else:
|
|
563
|
+
comps_exp = experiment.get_platform_object()
|
|
564
|
+
simulations = self.platform.flatten_item(comps_exp, raw=True)
|
|
565
|
+
for sim in simulations:
|
|
566
|
+
ret[sim.id] = self.platform._simulations.get_assets(sim, files,
|
|
567
|
+
include_experiment_assets=include_experiment_assets,
|
|
568
|
+
**kwargs)
|
|
569
|
+
return ret
|