Try on DesignSafe

Job Metadata#

by Silvia Mazzoni, DesignSafe, 2025

Using the job ID (uuid), you can access detailed metadata on a job via Tapis’s getJob… method.

This command is used to retrieve detailed information about specific jobs, rather than to list/filter many jobs.

We will use a python function that collects these metadata and process them to give you more informtion on output paths.

Using local utilities library

Connect to Tapis#

t=OpsUtils.connect_tapis()
 -- Checking Tapis token --
 Token loaded from file. Token is still valid!
 Token expires at: 2025-08-21T02:49:32+00:00
 Token expires in: 3:39:33.642532
-- LOG IN SUCCESSFUL! --

Get detailed job metadata from Tapis#

User Input – job id#

Please enter your own jobUuid

jobUuid = '4dfa35e1-15cd-48fd-a090-f348544dee1f-007'

Get all job metadata#

get_tapis_job_metadata.py
# ../OpsUtils/OpsUtils/Tapis/get_tapis_job_metadata.py
def get_tapis_job_metadata(t, jobUuid,printAll = True):
    """
    Retrieves and prints metadata for a specified Tapis job, including robust local archive reconstruction.

    This function queries the Tapis jobs API for metadata on a given job, printing
    details such as UUID, name, status, application ID, creation time, and archive location.

    If the job has completed successfully (status == 'FINISHED'), it reconstructs
    the expected local archive directory path under '~/MyData/tapis-jobs-archive',
    checks whether it exists, lists its contents, and returns this information
    in a structured dictionary.

    If the job is not yet finished or the local directory does not exist, it prints
    a notice and returns a dictionary describing the situation.

    Parameters
    ----------
    t : object
        An authenticated Tapis client instance (e.g., from `tapis3`).
    jobUuid : str
        UUID of the job whose metadata is to be retrieved.

    Returns
    -------
    dict
        A dictionary with the following keys:
        - "local_path" (str or None): local archive directory path if job is finished, else None.
        - "exists" (bool): True if the local directory exists.
        - "files" (list of str): list of files in the directory if it exists.
        - "message" (str, optional): explanatory message if no data is available.

    Prints
    ------
    - Job UUID, name, status, appId, creation time, and archive directory.
    - If finished, the reconstructed local archive path and the files contained within it,
      or a notice if the local directory does not exist.

    Example
    -------
    >>> result = get_tapis_job_metadata(t, "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv")
    >>> if result["exists"]:
    ...     print("Archived job data at:", result["local_path"])
    ...     print("Files:", result["files"])
    ... else:
    ...     print(result.get("message", "Job not yet completed."))
    """

    # Silvia Mazzoni, 2025
    import os
    import json
    if printAll:
        import ipywidgets as widgets
        from IPython.display import display, clear_output
        metadata_out = widgets.Output()
        metadata_accordion = widgets.Accordion(children=[metadata_out])
        metadata_accordion.set_title(0, f'Job Metadata   ({jobUuid})')
        metadata_accordion.selected_index = 0
        display(metadata_accordion)

    job_response = t.jobs.getJob(jobUuid=jobUuid)
    job_dict_all = json.loads(json.dumps(job_response, default=lambda o: vars(o)))
    if printAll:
        with metadata_out:
            # print('+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
            print('+++++++++++++++++++++++++')
            print('++++++ Job Metadata')
            print('+++++++++++++++++++++++++')
    
            print('+ uuid:      ', job_response.uuid)
            print('+ name:      ', job_response.name)
            print('+ status:    ', job_response.status)
            print('+ appId:     ', job_response.appId)
            print('+ created:   ', job_response.created)
            print('+ output-file location')
            print('+ archiveSystemId:', job_response.archiveSystemId)
            print('+ archiveSystemDir:', job_response.archiveSystemDir)


    # ------
    execSystemId = job_response.execSystemId
    archiveSystemId = job_response.archiveSystemId
    sysPath = ''
    if archiveSystemId == 'designsafe.storage.default':
        sysPath = 'MyData'; 
    elif archiveSystemId in ['Work','work','cloud.data','stampede3',execSystemId]:
        sysPath = f'Work/{execSystemId}'; 
    if archiveSystemId == 'designsafe.storage.community':
        sysPath = 'CommunityData'; ## but you can't write to community
    if archiveSystemId == 'designsafe.storage.published':
        sysPath = 'Published'; ## but you can't write to published!
    
    archiveSystemDir = job_dict_all['archiveSystemDir']
    archiveSystemDir_user = archiveSystemDir.split('tapis-jobs-archive'+os.path.sep)[1] # remove the first character slash

    
    archiveSystemDir_user = os.path.join(sysPath,'tapis-jobs-archive',archiveSystemDir_user)
    
    archiveSystemDir_out = archiveSystemDir_user
    if job_response.appId in ["opensees-mp-s3","opensees-sp-s3"]:
        archiveSystemDir_out = os.path.join(archiveSystemDir_out,'inputDirectory')
    elif job_response.appId in ["opensees-express"]:
        fileInputsList =  json.loads(job_dict_all['fileInputs'])
        for fileInputs in fileInputsList:
            if fileInputs['name'] in ['Input Directory'] or fileInputs['envKey'] in ['inputDirectory']:
                sourceUrl = fileInputs['sourceUrl']
                input_folder_end = os.path.basename(sourceUrl)
                archiveSystemDir_out = os.path.join(archiveSystemDir_user,input_folder_end)
                break

    # if sysPath != '':
    archiveSystemDir_user = os.path.expanduser(os.path.join('~',archiveSystemDir_user))
    archiveSystemDir_out = os.path.expanduser(os.path.join('~',archiveSystemDir_out))

    
    job_dict_all['archiveSystemDir_user'] = archiveSystemDir_user
    job_dict_all['archiveSystemDir_out'] = archiveSystemDir_out
    
    if printAll:
        with metadata_out:
            print('+ archiveSystemDir_user:', job_dict_all['archiveSystemDir_user'])
            print('+ archiveSystemDir_out:', job_dict_all['archiveSystemDir_out'])

        
    
            JobInfoKeys = ['','uuid','name','','status','remoteOutcome','condition','lastMessage','','execSystemId','execSystemExecDir','execSystemOutputDir','','appId','appVersion','','tenant','trackingId','createdby','created','description','','execSystemId','execSystemLogicalQueue','nodeCount','coresPerNode','maxMinutes','memoryMB']
            
            # print('\n-- Additional Relevant Job Metadata --')
            print('+ ++++++++++++++++++++++++')
            print('+ +++++ Additional Relevant Job Metadata')
            print('+ ++++++++++++++++++++++++')
            for thisKey in JobInfoKeys:
                if thisKey in job_dict_all.keys():
                    thisValue = job_dict_all[thisKey]
                    # myJobTapisData[thisKey] = thisValue
                    print(f'+ - {thisKey}: {thisValue}')
                else:
                    print('+ ----------------------')
            print('+ ++++++++++++++++++++++++')
        if 'fileInputs' in job_dict_all.keys():

            this_out = widgets.Output()
            this_accordion = widgets.Accordion(children=[this_out])
            this_accordion.set_title(0, f'fileInputs')
            # this_accordion.selected_index = 0
            
            with metadata_out:
                display(this_accordion)
            with this_out:
            
                print('')
                print('#'*32)
                print('### fileInputs')
                print('#'*32)
                fileInputsList =  json.loads(job_dict_all['fileInputs'])
                for thisLine in fileInputsList:
                    print('# ' + '+'*30)
                    for thisKey,thisKeyVal in thisLine.items():
                        print(f'# + {thisKey} = {thisKeyVal}')
                    print('# ' + '+'*30)
                print('#'*32)
    
        if 'parameterSet' in job_dict_all.keys():

            this_out = widgets.Output()
            this_accordion = widgets.Accordion(children=[this_out])
            this_accordion.set_title(0, f'parameterSet')
            # this_accordion.selected_index = 0
            
            with metadata_out:
                display(this_accordion)
            with this_out:
                
                print('')
                print('#'*32)
                print('### parameterSet')
                print('#'*32)
                parameterSetDict =  json.loads(job_dict_all['parameterSet'])
                for thisLineKey,thisLineValues in parameterSetDict.items():
                    print('#')
                    print('# ' + '+'*30)
                    print(f'# ++ {thisLineKey} ')
                    print('# ' + '+'*30)
                    if isinstance(thisLineValues, list):
                        for thisLine in thisLineValues:
                            print('# ' + '+ ' + '-'*28)
                            if isinstance(thisLine, dict):
                                for thisKey,thisVal in thisLine.items():
                                    if thisVal != None:
                                        print(f'# + -  {thisKey} : {thisVal}')
                            else:
                                print('####################not dict',thisLine)
                            print('# ' + '+ ' + '-'*28)
                    elif isinstance(thisLineValues, dict):
                        for thisKey,thisVal in thisLineValues.items():
                            if thisVal != None:
                                print(f'# +  {thisKey} : {thisVal}')
                    else:
                        print('not list nor dict',thisLine)
                    print('# ' + '+'*30)
                print('#'*32)
        
    return job_dict_all
    




