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,678 @@
|
|
|
1
|
+
"""idmtools comps simulation operations.
|
|
2
|
+
|
|
3
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
4
|
+
"""
|
|
5
|
+
from concurrent.futures.thread import ThreadPoolExecutor
|
|
6
|
+
from contextlib import suppress
|
|
7
|
+
from threading import Lock
|
|
8
|
+
import time
|
|
9
|
+
import json
|
|
10
|
+
import uuid
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from COMPS.Data.Simulation import SimulationState
|
|
13
|
+
from functools import partial
|
|
14
|
+
from logging import getLogger, DEBUG
|
|
15
|
+
from typing import Any, List, Dict, Type, Optional, TYPE_CHECKING
|
|
16
|
+
from uuid import UUID
|
|
17
|
+
from COMPS.Data import Simulation as COMPSSimulation, QueryCriteria, Experiment as COMPSExperiment, SimulationFile, \
|
|
18
|
+
Configuration
|
|
19
|
+
from idmtools.assets import AssetCollection, Asset
|
|
20
|
+
from idmtools.core import ItemType, EntityStatus
|
|
21
|
+
from idmtools.core.task_factory import TaskFactory
|
|
22
|
+
from idmtools.entities import CommandLine
|
|
23
|
+
from idmtools.entities.command_task import CommandTask
|
|
24
|
+
from idmtools.entities.experiment import Experiment
|
|
25
|
+
from idmtools.entities.iplatform_ops.iplatform_simulation_operations import IPlatformSimulationOperations
|
|
26
|
+
from idmtools.entities.iplatform_ops.utils import batch_create_items
|
|
27
|
+
from idmtools.entities.simulation import Simulation
|
|
28
|
+
from idmtools.utils.json import IDMJSONEncoder
|
|
29
|
+
from idmtools_platform_comps.utils.general import convert_comps_status, get_asset_for_comps_item, clean_experiment_name
|
|
30
|
+
from idmtools_platform_comps.utils.scheduling import scheduled
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
33
|
+
from idmtools_platform_comps.comps_platform import COMPSPlatform
|
|
34
|
+
|
|
35
|
+
logger = getLogger(__name__)
|
|
36
|
+
user_logger = getLogger('user')
|
|
37
|
+
|
|
38
|
+
# Track commissioning in batches
|
|
39
|
+
COMPS_EXPERIMENT_BATCH_COMMISSION_LOCK = Lock()
|
|
40
|
+
COMPS_EXPERIMENT_BATCH_COMMISSION_TIMESTAMP = 0
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def comps_batch_worker(simulations: List[Simulation], interface: 'CompsPlatformSimulationOperations', executor,
|
|
44
|
+
num_cores: Optional[int] = None, priority: Optional[str] = None, asset_collection_id: str = None,
|
|
45
|
+
min_time_between_commissions: int = 10, **kwargs) -> List[COMPSSimulation]:
|
|
46
|
+
"""
|
|
47
|
+
Run batch worker.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
simulations: Batch of simulation to process
|
|
51
|
+
interface: SimulationOperation Interface
|
|
52
|
+
executor: Thread/process executor
|
|
53
|
+
num_cores: Optional Number of core to allocate for MPI
|
|
54
|
+
priority: Optional Priority to set to
|
|
55
|
+
asset_collection_id: Override asset collection id
|
|
56
|
+
min_time_between_commissions: Minimum amount of time(in seconds) between calls to commission on an experiment
|
|
57
|
+
extra info for
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List of Comps Simulations
|
|
61
|
+
"""
|
|
62
|
+
global COMPS_EXPERIMENT_BATCH_COMMISSION_LOCK, COMPS_EXPERIMENT_BATCH_COMMISSION_TIMESTAMP # noqa: F824
|
|
63
|
+
if logger.isEnabledFor(DEBUG):
|
|
64
|
+
logger.debug(f'Converting {len(simulations)} to COMPS')
|
|
65
|
+
created_simulations = []
|
|
66
|
+
|
|
67
|
+
new_sims = 0
|
|
68
|
+
for simulation in simulations:
|
|
69
|
+
if simulation.status is None:
|
|
70
|
+
interface.pre_create(simulation)
|
|
71
|
+
new_sims += 1
|
|
72
|
+
simulation.platform = interface.platform
|
|
73
|
+
simulation._platform_object = interface.to_comps_sim(simulation, num_cores=num_cores, priority=priority,
|
|
74
|
+
asset_collection_id=asset_collection_id, **kwargs)
|
|
75
|
+
created_simulations.append(simulation)
|
|
76
|
+
if logger.isEnabledFor(DEBUG):
|
|
77
|
+
logger.debug(f'Finished converting to COMPS. Starting saving of {len(simulations)}')
|
|
78
|
+
COMPSSimulation.save_all(None, save_semaphore=COMPSSimulation.get_save_semaphore())
|
|
79
|
+
if logger.isEnabledFor(DEBUG):
|
|
80
|
+
logger.debug(f'Finished saving of {len(simulations)}. Starting post_create')
|
|
81
|
+
for simulation in simulations:
|
|
82
|
+
simulation.uid = simulation.get_platform_object().id
|
|
83
|
+
simulation.status = convert_comps_status(simulation.get_platform_object().state)
|
|
84
|
+
interface.post_create(simulation)
|
|
85
|
+
if logger.isEnabledFor(DEBUG):
|
|
86
|
+
logger.debug(f'Finished post-create of {len(simulations)}')
|
|
87
|
+
|
|
88
|
+
current_time = time.time()
|
|
89
|
+
# check current commission queue and last commission call
|
|
90
|
+
if new_sims > 0 and current_time - COMPS_EXPERIMENT_BATCH_COMMISSION_TIMESTAMP > min_time_between_commissions:
|
|
91
|
+
# be aggressive in waiting on lock. Worse case, another thread triggers this near same time
|
|
92
|
+
locked = COMPS_EXPERIMENT_BATCH_COMMISSION_LOCK.acquire(timeout=0.015)
|
|
93
|
+
if locked:
|
|
94
|
+
# do commission asynchronously. If it fine if we happen to miss a commission
|
|
95
|
+
def do_commission():
|
|
96
|
+
with suppress(RuntimeError):
|
|
97
|
+
simulations[0].experiment.get_platform_object().commission()
|
|
98
|
+
|
|
99
|
+
executor.submit(do_commission)
|
|
100
|
+
COMPS_EXPERIMENT_BATCH_COMMISSION_TIMESTAMP = current_time
|
|
101
|
+
COMPS_EXPERIMENT_BATCH_COMMISSION_LOCK.release()
|
|
102
|
+
for simulation in simulations:
|
|
103
|
+
simulation.status = EntityStatus.RUNNING
|
|
104
|
+
|
|
105
|
+
return simulations
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass
|
|
109
|
+
class CompsPlatformSimulationOperations(IPlatformSimulationOperations):
|
|
110
|
+
"""Provides simuilation operations to COMPSPlatform."""
|
|
111
|
+
platform: 'COMPSPlatform' # noqa F821
|
|
112
|
+
platform_type: Type = field(default=COMPSSimulation)
|
|
113
|
+
|
|
114
|
+
def get(self, simulation_id: UUID, columns: Optional[List[str]] = None, load_children: Optional[List[str]] = None,
|
|
115
|
+
query_criteria: Optional[QueryCriteria] = None, **kwargs) -> COMPSSimulation:
|
|
116
|
+
"""
|
|
117
|
+
Get Simulation from Comps.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
simulation_id: ID
|
|
121
|
+
columns: Optional list of columns to load. Defaults to "id", "name", "experiment_id", "state"
|
|
122
|
+
load_children: Optional children to load. Defaults to "tags", "configuration"
|
|
123
|
+
query_criteria: Optional query_criteria object to use your own custom criteria object
|
|
124
|
+
**kwargs:
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
COMPSSimulation
|
|
128
|
+
"""
|
|
129
|
+
columns = columns or ["id", "name", "experiment_id", "state"]
|
|
130
|
+
children = load_children if load_children is not None else ["tags", "configuration", "files"]
|
|
131
|
+
query_criteria = query_criteria or QueryCriteria().select(columns).select_children(children)
|
|
132
|
+
return COMPSSimulation.get(
|
|
133
|
+
id=simulation_id,
|
|
134
|
+
query_criteria=query_criteria
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def platform_create(self, simulation: Simulation, num_cores: int = None, priority: str = None,
|
|
138
|
+
enable_platform_task_hooks: bool = True, asset_collection_id: str = None, **kwargs) -> COMPSSimulation:
|
|
139
|
+
"""
|
|
140
|
+
Create Simulation on COMPS.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
simulation: Simulation to create
|
|
144
|
+
num_cores: Optional number of MPI Cores to allocate
|
|
145
|
+
priority: Priority to load
|
|
146
|
+
enable_platform_task_hooks: Should platform task hoooks be ran
|
|
147
|
+
asset_collection_id: Override for asset collection id on sim
|
|
148
|
+
**kwargs: Expansion fields
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
COMPS Simulation
|
|
152
|
+
"""
|
|
153
|
+
from idmtools_platform_comps.utils.python_version import platform_task_hooks
|
|
154
|
+
if enable_platform_task_hooks:
|
|
155
|
+
simulation.task = platform_task_hooks(simulation.task, self.platform)
|
|
156
|
+
s = self.to_comps_sim(simulation, num_cores=num_cores, priority=priority,
|
|
157
|
+
asset_collection_id=asset_collection_id, **kwargs)
|
|
158
|
+
COMPSSimulation.save(s, save_semaphore=COMPSSimulation.get_save_semaphore())
|
|
159
|
+
return s
|
|
160
|
+
|
|
161
|
+
def to_comps_sim(self, simulation: Simulation, num_cores: int = None, priority: str = None,
|
|
162
|
+
config: Configuration = None, asset_collection_id: str = None, **kwargs):
|
|
163
|
+
"""
|
|
164
|
+
Covert IDMTools object to COMPS Object.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
simulation: Simulation object to convert
|
|
168
|
+
num_cores: Optional Num of MPI Cores to allocate
|
|
169
|
+
priority: Optional Priority
|
|
170
|
+
config: Optional Configuration object
|
|
171
|
+
asset_collection_id:
|
|
172
|
+
**kwargs additional option for comps
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
COMPS Simulation
|
|
176
|
+
"""
|
|
177
|
+
if config is None:
|
|
178
|
+
if asset_collection_id and isinstance(asset_collection_id, str):
|
|
179
|
+
asset_collection_id = uuid.UUID(asset_collection_id)
|
|
180
|
+
kwargs['num_cores'] = num_cores
|
|
181
|
+
kwargs['priority'] = priority
|
|
182
|
+
kwargs['asset_collection_id'] = asset_collection_id
|
|
183
|
+
kwargs.update(simulation._platform_kwargs)
|
|
184
|
+
config = self.get_simulation_config_from_simulation(simulation, **kwargs)
|
|
185
|
+
|
|
186
|
+
if simulation.name:
|
|
187
|
+
simulation.name = clean_experiment_name(simulation.name)
|
|
188
|
+
s = COMPSSimulation(
|
|
189
|
+
name=clean_experiment_name(simulation.experiment.name if not simulation.name else simulation.name),
|
|
190
|
+
experiment_id=simulation.parent_id,
|
|
191
|
+
configuration=config
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
self.send_assets(simulation, s, **kwargs)
|
|
195
|
+
s.set_tags(simulation.tags)
|
|
196
|
+
simulation._platform_object = s
|
|
197
|
+
return s
|
|
198
|
+
|
|
199
|
+
def get_simulation_config_from_simulation(self, simulation: Simulation, num_cores: int = None, priority: str = None,
|
|
200
|
+
asset_collection_id: str = None, **kwargs) -> \
|
|
201
|
+
Configuration:
|
|
202
|
+
"""
|
|
203
|
+
Get the comps configuration for a Simulation Object.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
simulation: Simulation
|
|
207
|
+
num_cores: Optional Num of core for MPI
|
|
208
|
+
priority: Optional Priority
|
|
209
|
+
asset_collection_id: Override simulation asset_collection_id
|
|
210
|
+
**kwargs additional option for comps
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Configuration
|
|
214
|
+
"""
|
|
215
|
+
global_scheduling = kwargs.get("scheduling", False)
|
|
216
|
+
sim_scheduling = scheduled(simulation)
|
|
217
|
+
scheduling = global_scheduling or sim_scheduling
|
|
218
|
+
|
|
219
|
+
comps_configuration = dict()
|
|
220
|
+
if global_scheduling:
|
|
221
|
+
config = getattr(self.platform, 'comps_config', {})
|
|
222
|
+
comps_exp_config = Configuration(**config)
|
|
223
|
+
else:
|
|
224
|
+
comps_exp: COMPSExperiment = simulation.parent.get_platform_object()
|
|
225
|
+
comps_exp_config: Configuration = comps_exp.configuration
|
|
226
|
+
|
|
227
|
+
if asset_collection_id:
|
|
228
|
+
comps_configuration['asset_collection_id'] = asset_collection_id
|
|
229
|
+
if num_cores is not None and num_cores != comps_exp_config.max_cores:
|
|
230
|
+
logger.info(f'Overriding cores for sim to {num_cores}')
|
|
231
|
+
comps_configuration['max_cores'] = num_cores
|
|
232
|
+
comps_configuration['min_cores'] = num_cores
|
|
233
|
+
if priority is not None and priority != comps_exp_config.priority:
|
|
234
|
+
logger.info(f'Overriding priority for sim to {priority}')
|
|
235
|
+
comps_configuration['priority'] = priority
|
|
236
|
+
if comps_exp_config.executable_path != simulation.task.command.executable:
|
|
237
|
+
logger.info(f'Overriding executable_path for sim to {simulation.task.command.executable}')
|
|
238
|
+
from idmtools_platform_comps.utils.python_version import platform_task_hooks
|
|
239
|
+
platform_task_hooks(simulation.task, self.platform)
|
|
240
|
+
comps_configuration['executable_path'] = simulation.task.command.executable
|
|
241
|
+
sim_task = simulation.task.command.arguments + " " + simulation.task.command.options
|
|
242
|
+
sim_task = sim_task.strip()
|
|
243
|
+
if comps_exp_config.simulation_input_args != sim_task:
|
|
244
|
+
logger.info(f'Overriding simulation_input_args for sim to {sim_task}')
|
|
245
|
+
comps_configuration['simulation_input_args'] = sim_task
|
|
246
|
+
if logger.isEnabledFor(DEBUG):
|
|
247
|
+
logger.debug(f'Simulation config: {str(comps_configuration)}')
|
|
248
|
+
if scheduling:
|
|
249
|
+
comps_configuration.update(executable_path=None, node_group_name=None, min_cores=None, max_cores=None,
|
|
250
|
+
exclusive=None, simulation_input_args=None)
|
|
251
|
+
|
|
252
|
+
return Configuration(**comps_configuration)
|
|
253
|
+
|
|
254
|
+
def batch_create(self, simulations: List[Simulation], num_cores: int = None, priority: str = None,
|
|
255
|
+
asset_collection_id: str = None, **kwargs) -> List[COMPSSimulation]:
|
|
256
|
+
"""
|
|
257
|
+
Perform batch creation of Simulations.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
simulations: Simulation to create
|
|
261
|
+
num_cores: Optional MPI Cores to allocate per simulation
|
|
262
|
+
priority: Optional Priority
|
|
263
|
+
asset_collection_id: Asset collection id for sim(overide experiment)
|
|
264
|
+
**kwargs: Future expansion
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
List of COMPSSimulations that were created
|
|
268
|
+
"""
|
|
269
|
+
global COMPS_EXPERIMENT_BATCH_COMMISSION_TIMESTAMP
|
|
270
|
+
executor = ThreadPoolExecutor()
|
|
271
|
+
thread_func = partial(comps_batch_worker, interface=self, num_cores=num_cores, priority=priority,
|
|
272
|
+
asset_collection_id=asset_collection_id,
|
|
273
|
+
min_time_between_commissions=self.platform.min_time_between_commissions,
|
|
274
|
+
executor=executor, **kwargs)
|
|
275
|
+
results = batch_create_items(
|
|
276
|
+
simulations,
|
|
277
|
+
batch_worker_thread_func=thread_func,
|
|
278
|
+
progress_description="Creating Simulations on Comps",
|
|
279
|
+
unit="simulation"
|
|
280
|
+
)
|
|
281
|
+
# Always commission again
|
|
282
|
+
try:
|
|
283
|
+
results[0].parent.get_platform_object().commission()
|
|
284
|
+
except RuntimeError as ex: # occasionally we hit this because double commissioning. Its ok to ignore though because that means we have already commissioned this experiment
|
|
285
|
+
if logger.isEnabledFor(DEBUG):
|
|
286
|
+
logger.debug(f"COMMISSION Response: {ex.args}")
|
|
287
|
+
COMPS_EXPERIMENT_BATCH_COMMISSION_TIMESTAMP = 0
|
|
288
|
+
# set commission here in comps objects to prevent commission in Experiment when batching
|
|
289
|
+
for sim in results:
|
|
290
|
+
sim.status = EntityStatus.RUNNING
|
|
291
|
+
sim.get_platform_object()._state = SimulationState.CommissionRequested
|
|
292
|
+
return results
|
|
293
|
+
|
|
294
|
+
def get_parent(self, simulation: Any, **kwargs) -> COMPSExperiment:
|
|
295
|
+
"""
|
|
296
|
+
Get the parent of the simulation.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
simulation: Simulation to load parent for
|
|
300
|
+
**kwargs:
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
COMPSExperiment
|
|
304
|
+
"""
|
|
305
|
+
return self.platform._experiments.get(simulation.experiment_id, **kwargs) if simulation.experiment_id else None
|
|
306
|
+
|
|
307
|
+
def platform_run_item(self, simulation: Simulation, **kwargs):
|
|
308
|
+
"""For simulations, there is no running for COMPS."""
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
def send_assets(self, simulation: Simulation, comps_sim: Optional[COMPSSimulation] = None,
|
|
312
|
+
add_metadata: bool = False, **kwargs):
|
|
313
|
+
"""
|
|
314
|
+
Send assets to Simulation.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
simulation: Simulation to send asset for
|
|
318
|
+
comps_sim: Optional COMPSSimulation object to prevent reloading it
|
|
319
|
+
add_metadata: Add idmtools metadata object
|
|
320
|
+
**kwargs:
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
None
|
|
324
|
+
"""
|
|
325
|
+
if comps_sim is None:
|
|
326
|
+
comps_sim = simulation.get_platform_object()
|
|
327
|
+
for asset in simulation.assets:
|
|
328
|
+
if asset.filename.lower() == 'workorder.json' and scheduled(simulation):
|
|
329
|
+
comps_sim.add_file(simulationfile=SimulationFile(asset.filename, 'WorkOrder'), data=asset.bytes)
|
|
330
|
+
else:
|
|
331
|
+
comps_sim.add_file(simulationfile=SimulationFile(asset.filename, 'input'), data=asset.bytes)
|
|
332
|
+
|
|
333
|
+
# add metadata
|
|
334
|
+
if add_metadata:
|
|
335
|
+
if logger.isEnabledFor(DEBUG):
|
|
336
|
+
logger.debug("Creating idmtools metadata for simulation and task on COMPS")
|
|
337
|
+
# later we should add some filtering for passwords and such here in case anything weird happens
|
|
338
|
+
metadata = json.dumps(simulation.task.to_dict(), cls=IDMJSONEncoder)
|
|
339
|
+
from idmtools import __version__
|
|
340
|
+
comps_sim.add_file(
|
|
341
|
+
SimulationFile("idmtools_metadata.json", 'input', description=f'IDMTools {__version__}'),
|
|
342
|
+
data=metadata.encode()
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
def refresh_status(self, simulation: Simulation, additional_columns: Optional[List[str]] = None, **kwargs):
|
|
346
|
+
"""
|
|
347
|
+
Refresh status of a simulation.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
simulation: Simulation to refresh
|
|
351
|
+
additional_columns: Optional additional columns to load from COMPS
|
|
352
|
+
**kwargs:
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
None
|
|
356
|
+
"""
|
|
357
|
+
cols = ['state']
|
|
358
|
+
if additional_columns:
|
|
359
|
+
cols.extend(additional_columns)
|
|
360
|
+
s = COMPSSimulation.get(id=simulation.uid, query_criteria=QueryCriteria().select(cols))
|
|
361
|
+
simulation.status = convert_comps_status(s.state)
|
|
362
|
+
|
|
363
|
+
def to_entity(self, simulation: COMPSSimulation, load_task: bool = False, parent: Optional[Experiment] = None,
|
|
364
|
+
load_parent: bool = False, load_metadata: bool = False, load_cli_from_workorder: bool = False,
|
|
365
|
+
**kwargs) -> Simulation:
|
|
366
|
+
"""
|
|
367
|
+
Convert COMPS simulation object to IDM Tools simulation object.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
simulation: Simulation object
|
|
371
|
+
load_task: Should we load tasks. Defaults to No. This can increase the load items on fetchs
|
|
372
|
+
parent: Optional parent object to prevent reloads
|
|
373
|
+
load_parent: Force load of parent(Beware, This could cause loading loops)
|
|
374
|
+
load_metadata: Should we load metadata by default. If load task is enabled, this is also enabled
|
|
375
|
+
load_cli_from_workorder: Used with COMPS scheduling where the CLI is defined in our workorder
|
|
376
|
+
**kwargs:
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Simulation object
|
|
380
|
+
"""
|
|
381
|
+
# Recreate the experiment if needed
|
|
382
|
+
if parent is None or load_parent:
|
|
383
|
+
if logger.isEnabledFor(DEBUG):
|
|
384
|
+
logger.debug(f'Loading parent {simulation.experiment_id}')
|
|
385
|
+
parent = self.platform.get_item(simulation.experiment_id, item_type=ItemType.EXPERIMENT)
|
|
386
|
+
# Get a simulation
|
|
387
|
+
obj = Simulation()
|
|
388
|
+
obj.platform = self.platform
|
|
389
|
+
obj._platform_object = simulation
|
|
390
|
+
obj.parent = parent
|
|
391
|
+
obj.experiment = parent
|
|
392
|
+
# Set its correct attributes
|
|
393
|
+
obj.uid = simulation.id
|
|
394
|
+
obj.tags = simulation.tags
|
|
395
|
+
obj.status = convert_comps_status(simulation.state)
|
|
396
|
+
if simulation.files:
|
|
397
|
+
obj.assets = self.platform._assets.to_entity(simulation.files)
|
|
398
|
+
|
|
399
|
+
# should we load metadata
|
|
400
|
+
metadata = self.__load_metadata_from_simulation(obj) if load_metadata else None
|
|
401
|
+
if load_task:
|
|
402
|
+
self._load_task_from_simulation2(obj, simulation, parent.task_type, metadata)
|
|
403
|
+
else:
|
|
404
|
+
obj.task = None
|
|
405
|
+
self.__extract_cli(simulation, parent, obj, load_cli_from_workorder)
|
|
406
|
+
|
|
407
|
+
# call task load options(load configs from files, etc)
|
|
408
|
+
obj.task.reload_from_simulation(obj)
|
|
409
|
+
return obj
|
|
410
|
+
|
|
411
|
+
def get_asset_collection_from_comps_simulation(self, simulation: COMPSSimulation) -> Optional[AssetCollection]:
|
|
412
|
+
"""
|
|
413
|
+
Get assets from COMPS Simulation.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
simulation: Simulation to get assets from
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
Simulation Asset Collection, if any.
|
|
420
|
+
"""
|
|
421
|
+
if simulation.configuration and simulation.configuration.asset_collection_id:
|
|
422
|
+
return self.platform.get_item(simulation.configuration.asset_collection_id, ItemType.ASSETCOLLECTION)
|
|
423
|
+
return None
|
|
424
|
+
|
|
425
|
+
def _load_task_from_simulation(self, simulation: Simulation, comps_sim: COMPSSimulation,
|
|
426
|
+
metadata: Dict = None):
|
|
427
|
+
"""
|
|
428
|
+
Load task from the simulation object.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
simulation: Simulation to populate with task
|
|
432
|
+
comps_sim: COMPSSimulation object
|
|
433
|
+
metadata: Metadata loaded to be used in the task object
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
None
|
|
437
|
+
"""
|
|
438
|
+
simulation.task = None
|
|
439
|
+
if comps_sim.tags and 'task_type' in comps_sim.tags:
|
|
440
|
+
# check for metadata
|
|
441
|
+
if not metadata:
|
|
442
|
+
metadata = self.__load_metadata_from_simulation(simulation)
|
|
443
|
+
try:
|
|
444
|
+
if logger.isEnabledFor(DEBUG):
|
|
445
|
+
logger.debug(f"Metadata: {metadata}")
|
|
446
|
+
simulation.task = TaskFactory().create(comps_sim.tags['task_type'], **metadata)
|
|
447
|
+
except Exception as e:
|
|
448
|
+
user_logger.warning(f"Could not load task of type {comps_sim.tags['task_type']}. "
|
|
449
|
+
f"Received error {str(e)}")
|
|
450
|
+
logger.exception(e)
|
|
451
|
+
# ensure we have loaded the configuration
|
|
452
|
+
if comps_sim.configuration is None:
|
|
453
|
+
comps_sim.refresh(QueryCriteria().select_children('configuration'))
|
|
454
|
+
|
|
455
|
+
def _load_task_from_simulation2(self, simulation: Simulation, comps_sim: COMPSSimulation, task_type: str,
|
|
456
|
+
metadata: Dict = None):
|
|
457
|
+
"""
|
|
458
|
+
Load task from the simulation object.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
simulation: Simulation to populate with task
|
|
462
|
+
comps_sim: COMPSSimulation object
|
|
463
|
+
task_type: Experiment's task type
|
|
464
|
+
metadata: Metadata loaded to be used in the task object
|
|
465
|
+
|
|
466
|
+
Returns:
|
|
467
|
+
None
|
|
468
|
+
"""
|
|
469
|
+
simulation.task = None
|
|
470
|
+
if task_type:
|
|
471
|
+
# check for metadata
|
|
472
|
+
if not metadata:
|
|
473
|
+
metadata = self.__load_metadata_from_simulation(simulation)
|
|
474
|
+
try:
|
|
475
|
+
if logger.isEnabledFor(DEBUG):
|
|
476
|
+
logger.debug(f"Metadata: {metadata}")
|
|
477
|
+
simulation.task = TaskFactory().create(task_type, **metadata)
|
|
478
|
+
except Exception as e:
|
|
479
|
+
user_logger.warning(f"Could not load task of type {task_type}. "
|
|
480
|
+
f"Received error {str(e)}")
|
|
481
|
+
logger.exception(e)
|
|
482
|
+
# ensure we have loaded the configuration
|
|
483
|
+
if comps_sim.configuration is None:
|
|
484
|
+
comps_sim.refresh(QueryCriteria().select_children('configuration'))
|
|
485
|
+
|
|
486
|
+
def __extract_cli(self, comps_sim, parent, simulation, load_cli_from_workorder):
|
|
487
|
+
cli = self._detect_command_line_from_simulation(parent, comps_sim, simulation, load_cli_from_workorder)
|
|
488
|
+
# if we could not find task, set it now, otherwise rebuild the cli
|
|
489
|
+
if simulation.task is None:
|
|
490
|
+
simulation.task = CommandTask(CommandLine.from_string(cli))
|
|
491
|
+
else:
|
|
492
|
+
simulation.task.command = CommandLine.from_string(cli)
|
|
493
|
+
|
|
494
|
+
@staticmethod
|
|
495
|
+
def __load_metadata_from_simulation(simulation) -> Dict[str, Any]:
|
|
496
|
+
"""
|
|
497
|
+
Load IDMTools metadata from a simulation.
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
simulation:
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
Metadata if found
|
|
504
|
+
|
|
505
|
+
Raise:
|
|
506
|
+
FileNotFoundError error if metadata not found
|
|
507
|
+
"""
|
|
508
|
+
metadata = None
|
|
509
|
+
for file in simulation.assets:
|
|
510
|
+
if file.filename == "idmtools_metadata.json":
|
|
511
|
+
# load the asset
|
|
512
|
+
metadata = json.loads(file.content.decode('utf-8'))
|
|
513
|
+
return metadata
|
|
514
|
+
raise FileNotFoundError(f"Cannot find idmtools_metadata.json on the simulation {simulation.uid}")
|
|
515
|
+
|
|
516
|
+
@staticmethod
|
|
517
|
+
def _detect_command_line_from_simulation(experiment, comps_sim, simulation, load_cli_from_workorder=False):
|
|
518
|
+
"""
|
|
519
|
+
Detect Command Line from the Experiment/Simulation objects.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
experiment: Experiment object
|
|
523
|
+
comps_sim: Comps sim object
|
|
524
|
+
simulation: Simulation(idmtools) object
|
|
525
|
+
load_cli_from_workorder: Should we load metadata. we use this to determine if we should load our workorder.json
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
CommandLine
|
|
529
|
+
Raise:
|
|
530
|
+
ValueError when command cannot be detected
|
|
531
|
+
"""
|
|
532
|
+
cli = None
|
|
533
|
+
if isinstance(experiment, COMPSExperiment):
|
|
534
|
+
po = experiment
|
|
535
|
+
else:
|
|
536
|
+
# do we have a configuration?
|
|
537
|
+
po: COMPSExperiment = experiment.get_platform_object()
|
|
538
|
+
if po.configuration is None:
|
|
539
|
+
po.refresh(QueryCriteria().select_children('configuration'))
|
|
540
|
+
# simulation configuration for executable?
|
|
541
|
+
if comps_sim.configuration and comps_sim.configuration.executable_path:
|
|
542
|
+
cli = f'{comps_sim.configuration.executable_path}'
|
|
543
|
+
if comps_sim.configuration.simulation_input_args:
|
|
544
|
+
cli += " " + comps_sim.configuration.simulation_input_args.strip()
|
|
545
|
+
elif po.configuration and po.configuration.executable_path:
|
|
546
|
+
cli = f'{po.configuration.executable_path} {po.configuration.simulation_input_args.strip()}'
|
|
547
|
+
if cli is None:
|
|
548
|
+
# check if we should try to load our workorder
|
|
549
|
+
if load_cli_from_workorder:
|
|
550
|
+
# filter for workorder
|
|
551
|
+
workorder_obj = None
|
|
552
|
+
for a in simulation.assets:
|
|
553
|
+
if getattr(a, '_platform_object', None) and isinstance(a._platform_object,
|
|
554
|
+
SimulationFile) and a._platform_object.file_type == "WorkOrder":
|
|
555
|
+
workorder_obj = a
|
|
556
|
+
break
|
|
557
|
+
# if assets
|
|
558
|
+
if workorder_obj:
|
|
559
|
+
asset: Asset = workorder_obj
|
|
560
|
+
wo = json.loads(asset.content.decode('utf-8'))
|
|
561
|
+
cli = wo['Command']
|
|
562
|
+
else:
|
|
563
|
+
raise ValueError("Could not detect cli")
|
|
564
|
+
else: # if user doesn't care oabout metadata don't error
|
|
565
|
+
logger.debug(
|
|
566
|
+
"Could not load the cli from simulation. This is usually due to the use of scheduling config.")
|
|
567
|
+
cli = "WARNING_CouldNotLoadCLI"
|
|
568
|
+
elif logger.isEnabledFor(DEBUG):
|
|
569
|
+
logger.debug(f"Detected task CLI {cli}")
|
|
570
|
+
return cli
|
|
571
|
+
|
|
572
|
+
def get_assets(self, simulation: Simulation, files: List[str], include_experiment_assets: bool = True, **kwargs) -> \
|
|
573
|
+
Dict[str, bytearray]:
|
|
574
|
+
"""
|
|
575
|
+
Fetch the files associated with a simulation.
|
|
576
|
+
|
|
577
|
+
Args:
|
|
578
|
+
simulation: Simulation
|
|
579
|
+
files: List of files to download
|
|
580
|
+
include_experiment_assets: Should we also load experiment assets?
|
|
581
|
+
**kwargs:
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
Dictionary of filename -> ByteArray
|
|
585
|
+
"""
|
|
586
|
+
# since assets could be in the common assets, we should check that firs
|
|
587
|
+
# load comps config first
|
|
588
|
+
if include_experiment_assets and (
|
|
589
|
+
simulation.configuration is None or simulation.configuration.asset_collection_id is None):
|
|
590
|
+
if logger.isEnabledFor(DEBUG):
|
|
591
|
+
logger.debug("Gathering assets from experiment first")
|
|
592
|
+
exp_assets = get_asset_for_comps_item(self.platform, simulation.experiment, files, self.cache,
|
|
593
|
+
comps_item=simulation.experiment)
|
|
594
|
+
if exp_assets is None:
|
|
595
|
+
exp_assets = dict()
|
|
596
|
+
else:
|
|
597
|
+
exp_assets = dict()
|
|
598
|
+
exp_assets.update(get_asset_for_comps_item(self.platform, simulation, files, self.cache, comps_item=simulation))
|
|
599
|
+
return exp_assets
|
|
600
|
+
|
|
601
|
+
def list_assets(self, simulation: Simulation, common_assets: bool = False, **kwargs) -> List[Asset]:
|
|
602
|
+
"""
|
|
603
|
+
List assets for a simulation.
|
|
604
|
+
|
|
605
|
+
Args:
|
|
606
|
+
simulation: Simulation to load data for
|
|
607
|
+
common_assets: Should we load asset files
|
|
608
|
+
**kwargs:
|
|
609
|
+
|
|
610
|
+
Returns:
|
|
611
|
+
AssetCollection
|
|
612
|
+
"""
|
|
613
|
+
comps_sim: COMPSSimulation = simulation.get_platform_object(load_children=["files", "configuration"])
|
|
614
|
+
assets = []
|
|
615
|
+
# load non comps objects
|
|
616
|
+
if comps_sim.files:
|
|
617
|
+
assets = self.platform._assets.to_entity(comps_sim.files).assets
|
|
618
|
+
|
|
619
|
+
if common_assets:
|
|
620
|
+
# here we are loading the simulation assets
|
|
621
|
+
sa = self.get_asset_collection_from_comps_simulation(comps_sim)
|
|
622
|
+
if sa:
|
|
623
|
+
assets.extend(sa.assets)
|
|
624
|
+
return assets
|
|
625
|
+
|
|
626
|
+
def retrieve_output_files(self, simulation: Simulation):
|
|
627
|
+
"""
|
|
628
|
+
Retrieve the output files for a simulation.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
simulation: Simulation to fetch files for
|
|
632
|
+
|
|
633
|
+
Returns:
|
|
634
|
+
List of output files for simulation
|
|
635
|
+
"""
|
|
636
|
+
metadata = simulation.get_platform_object().retrieve_output_file_info([])
|
|
637
|
+
assets = self.platform._assets.to_entity(metadata).assets
|
|
638
|
+
return assets
|
|
639
|
+
|
|
640
|
+
def all_files(self, simulation: Simulation, common_assets: bool = False, outfiles: bool = True, **kwargs) -> List[Asset]:
|
|
641
|
+
"""
|
|
642
|
+
Returns all files for a specific simulation including experiments or non-assets.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
simulation: Simulation all files
|
|
646
|
+
common_assets: Include experiment assets
|
|
647
|
+
outfiles: Include output files
|
|
648
|
+
**kwargs:
|
|
649
|
+
|
|
650
|
+
Returns:
|
|
651
|
+
AssetCollection
|
|
652
|
+
"""
|
|
653
|
+
ac = AssetCollection()
|
|
654
|
+
ac.add_assets(self.list_assets(simulation, **kwargs))
|
|
655
|
+
if common_assets:
|
|
656
|
+
ac.add_assets(simulation.parent.assets)
|
|
657
|
+
if outfiles:
|
|
658
|
+
ac.add_assets(self.retrieve_output_files(simulation))
|
|
659
|
+
|
|
660
|
+
return ac.assets
|
|
661
|
+
|
|
662
|
+
def create_sim_directory_map(self, simulation_id: str) -> Dict:
|
|
663
|
+
"""
|
|
664
|
+
Build simulation working directory mapping.
|
|
665
|
+
Args:
|
|
666
|
+
simulation_id: simulation id
|
|
667
|
+
|
|
668
|
+
Returns:
|
|
669
|
+
Dict of simulation id as key and working dir as value
|
|
670
|
+
"""
|
|
671
|
+
from idmtools_platform_comps.utils.linux_mounts import set_linux_mounts, clear_linux_mounts
|
|
672
|
+
set_linux_mounts(self.platform)
|
|
673
|
+
comps_sim = self.platform.get_item(simulation_id, ItemType.SIMULATION,
|
|
674
|
+
query_criteria=QueryCriteria().select(['id', 'state']).select_children(
|
|
675
|
+
'hpc_jobs'), raw=True)
|
|
676
|
+
sim_map = {str(comps_sim.id): comps_sim.hpc_jobs[-1].working_directory}
|
|
677
|
+
clear_linux_mounts(self.platform)
|
|
678
|
+
return sim_map
|