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.
Files changed (64) hide show
  1. idmtools_platform_comps/__init__.py +25 -8
  2. idmtools_platform_comps/cli/__init__.py +4 -0
  3. idmtools_platform_comps/cli/cli_functions.py +50 -0
  4. idmtools_platform_comps/cli/comps.py +492 -0
  5. idmtools_platform_comps/comps_cli.py +48 -0
  6. idmtools_platform_comps/comps_operations/__init__.py +6 -0
  7. idmtools_platform_comps/comps_operations/asset_collection_operations.py +263 -0
  8. idmtools_platform_comps/comps_operations/experiment_operations.py +569 -0
  9. idmtools_platform_comps/comps_operations/simulation_operations.py +678 -0
  10. idmtools_platform_comps/comps_operations/suite_operations.py +228 -0
  11. idmtools_platform_comps/comps_operations/workflow_item_operations.py +269 -0
  12. idmtools_platform_comps/comps_platform.py +309 -0
  13. idmtools_platform_comps/plugin_info.py +168 -0
  14. idmtools_platform_comps/ssmt_operations/__init__.py +6 -0
  15. idmtools_platform_comps/ssmt_operations/simulation_operations.py +77 -0
  16. idmtools_platform_comps/ssmt_operations/workflow_item_operations.py +73 -0
  17. idmtools_platform_comps/ssmt_platform.py +44 -0
  18. idmtools_platform_comps/ssmt_work_items/__init__.py +4 -0
  19. idmtools_platform_comps/ssmt_work_items/comps_work_order_task.py +29 -0
  20. idmtools_platform_comps/ssmt_work_items/comps_workitems.py +113 -0
  21. idmtools_platform_comps/ssmt_work_items/icomps_workflowitem.py +71 -0
  22. idmtools_platform_comps/ssmt_work_items/work_order.py +54 -0
  23. idmtools_platform_comps/utils/__init__.py +4 -0
  24. idmtools_platform_comps/utils/assetize_output/__init__.py +4 -0
  25. idmtools_platform_comps/utils/assetize_output/assetize_output.py +125 -0
  26. idmtools_platform_comps/utils/assetize_output/assetize_ssmt_script.py +144 -0
  27. idmtools_platform_comps/utils/base_singularity_work_order.json +6 -0
  28. idmtools_platform_comps/utils/download/__init__.py +4 -0
  29. idmtools_platform_comps/utils/download/download.py +178 -0
  30. idmtools_platform_comps/utils/download/download_ssmt.py +81 -0
  31. idmtools_platform_comps/utils/download_experiment.py +116 -0
  32. idmtools_platform_comps/utils/file_filter_workitem.py +519 -0
  33. idmtools_platform_comps/utils/general.py +358 -0
  34. idmtools_platform_comps/utils/linux_mounts.py +73 -0
  35. idmtools_platform_comps/utils/lookups.py +123 -0
  36. idmtools_platform_comps/utils/package_version.py +489 -0
  37. idmtools_platform_comps/utils/python_requirements_ac/__init__.py +4 -0
  38. idmtools_platform_comps/utils/python_requirements_ac/create_asset_collection.py +155 -0
  39. idmtools_platform_comps/utils/python_requirements_ac/install_requirements.py +109 -0
  40. idmtools_platform_comps/utils/python_requirements_ac/requirements_to_asset_collection.py +374 -0
  41. idmtools_platform_comps/utils/python_version.py +40 -0
  42. idmtools_platform_comps/utils/scheduling.py +154 -0
  43. idmtools_platform_comps/utils/singularity_build.py +491 -0
  44. idmtools_platform_comps/utils/spatial_output.py +76 -0
  45. idmtools_platform_comps/utils/ssmt_utils/__init__.py +6 -0
  46. idmtools_platform_comps/utils/ssmt_utils/common.py +70 -0
  47. idmtools_platform_comps/utils/ssmt_utils/file_filter.py +568 -0
  48. idmtools_platform_comps/utils/sweeping.py +162 -0
  49. idmtools_platform_comps-0.0.2.dist-info/METADATA +100 -0
  50. idmtools_platform_comps-0.0.2.dist-info/RECORD +62 -0
  51. idmtools_platform_comps-0.0.2.dist-info/entry_points.txt +9 -0
  52. idmtools_platform_comps-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
  53. {idmtools_platform_comps-0.0.0.dev0.dist-info → idmtools_platform_comps-0.0.2.dist-info}/top_level.txt +1 -0
  54. ssmt_image/Dockerfile +52 -0
  55. ssmt_image/Makefile +21 -0
  56. ssmt_image/__init__.py +6 -0
  57. ssmt_image/bootstrap.sh +30 -0
  58. ssmt_image/build_docker_image.py +161 -0
  59. ssmt_image/pip.conf +3 -0
  60. ssmt_image/push_docker_image.py +49 -0
  61. ssmt_image/requirements.txt +9 -0
  62. idmtools_platform_comps-0.0.0.dev0.dist-info/METADATA +0 -41
  63. idmtools_platform_comps-0.0.0.dev0.dist-info/RECORD +0 -5
  64. {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