JobMetadata = OpsUtils.get_tapis_job_metadata(t,jobUuid)
JobMetadata = OpsUtils.get_tapis_job_metadata(t,'5d2d6e55-3074-4632-8e8d-1ed63a730f5f-007') # opensees-express job
JobMetadata = OpsUtils.get_tapis_job_metadata(t,'b35ab3c2-0436-4c98-9066-367b9db67f9c-007') # this job was submitted and failed
JobMetadata = OpsUtils.get_tapis_job_metadata(t,'a3c514c4-d488-40d0-9efe-0e0a77039033-007') # this job also failed

Collect Metadata for your own processing#

jobUuid = '4dfa35e1-15cd-48fd-a090-f348544dee1f-007'
JobMetadata = OpsUtils.get_tapis_job_metadata(t,jobUuid,printAll=False) # don't print, we will look at the data

SCRATCH-Folder path#

This is the location where the work was actually done!
This is where you can find your files while the job is running as well as if file-transfer to the archive path failed

execSystemOutputDir = JobMetadata['execSystemOutputDir']
print('execSystemOutputDir',execSystemOutputDir)
execSystemOutputDir /scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007

ARCHIVE-Folder path#

This is the location where the final location of your job files.
They are transferred here from the scratch path, you can now move them to your desired location.

