55from . import files as files_module
66from . import jobs as jobs_module
77from . import systems as systems_module
8+ from . import launcher as launcher_module
89from .db .accessor import DatabaseAccessor
910
1011# Import only the necessary classes/functions from jobs
@@ -366,6 +367,103 @@ def revoke_credentials(
366367 )
367368
368369
370+ class ParametricSweepMethods :
371+ """Interface for PyLauncher parameter sweeps.
372+
373+ - ``generate`` — preview (``preview=True``) or write sweep files.
374+ - ``submit`` — submit the sweep job to TACC.
375+ """
376+
377+ def __init__ (self , tapis_client ):
378+ self ._tapis = tapis_client
379+
380+ def generate (
381+ self ,
382+ base_command : str ,
383+ sweep : Dict [str , Any ],
384+ directory : str = None ,
385+ * ,
386+ placeholder_style : str = "token" ,
387+ debug : str = None ,
388+ preview : bool = False ,
389+ ):
390+ """Generate PyLauncher sweep files or preview the parameter grid.
391+
392+ With ``preview=True``, returns a DataFrame of all parameter
393+ combinations — no files are written.
394+
395+ Otherwise, expands *base_command* into one command per combination
396+ and writes ``runsList.txt`` and ``call_pylauncher.py`` into
397+ *directory*. Returns the list of generated commands.
398+
399+ Args:
400+ base_command: Command template with placeholders matching sweep keys.
401+ sweep: Mapping of placeholder name to sequence of values.
402+ directory: Directory to write files into (created if needed).
403+ Required when *preview* is ``False``.
404+ placeholder_style: ``"token"`` (default) for bare ``ALPHA``,
405+ or ``"braces"`` for ``{ALPHA}``.
406+ debug: Optional debug string (e.g. ``"host+job"``).
407+ preview: If ``True``, return a DataFrame (dry run).
408+
409+ Returns:
410+ ``List[str]`` of commands, or ``pandas.DataFrame`` when
411+ *preview* is ``True``.
412+ """
413+ return launcher_module .generate_sweep (
414+ base_command , sweep , directory ,
415+ placeholder_style = placeholder_style , debug = debug , preview = preview ,
416+ )
417+
418+ def submit (
419+ self ,
420+ directory : str ,
421+ app_id : str ,
422+ allocation : str ,
423+ * ,
424+ node_count : Optional [int ] = None ,
425+ cores_per_node : Optional [int ] = None ,
426+ max_minutes : Optional [int ] = None ,
427+ queue : Optional [str ] = None ,
428+ ** kwargs ,
429+ ):
430+ """Submit a PyLauncher sweep job.
431+
432+ Translates *directory* to a Tapis URI, builds a job request with
433+ ``call_pylauncher.py`` as the script, and submits it.
434+
435+ Args:
436+ directory: Path to the input directory containing
437+ ``runsList.txt`` and ``call_pylauncher.py``
438+ (e.g. ``"/MyData/sweep/"``).
439+ app_id: Tapis application ID (e.g. ``"openseespy-s3"``).
440+ allocation: TACC allocation to charge.
441+ node_count: Number of compute nodes.
442+ cores_per_node: Cores per node.
443+ max_minutes: Maximum runtime in minutes.
444+ queue: Execution queue name.
445+ **kwargs: Additional arguments passed to
446+ ``ds.jobs.generate()``.
447+
448+ Returns:
449+ SubmittedJob: A job object for monitoring via ``.monitor()``.
450+ """
451+ input_uri = files_module .get_ds_path_uri (self ._tapis , directory )
452+ job_request = jobs_module .generate_job_request (
453+ tapis_client = self ._tapis ,
454+ app_id = app_id ,
455+ input_dir_uri = input_uri ,
456+ script_filename = "call_pylauncher.py" ,
457+ node_count = node_count ,
458+ cores_per_node = cores_per_node ,
459+ max_minutes = max_minutes ,
460+ queue = queue ,
461+ allocation = allocation ,
462+ ** kwargs ,
463+ )
464+ return jobs_module .submit_job_request (self ._tapis , job_request )
465+
466+
369467class JobMethods :
370468 """Interface for Tapis job submission, monitoring, and management.
371469
@@ -374,6 +472,10 @@ class JobMethods:
374472
375473 Args:
376474 tapis_client (Tapis): Authenticated Tapis client instance.
475+
476+ Attributes:
477+ parametric_sweep (ParametricSweepMethods): Interface for PyLauncher
478+ parameter sweep generation.
377479 """
378480
379481 def __init__ (self , tapis_client : Tapis ):
@@ -383,9 +485,10 @@ def __init__(self, tapis_client: Tapis):
383485 tapis_client (Tapis): Authenticated Tapis client instance.
384486 """
385487 self ._tapis = tapis_client
488+ self .parametric_sweep = ParametricSweepMethods (tapis_client )
386489
387490 # Method to generate the request dictionary
388- def generate_request (
491+ def generate (
389492 self ,
390493 app_id : str ,
391494 input_dir_uri : str ,
@@ -456,7 +559,7 @@ def generate_request(
456559 JobSubmissionError: If job request generation fails.
457560
458561 Example:
459- >>> job_request = ds.jobs.generate_request (
562+ >>> job_request = ds.jobs.generate (
460563 ... app_id="matlab-r2023a",
461564 ... input_dir_uri="tapis://designsafe.storage.default/username/input/",
462565 ... script_filename="run_analysis.m",
@@ -491,27 +594,26 @@ def generate_request(
491594 )
492595
493596 # Method to submit the generated request dictionary
494- def submit_request (self , job_request : Dict [str , Any ]) -> SubmittedJob :
495- """Submit a pre-generated job request dictionary to Tapis.
597+ def submit (self , job_request : Dict [str , Any ]) -> SubmittedJob :
598+ """Submit a job request dictionary to Tapis.
496599
497- This method takes a complete job request dictionary (typically generated
498- by generate_request) and submits it to Tapis for execution.
600+ Takes a job request dictionary (typically from ``generate()``) and
601+ submits it to Tapis for execution.
499602
500603 Args:
501- job_request (Dict[str, Any]): Complete job request dictionary containing
502- all necessary job parameters and configuration.
604+ job_request (Dict[str, Any]): Complete job request dictionary.
503605
504606 Returns:
505607 SubmittedJob: A SubmittedJob object for monitoring and managing the job.
506608
507609 Raises:
508610 ValueError: If job_request is not a dictionary.
509- JobSubmissionError: If the Tapis submission fails or encounters an error .
611+ JobSubmissionError: If the Tapis submission fails.
510612
511613 Example:
512- >>> job_request = ds.jobs.generate_request (...)
513- >>> submitted_job = ds.jobs.submit_request (job_request)
514- >>> print(f"Job submitted with UUID: {submitted_job .uuid}")
614+ >>> job_request = ds.jobs.generate (...)
615+ >>> job = ds.jobs.submit (job_request)
616+ >>> print(f"Job submitted with UUID: {job .uuid}")
515617 """
516618 return jobs_module .submit_job_request (self ._tapis , job_request )
517619
0 commit comments