archiveSystemDir = JobMetadata['archiveSystemDir']
print('archiveSystemDir',archiveSystemDir)
if os.path.exists(archiveSystemDir):
    print(os.listdir(archiveSystemDir))
else:
    print('no such directory')
archiveSystemDir /silvia/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007
no such directory
archiveSystemDir_user = JobMetadata['archiveSystemDir_user']
print('archiveSystemDir_user',archiveSystemDir_user)
if os.path.exists(archiveSystemDir_user):
    print(os.listdir(archiveSystemDir_user))
else:
    print('no such directory')
archiveSystemDir_user /home/jupyter/MyData/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007
['inputDirectory', 'tapisjob.sh', 'opensees.zip', 'tapisjob.env', 'tapisjob.out', 'tapisjob_app.sh', '.ipynb_checkpoints']
archiveSystemDir_out = JobMetadata['archiveSystemDir_out']
print('archiveSystemDir_out',archiveSystemDir_out)
if os.path.exists(archiveSystemDir_out):
    print(os.listdir(archiveSystemDir_out))
else:
    print('no such directory')
archiveSystemDir_out /home/jupyter/MyData/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007/inputDirectory
['DataTCLmp', 'Ex1a.tcl.Canti2D.Push.mp.tcl', 'Ex1many_end.tcl.Canti2D.Push.mp.tcl', '.ipynb_checkpoints', 'Ex1many-Copy1.tcl.Canti2D.Push.mp.tcl', 'Ex1a.py.Canti2D.Push.py', 'Ex1a.tcl.Canti2D.Push.tcl', 'Ex1many_start.tcl.Canti2D.Push.mp.tcl', 'Ex1a.py.Canti2D.Push.mpi4py.py', 'Ex1a.py.Canti2D.Push.mpi.py']

Display all Metadata#

display(JobMetadata)
{'id': 42126,
 'name': 'opensees-mp-s3-latest_2025-05-07T22:13:08',
 'owner': 'silvia',
 'tenant': 'designsafe',
 'description': 'opensees-mp-s3-latest submitted by silvia@designsafe',
 'status': 'FINISHED',
 'condition': 'NORMAL_COMPLETION',
 'lastMessage': 'Setting job status to FINISHED.',
 'created': '2025-05-07T22:15:14.785522Z',
 'ended': '2025-05-07T22:20:52.718025Z',
 'lastUpdated': '2025-05-07T22:20:52.718025Z',
 'uuid': '4dfa35e1-15cd-48fd-a090-f348544dee1f-007',
 'appId': 'opensees-mp-s3',
 'appVersion': 'latest',
 'archiveOnAppError': True,
 'dynamicExecSystem': False,
 'execSystemId': 'stampede3',
 'execSystemExecDir': '/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007',
 'execSystemInputDir': '/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007',
 'execSystemOutputDir': '/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007',
 'execSystemLogicalQueue': 'skx-dev',
 'archiveSystemId': 'designsafe.storage.default',
 'archiveSystemDir': '/silvia/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007',
 'dtnSystemId': None,
 'dtnSystemInputDir': None,
 'dtnSystemOutputDir': None,
 'nodeCount': 2,
 'coresPerNode': 48,
 'memoryMB': 192000,
 'maxMinutes': 100,
 'fileInputs': '[{"name": "Input Directory", "notes": "{\\"selectionMode\\":\\"directory\\"}", "envKey": "inputDirectory", "optional": false, "sourceUrl": "tapis://designsafe.storage.default/silvia/OpenSees/repos/training-OpenSees-on-DesignSafe/_static/OpenSeesScripts", "targetPath": "inputDirectory", "description": "Input directory that includes the tcl script as well as any other required files. Example input is in tapis://designsafe.storage.community/app_examples/opensees/OpenSeesMP", "autoMountLocal": true, "srcSharedAppCtx": "", "destSharedAppCtx": "wma_prtl"}]',
 'parameterSet': '{"appArgs": [{"arg": "OpenSeesMP", "name": "mainProgram", "notes": "{\\"isHidden\\":true}", "include": null, "description": null}, {"arg": "Ex1many_end.tcl.Canti2D.Push.mp.tcl", "name": "Main Script", "notes": "{\\"inputType\\":\\"fileInput\\"}", "include": null, "description": "The filename only of the OpenSees TCL script to execute. This file should reside in the Input Directory specified. To use with test input, use \'freeFieldEffective.tcl\'"}], "logConfig": {"stderrFilename": "tapisjob.out", "stdoutFilename": "tapisjob.out"}, "envVariables": [{"key": "_tapisAppId", "notes": null, "value": "opensees-mp-s3", "include": null, "description": null}, {"key": "_tapisAppVersion", "notes": null, "value": "latest", "include": null, "description": null}, {"key": "_tapisArchiveOnAppError", "notes": null, "value": "true", "include": null, "description": null}, {"key": "_tapisArchiveSystemDir", "notes": null, "value": "/silvia/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007", "include": null, "description": null}, {"key": "_tapisArchiveSystemId", "notes": null, "value": "designsafe.storage.default", "include": null, "description": null}, {"key": "_tapisCoresPerNode", "notes": null, "value": "48", "include": null, "description": null}, {"key": "_tapisDynamicExecSystem", "notes": null, "value": "false", "include": null, "description": null}, {"key": "_tapisEffectiveUserId", "notes": null, "value": "silvia", "include": null, "description": null}, {"key": "_tapisExecSystemExecDir", "notes": null, "value": "/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007", "include": null, "description": null}, {"key": "_tapisExecSystemHPCQueue", "notes": null, "value": "skx-dev", "include": null, "description": null}, {"key": "_tapisExecSystemId", "notes": null, "value": "stampede3", "include": null, "description": null}, {"key": "_tapisExecSystemInputDir", "notes": null, "value": "/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007", "include": null, "description": null}, {"key": "_tapisExecSystemLogicalQueue", "notes": null, "value": "skx-dev", "include": null, "description": null}, {"key": "_tapisExecSystemOutputDir", "notes": null, "value": "/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007", "include": null, "description": null}, {"key": "_tapisJobCreateDate", "notes": null, "value": "2025-05-07Z", "include": null, "description": null}, {"key": "_tapisJobCreateTime", "notes": null, "value": "22:15:14.785522177Z", "include": null, "description": null}, {"key": "_tapisJobCreateTimestamp", "notes": null, "value": "2025-05-07T22:15:14.785522177Z", "include": null, "description": null}, {"key": "_tapisJobName", "notes": null, "value": "opensees-mp-s3-latest_2025-05-07T22:13:08", "include": null, "description": null}, {"key": "_tapisJobOwner", "notes": null, "value": "silvia", "include": null, "description": null}, {"key": "_tapisJobUUID", "notes": null, "value": "4dfa35e1-15cd-48fd-a090-f348544dee1f-007", "include": null, "description": null}, {"key": "_tapisJobWorkingDir", "notes": null, "value": "/scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007", "include": null, "description": null}, {"key": "_tapisMaxMinutes", "notes": null, "value": "100", "include": null, "description": null}, {"key": "_tapisMemoryMB", "notes": null, "value": "192000", "include": null, "description": null}, {"key": "_tapisNodes", "notes": null, "value": "2", "include": null, "description": null}, {"key": "_tapisStderrFilename", "notes": null, "value": "tapisjob.out", "include": null, "description": null}, {"key": "_tapisStdoutFilename", "notes": null, "value": "tapisjob.out", "include": null, "description": null}, {"key": "_tapisSysBatchScheduler", "notes": null, "value": "SLURM", "include": null, "description": null}, {"key": "_tapisSysHost", "notes": null, "value": "stampede3.tacc.utexas.edu", "include": null, "description": null}, {"key": "_tapisSysRootDir", "notes": null, "value": "/", "include": null, "description": null}, {"key": "_tapisTenant", "notes": null, "value": "designsafe", "include": null, "description": null}, {"key": "inputDirectory", "notes": "{}", "value": "inputDirectory", "include": true, "description": "EnvKey from input file: Input Directory"}], "archiveFilter": {"excludes": [], "includes": [], "includeLaunchFiles": true}, "containerArgs": [], "schedulerOptions": [{"arg": "--tapis-profile OpenSees_default", "name": "OpenSees TACC Scheduler Profile", "notes": "{\\"isHidden\\":true}", "include": null, "description": "Scheduler profile for the default version of OpenSees"}, {"arg": "-A DS-HPC1", "name": "TACC Allocation", "notes": null, "include": null, "description": "The TACC allocation associated with this job execution"}]}',
 'execSystemConstraints': None,
 'subscriptions': '[{"enabled": true, "ttlMinutes": 10080, "description": "Portal job status notification", "deliveryTargets": [{"deliveryMethod": "WEBHOOK", "deliveryAddress": "https://www.designsafe-ci.org/webhooks/jobs/"}], "eventCategoryFilter": "JOB_NEW_STATUS"}]',
 'blockedCount': 0,
 'remoteJobId': '1874224',
 'remoteJobId2': None,
 'remoteOutcome': 'FINISHED',
 'remoteResultInfo': '0:0',
 'remoteQueue': None,
 'remoteSubmitted': '2025-05-07T22:16:49.606851Z',
 'remoteStarted': '2025-05-07T22:16:51.791782Z',
 'remoteEnded': '2025-05-07T22:17:17.601968Z',
 'remoteSubmitRetries': 0,
 'remoteChecksSuccess': 4,
 'remoteChecksFailed': 0,
 'remoteLastStatusCheck': '2025-05-07T22:17:17.595078Z',
 'inputTransactionId': '2510a293-a91d-481c-baf3-4f89a9ed998a',
 'inputCorrelationId': '6a36ee1d-2b05-4380-a221-e27dd8bd66b0',
 'archiveTransactionId': 'ad89111a-222a-4e71-9fd2-4691f15cafed',
 'archiveCorrelationId': '2f7852d4-9c67-470d-aef0-64d1341f1f5f',
 'stageAppTransactionId': '0e2ea3d8-1093-42f0-b8d9-7a3c9d185b33',
 'stageAppCorrelationId': 'b616a64e-a8f6-4a65-9ed0-cb3e29a58869',
 'dtnInputTransactionId': None,
 'dtnInputCorrelationId': None,
 'dtnOutputTransactionId': None,
 'dtnOutputCorrelationId': None,
 'tapisQueue': 'tapis.jobq.submit.DefaultQueue',
 'visible': True,
 'createdby': 'silvia',
 'createdbyTenant': 'designsafe',
 'tags': ['portalName: DESIGNSAFE'],
 'jobType': 'BATCH',
 'isMpi': False,
 'mpiCmd': None,
 'cmdPrefix': None,
 'sharedAppCtx': 'wma_prtl',
 'sharedAppCtxAttribs': ['SAC_EXEC_SYSTEM_ID',
  'SAC_EXEC_SYSTEM_INPUT_DIR',
  'SAC_EXEC_SYSTEM_EXEC_DIR',
  'SAC_EXEC_SYSTEM_OUTPUT_DIR'],
 'trackingId': 'portals.tjpzgzipxddro0y9h1f8iqna904lyphy',
 'notes': '{"icon": "OpenSees", "label": "OpenSeesMP", "helpUrl": "https://www.designsafe-ci.org/user-guide/tools/simulation/#opensees-user-guide", "category": "Simulation", "isInteractive": false, "hideNodeCountAndCoresPerNode": false}',
 '_fileInputsSpec': None,
 '_parameterSetModel': None,
 'archiveSystemDir_user': '/home/jupyter/MyData/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007',
 'archiveSystemDir_out': '/home/jupyter/MyData/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007/inputDirectory'}
import json
print('#'*32)
print('### fileInputs')
print('#'*32)
fileInputsList =  json.loads(JobMetadata['fileInputs'])
for thisLine in fileInputsList:
    print('# ' + '+'*30)
    for thisKey,thisKeyVal in thisLine.items():
        print(f'# + {thisKey} = {thisKeyVal}')
    print('# ' + '+'*30)
print('#'*32)
################################
### fileInputs
################################
# ++++++++++++++++++++++++++++++
# + name = Input Directory
# + notes = {"selectionMode":"directory"}
# + envKey = inputDirectory
# + optional = False
# + sourceUrl = tapis://designsafe.storage.default/silvia/OpenSees/repos/training-OpenSees-on-DesignSafe/_static/OpenSeesScripts
# + targetPath = inputDirectory
# + description = Input directory that includes the tcl script as well as any other required files. Example input is in tapis://designsafe.storage.community/app_examples/opensees/OpenSeesMP
# + autoMountLocal = True
# + srcSharedAppCtx = 
# + destSharedAppCtx = wma_prtl
# ++++++++++++++++++++++++++++++
################################
import json
print('#'*32)
print('### parameterSet')
print('#'*32)
parameterSetDict =  json.loads(JobMetadata['parameterSet'])
for thisLineKey,thisLineValues in parameterSetDict.items():
    print('#')
    print('# ' + '+'*30)
    print(f'# ++ {thisLineKey} ')
    print('# ' + '+'*30)
    if isinstance(thisLineValues, list):
        for thisLine in thisLineValues:
            print('# ' + '+ ' + '-'*28)
            if isinstance(thisLine, dict):
                for thisKey,thisVal in thisLine.items():
                    if thisVal != None:
                        print(f'# + -  {thisKey} : {thisVal}')
            else:
                print('####################not dict',thisLine)
            print('# ' + '+ ' + '-'*28)
    elif isinstance(thisLineValues, dict):
        for thisKey,thisVal in thisLineValues.items():
            if thisVal != None:
                print(f'# +  {thisKey} : {thisVal}')
    else:
        print('not list nor dict',thisLine)
    print('# ' + '+'*30)
print('#'*32)
        
################################
### parameterSet
################################
#
# ++++++++++++++++++++++++++++++
# ++ appArgs 
# ++++++++++++++++++++++++++++++
# + ----------------------------
# + -  arg : OpenSeesMP
# + -  name : mainProgram
# + -  notes : {"isHidden":true}
# + ----------------------------
# + ----------------------------
# + -  arg : Ex1many_end.tcl.Canti2D.Push.mp.tcl
# + -  name : Main Script
# + -  notes : {"inputType":"fileInput"}
# + -  description : The filename only of the OpenSees TCL script to execute. This file should reside in the Input Directory specified. To use with test input, use 'freeFieldEffective.tcl'
# + ----------------------------
# ++++++++++++++++++++++++++++++
#
# ++++++++++++++++++++++++++++++
# ++ logConfig 
# ++++++++++++++++++++++++++++++
# +  stderrFilename : tapisjob.out
# +  stdoutFilename : tapisjob.out
# ++++++++++++++++++++++++++++++
#
# ++++++++++++++++++++++++++++++
# ++ envVariables 
# ++++++++++++++++++++++++++++++
# + ----------------------------
# + -  key : _tapisAppId
# + -  value : opensees-mp-s3
# + ----------------------------
# + ----------------------------
# + -  key : _tapisAppVersion
# + -  value : latest
# + ----------------------------
# + ----------------------------
# + -  key : _tapisArchiveOnAppError
# + -  value : true
# + ----------------------------
# + ----------------------------
# + -  key : _tapisArchiveSystemDir
# + -  value : /silvia/tapis-jobs-archive/2025-05-07Z/opensees-mp-s3-latest_2025-05-07T22:13:08-4dfa35e1-15cd-48fd-a090-f348544dee1f-007
# + ----------------------------
# + ----------------------------
# + -  key : _tapisArchiveSystemId
# + -  value : designsafe.storage.default
# + ----------------------------
# + ----------------------------
# + -  key : _tapisCoresPerNode
# + -  value : 48
# + ----------------------------
# + ----------------------------
# + -  key : _tapisDynamicExecSystem
# + -  value : false
# + ----------------------------
# + ----------------------------
# + -  key : _tapisEffectiveUserId
# + -  value : silvia
# + ----------------------------
# + ----------------------------
# + -  key : _tapisExecSystemExecDir
# + -  value : /scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007
# + ----------------------------
# + ----------------------------
# + -  key : _tapisExecSystemHPCQueue
# + -  value : skx-dev
# + ----------------------------
# + ----------------------------
# + -  key : _tapisExecSystemId
# + -  value : stampede3
# + ----------------------------
# + ----------------------------
# + -  key : _tapisExecSystemInputDir
# + -  value : /scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007
# + ----------------------------
# + ----------------------------
# + -  key : _tapisExecSystemLogicalQueue
# + -  value : skx-dev
# + ----------------------------
# + ----------------------------
# + -  key : _tapisExecSystemOutputDir
# + -  value : /scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobCreateDate
# + -  value : 2025-05-07Z
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobCreateTime
# + -  value : 22:15:14.785522177Z
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobCreateTimestamp
# + -  value : 2025-05-07T22:15:14.785522177Z
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobName
# + -  value : opensees-mp-s3-latest_2025-05-07T22:13:08
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobOwner
# + -  value : silvia
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobUUID
# + -  value : 4dfa35e1-15cd-48fd-a090-f348544dee1f-007
# + ----------------------------
# + ----------------------------
# + -  key : _tapisJobWorkingDir
# + -  value : /scratch/05072/silvia/tapis/4dfa35e1-15cd-48fd-a090-f348544dee1f-007
# + ----------------------------
# + ----------------------------
# + -  key : _tapisMaxMinutes
# + -  value : 100
# + ----------------------------
# + ----------------------------
# + -  key : _tapisMemoryMB
# + -  value : 192000
# + ----------------------------
# + ----------------------------
# + -  key : _tapisNodes
# + -  value : 2
# + ----------------------------
# + ----------------------------
# + -  key : _tapisStderrFilename
# + -  value : tapisjob.out
# + ----------------------------
# + ----------------------------
# + -  key : _tapisStdoutFilename
# + -  value : tapisjob.out
# + ----------------------------
# + ----------------------------
# + -  key : _tapisSysBatchScheduler
# + -  value : SLURM
# + ----------------------------
# + ----------------------------
# + -  key : _tapisSysHost
# + -  value : stampede3.tacc.utexas.edu
# + ----------------------------
# + ----------------------------
# + -  key : _tapisSysRootDir
# + -  value : /
# + ----------------------------
# + ----------------------------
# + -  key : _tapisTenant
# + -  value : designsafe
# + ----------------------------
# + ----------------------------
# + -  key : inputDirectory
# + -  notes : {}
# + -  value : inputDirectory
# + -  include : True
# + -  description : EnvKey from input file: Input Directory
# + ----------------------------
# ++++++++++++++++++++++++++++++
#
# ++++++++++++++++++++++++++++++
# ++ archiveFilter 
# ++++++++++++++++++++++++++++++
# +  excludes : []
# +  includes : []
# +  includeLaunchFiles : True
# ++++++++++++++++++++++++++++++
#
# ++++++++++++++++++++++++++++++
# ++ containerArgs 
# ++++++++++++++++++++++++++++++
# ++++++++++++++++++++++++++++++
#
# ++++++++++++++++++++++++++++++
# ++ schedulerOptions 
# ++++++++++++++++++++++++++++++
# + ----------------------------
# + -  arg : --tapis-profile OpenSees_default
# + -  name : OpenSees TACC Scheduler Profile
# + -  notes : {"isHidden":true}
# + -  description : Scheduler profile for the default version of OpenSees
# + ----------------------------
# + ----------------------------
# + -  arg : -A DS-HPC1
# + -  name : TACC Allocation
# + -  description : The TACC allocation associated with this job execution
# + ----------------------------
# ++++++++++++++++++++++++++++++
################################