App Schema: opensees-express#
Dig Deep into an App Schema to Create the Input
by Silvia Mazzoni, DesignSafe, 2025
We are going to walk through the app schema to develop our job input, and then we will submit the job.
We will use the opensees-express app.
(OpenSees-Express GitHub Page)[https://github.com/TACC/WMA-Tapis-Templates/tree/main/applications/opensees-express]
(Tapis Apps Documentation)[https://tapis.readthedocs.io/en/latest/technical/apps.html]
(Tapis Jobs Documentation)[https://tapis.readthedocs.io/en/latest/technical/jobs.html]
from tapipy.tapis import TapisResult
Connect to Tapis#
t=OpsUtils.connect_tapis()
-- Checking Tapis token --
Token loaded from file. Token is still valid!
Token expires at: 2025-09-19T21:03:18+00:00
Token expires in: 2:47:28.669912
-- LOG IN SUCCESSFUL! --
App User Input#
Initialize#
# initalize
tapisInput = {}
tapisInput["name"] = 'OpenSeesExpressSubmit'
appId = 'opensees-express'
appVersion = 'latest'
Get app schema – input#
thisAppData_MP = OpsUtils.get_tapis_app_schema(t,appId,version=appVersion)
OpsUtils.display_tapis_app_schema(thisAppData_MP)
########################################
########### TAPIS-APP SCHEMA ###########
########################################
######## appID: opensees-express
######## version: latest
########################################
{
sharedAppCtx: "wma_prtl"
isPublic: True
tenant: "designsafe"
id: "opensees-express"
version: "latest"
description: "OpenSees-EXPRESS provides users with a sequential OpenSees interpreter. It is ideal to run small sequential scripts on DesignSafe resources freeing up your own machine."
owner: "wma_prtl"
enabled: True
versionEnabled: True
locked: False
runtime: "ZIP"
runtimeVersion: None
runtimeOptions: None
containerImage: "tapis://cloud.data/corral/tacc/aci/CEP/applications/v3/opensees/latest/OpenSees-EXPRESS/opensees_express.zip"
jobType: "FORK"
maxJobs: 2147483647
maxJobsPerUser: 2147483647
strictFileInputs: True
uuid: "30cb1fa1-e7c7-44a8-a0e8-d2f64043fc65"
deleted: False
created: "2025-02-20T18:41:03.661272Z"
updated: "2025-07-30T20:19:29.367292Z"
sharedWithUsers: []
tags: ["portalName: DesignSafe", "portalName: CEP"]
jobAttributes: {
description: None
dynamicExecSystem: False
execSystemConstraints: None
execSystemId: "wma-exec-01"
execSystemExecDir: "${JobWorkingDir}"
execSystemInputDir: "${JobWorkingDir}"
execSystemOutputDir: "${JobWorkingDir}"
dtnSystemInputDir: "!tapis_not_set"
dtnSystemOutputDir: "!tapis_not_set"
execSystemLogicalQueue: None
archiveSystemId: "cloud.data"
archiveSystemDir: "/tmp/${JobOwner}/tapis-jobs-archive/${JobCreateDate}/${JobName}-${JobUUID}"
archiveOnAppError: True
isMpi: False
mpiCmd: None
cmdPrefix: None
nodeCount: 1
coresPerNode: 1
memoryMB: 100
maxMinutes: 1440
fileInputs: [
{
name: "Input Directory"
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/OpenSeesEXPRESS"
inputMode: "REQUIRED"
autoMountLocal: True
envKey: "inputDirectory"
sourceUrl: None
targetPath: "*"
notes: {
selectionMode: "directory"
}
}
]
fileInputArrays: []
subscriptions: []
tags: []
parameterSet: {
appArgs: []
containerArgs: []
schedulerOptions: []
envVariables: [
{
key: "mainProgram"
value: "OpenSees"
description: "Choose the OpenSees binary to use."
inputMode: "REQUIRED"
notes: {
label: "Main Program"
enum_values: [
{
OpenSees: "OpenSees"
}
{
OpenSeesSP: "OpenSeesSP"
}
]
}
}
{
key: "tclScript"
value: ""
description: "The filename of the OpenSees TCL script to execute, e.g. "freeFieldEffective.tcl"."
inputMode: "REQUIRED"
notes: {
label: "Main Script"
inputType: "fileInput"
}
}
]
archiveFilter: {
includeLaunchFiles: True
includes: []
excludes: ["opensees-express.zip", "tapisjob.env"]
}
logConfig: {
stdoutFilename: ""
stderrFilename: ""
}
}
}
notes: {
icon: "OpenSees"
label: "OpenSees-EXPRESS (VM)"
helpUrl: "https://www.designsafe-ci.org/user-guide/tools/simulation/#opensees-user-guide"
category: "Simulation"
isInteractive: False
hideNodeCountAndCoresPerNode: True
}
}
########################################
Create a Job input#
# Initialize our input
TapisInput = {}
Break app schema into detailed parts and review them for possible input arguments#
# Convert TapisResults Objects to dictionaries
app_MetaData = thisAppData_MP.__dict__
# Review the keys to see if there is any input we are interested in.
# You can view the full schema above to see the values
# print('app_MetaData.keys',app_MetaData.keys())
dictKeys = []
TapisResultKeys = []
listKeys = []
print('*** MAIN INPUT***')
for thisKey,thisValue in app_MetaData.items():
if isinstance(thisValue, dict):
dictKeys.append(thisKey)
if isinstance(thisValue, TapisResult):
TapisResultKeys.append(thisKey)
elif isinstance(thisValue, list):
listKeys.append(thisKey)
else:
print(f'{thisKey} = {thisValue}')
print('\n--- Nested Objects---')
print('dict-type input keys',dictKeys)
print('list-type input keys',listKeys)
print('TapisResult-type input keys',TapisResultKeys)
*** MAIN INPUT***
sharedAppCtx = wma_prtl
isPublic = True
tenant = designsafe
id = opensees-express
version = latest
description = OpenSees-EXPRESS provides users with a sequential OpenSees interpreter. It is ideal to run small sequential scripts on DesignSafe resources freeing up your own machine.
owner = wma_prtl
enabled = True
versionEnabled = True
locked = False
runtime = ZIP
runtimeVersion = None
runtimeOptions = None
containerImage = tapis://cloud.data/corral/tacc/aci/CEP/applications/v3/opensees/latest/OpenSees-EXPRESS/opensees_express.zip
jobType = FORK
maxJobs = 2147483647
maxJobsPerUser = 2147483647
strictFileInputs = True
uuid = 30cb1fa1-e7c7-44a8-a0e8-d2f64043fc65
deleted = False
created = 2025-02-20T18:41:03.661272Z
updated = 2025-07-30T20:19:29.367292Z
--- Nested Objects---
dict-type input keys []
list-type input keys ['sharedWithUsers', 'tags']
TapisResult-type input keys ['jobAttributes', 'notes']
Check App Basics#
A few things to check here:
app name and version are what you want
isPublic = True (you can use the app)
if isPublic = False, make sure owner = usename
enabled = True (app is usable)
deleted = False (it exists)
read the description
print(f'***** list-type INPUT ****')
if len(listKeys)>0:
for thisKey in listKeys:
thisList = app_MetaData[thisKey]
print(f'{thisKey} : {thisList}')
else:
print('-none')
***** list-type INPUT ****
sharedWithUsers : []
tags : ['portalName: DesignSafe', 'portalName: CEP']
# Nothing related to input in the lists.
print(f'***** dict-type INPUT ****')
if len(dictKeys)>0:
for thisKey in dictKeys:
thisDict = app_MetaData[thisKey]
print(f'{thisKey} : {thisDict}')
else:
print('there are no dictionaries within jobAttributes, as expected, since Tapis works with TapisResults objects instead')
***** dict-type INPUT ****
there are no dictionaries within jobAttributes, as expected, since Tapis works with TapisResults objects instead
print(f'***** TapisResult-type INPUT ****')
TR_dictKeys = {}
TR_TapisResultKeys = {}
TR_listKeys = {}
for hereKey in TapisResultKeys:
print(f'\n*** {hereKey} ***')
thisTapisResult = app_MetaData[hereKey]
thisTapisResult_dict = thisTapisResult.__dict__
TR_dictKeys[hereKey] = []
TR_TapisResultKeys[hereKey] = []
TR_listKeys[hereKey] = []
for thisKey,thisValue in thisTapisResult_dict.items():
if isinstance(thisValue, dict):
TR_dictKeys[hereKey].append(thisKey)
if isinstance(thisValue, TapisResult):
TR_TapisResultKeys[hereKey].append(thisKey)
elif isinstance(thisValue, list):
TR_listKeys[hereKey].append(thisKey)
else:
print(f' {thisKey} = {thisValue}')
print(f'\n --- {hereKey} -- Nested Objects---')
print(f' dict-type input keys',TR_dictKeys[hereKey])
print(f' list-type input keys',TR_listKeys[hereKey])
print(f' TapisResult-type input keys',TR_TapisResultKeys[hereKey])
print('')
***** TapisResult-type INPUT ****
*** jobAttributes ***
description = None
dynamicExecSystem = False
execSystemConstraints = None
execSystemId = wma-exec-01
execSystemExecDir = ${JobWorkingDir}
execSystemInputDir = ${JobWorkingDir}
execSystemOutputDir = ${JobWorkingDir}
dtnSystemInputDir = !tapis_not_set
dtnSystemOutputDir = !tapis_not_set
execSystemLogicalQueue = None
archiveSystemId = cloud.data
archiveSystemDir = /tmp/${JobOwner}/tapis-jobs-archive/${JobCreateDate}/${JobName}-${JobUUID}
archiveOnAppError = True
isMpi = False
mpiCmd = None
cmdPrefix = None
nodeCount = 1
coresPerNode = 1
memoryMB = 100
maxMinutes = 1440
--- jobAttributes -- Nested Objects---
dict-type input keys []
list-type input keys ['fileInputs', 'fileInputArrays', 'subscriptions', 'tags']
TapisResult-type input keys ['parameterSet']
*** notes ***
icon = OpenSees
label = OpenSees-EXPRESS (VM)
helpUrl = https://www.designsafe-ci.org/user-guide/tools/simulation/#opensees-user-guide
category = Simulation
isInteractive = False
hideNodeCountAndCoresPerNode = True
--- notes -- Nested Objects---
dict-type input keys []
list-type input keys []
TapisResult-type input keys []
notes {}#
notes is just informational.
jobAttributes {}#
This is the content we will be submitting to the app.
jobAttributes has valuable input arguments as well as nested ones.
We need to initialize this json object in our TapisInput.
Let’s look at the high-level input
Let’s look at the nested content
Initialize TapisInput[‘jobAttributes’]
TapisInput['jobAttributes'] = {}
# start this input with app id and version to jobAttributes since that is what we send to tapis
TapisInput['jobAttributes']["name"] = f'My first Tapis Job on {appId}'
TapisInput['jobAttributes']["appId"] = appId
TapisInput['jobAttributes']["appVersion"] = appVersion
Look at high-level variables to see if there is anything of value
This is where you find information about where the job is run (execSystemId)
In HPC applications this is where you define the SLURM input on queues and nodes, etc
# we see that OpenSees-Express is NOT run on stampede3, but on a Virtual Machine, no need to specify it
# TapisInput['jobAttributes']['execSystemId'] = 'wma-exec-01'
# slurmm-job input
# there is NO SLURM-JOB INPUT for Opensees Express, but we do have maxMinutes
TapisInput['jobAttributes']['maxMinutes'] = 7
3. Let’s dig into the first level – app.jobAttributes#
# Let's extract and study jobAttributes
myKey = 'jobAttributes'
app_jobAttributes = app_MetaData[myKey].__dict__
# we have already extracted the contents of this dict
print(f'***** {myKey} dict-type Input ****')
if len(TR_dictKeys[myKey])>0:
for thisKey in TR_dictKeys[myKey]:
thisDict = app_jobAttributes[thisKey]
print(f'{myKey}.{thisKey} : {thisDict}')
else:
print('there are no dictionaries within jobAttributes, as expected, since Tapis works with TapisResults objects instead')
***** jobAttributes dict-type Input ****
there are no dictionaries within jobAttributes, as expected, since Tapis works with TapisResults objects instead
print(f'***** {myKey} list-type input****')
if len(TR_listKeys[myKey])>0:
for thisKey in TR_listKeys[myKey]:
thisList = app_jobAttributes[thisKey]
if len(thisList)>0:
print(f' {thisKey} = ')
print(' [')
for thisValue in thisList:
if isinstance(thisValue, TapisResult):
thisValue = thisValue.__dict__
print(f' {thisValue}')
print(' ]')
else:
print(f' {thisKey}: {thisList}')
else:
print('none')
***** jobAttributes list-type input****
fileInputs =
[
{'name': 'Input Directory', '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/OpenSeesEXPRESS', 'inputMode': 'REQUIRED', 'autoMountLocal': True, 'envKey': 'inputDirectory', 'notes':
selectionMode: directory, 'sourceUrl': None, 'targetPath': '*'}
]
fileInputArrays: []
subscriptions: []
tags: []
fileInputs#
Here we find that jobAttributes.fileInputs is a REQUIRED input.
In this case we need to get the tapisURI for our input directory.
Storage SystemTapis & Tapis Base Path in URI format#
this is the very first part of your path, just above your home folder.
Options:
CommunityData
Published
The following options are user or project-dependent, and require unique path input.
The following option requires additional user-dependent input:
MyData
The following option requires additional user- and system- dependent input:
Work
The following option requires additional project-dependent input:
MyProjects
You can obtain a dependent tapis-URI path by performing the first step of submitting an OpenSeesMP job at the app portal: https://www.designsafe-ci.org/workspace/opensees-mp-s3
storage_system = 'MyData' # options: Community,MyData,Published,MyProjects,Work/stampede3,Work/frontera,Work/ls6
storage_system_baseURL = OpsUtils.get_user_path_tapis_uri(t,storage_system)
print('storage_system_baseURL:',storage_system_baseURL)
found paths file: /home/jupyter/MyData/.tapis_user_paths.json
storage_system_baseURL: tapis://designsafe.storage.default/silvia
input_folder = '_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Examples_OpenSees/BasicExamples'
sourceUrl = f'{storage_system_baseURL}/{input_folder}'
print('sourceUrl',sourceUrl)
sourceUrl tapis://designsafe.storage.default/silvia/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Examples_OpenSees/BasicExamples
TapisInput['jobAttributes']['fileInputs'] = [{'name': 'Input Directory','sourceUrl':sourceUrl}]; # notice that it is a list!
print(f'***** {myKey} TapisResult-type input ****')
if len(TR_TapisResultKeys[myKey])>0:
for hereKey in TR_TapisResultKeys[myKey]:
print(f'\n*** {hereKey} ***')
thisTapisResult = app_jobAttributes[hereKey]
thisTapisResult_dict = thisTapisResult.__dict__
TR_dictKeys[hereKey] = []
TR_TapisResultKeys[hereKey] = []
TR_listKeys[hereKey] = []
for thisKey,thisValue in thisTapisResult_dict.items():
if isinstance(thisValue, dict):
TR_dictKeys[hereKey].append(thisKey)
if isinstance(thisValue, TapisResult):
TR_TapisResultKeys[hereKey].append(thisKey)
elif isinstance(thisValue, list):
TR_listKeys[hereKey].append(thisKey)
else:
print(f' {myKey}.{thisKey} = {thisValue}')
print(f'\n --- {hereKey} -- Nested Objects---')
print(f' {myKey}.{hereKey} dict-type input keys',TR_dictKeys[hereKey])
print(f' {myKey}.{hereKey} list-type input keys',TR_listKeys[hereKey])
print(f' {myKey}.{hereKey} TapisResult-type input keys',TR_TapisResultKeys[hereKey])
print('')
else:
print('-none')
***** jobAttributes TapisResult-type input ****
*** parameterSet ***
--- parameterSet -- Nested Objects---
jobAttributes.parameterSet dict-type input keys []
jobAttributes.parameterSet list-type input keys ['appArgs', 'containerArgs', 'schedulerOptions', 'envVariables']
jobAttributes.parameterSet TapisResult-type input keys ['archiveFilter', 'logConfig']
app.jobAttributes.parameterSet is the interesting one#
# Let's extract and study jobAttributes
myKey = 'parameterSet'
app_parameterSet = app_jobAttributes[myKey].__dict__
# we have already extracted the contents of this dict
## parameterSet is a dictionary (TapisResult):
TapisInput['jobAttributes'][myKey] = {}
print(f'***** {myKey} dict-type Input ****')
if len(TR_dictKeys[myKey])>0:
for thisKey in TR_dictKeys[myKey]:
thisDict = app_parameterSet[thisKey]
print(f'{myKey}.{thisKey} : {thisDict}')
else:
print('there are no dictionaries within app_parameterSet, as expected, since Tapis works with TapisResults objects instead')
***** parameterSet dict-type Input ****
there are no dictionaries within app_parameterSet, as expected, since Tapis works with TapisResults objects instead
print(f'***** {myKey} list-type input****')
if len(TR_listKeys[myKey])>0:
for thisKey in TR_listKeys[myKey]:
thisList = app_parameterSet[thisKey]
if len(thisList)>0:
print(f' {thisKey} = ')
print(' [')
for thisValue in thisList:
if isinstance(thisValue, TapisResult):
thisValue = thisValue.__dict__
print(f' {str(thisValue)}')
print(' ]')
else:
print(f' {thisKey}: {thisList}')
else:
print('none')
***** parameterSet list-type input****
appArgs: []
containerArgs: []
schedulerOptions: []
envVariables =
[
{'key': 'mainProgram', 'value': 'OpenSees', 'description': 'Choose the OpenSees binary to use.', 'inputMode': 'REQUIRED', 'notes':
enum_values: [
OpenSees: OpenSees,
OpenSeesSP: OpenSeesSP]
label: Main Program}
{'key': 'tclScript', 'value': '', 'description': 'The filename of the OpenSees TCL script to execute, e.g. "freeFieldEffective.tcl".', 'inputMode': 'REQUIRED', 'notes':
inputType: fileInput
label: Main Script}
]
While the opensees-mp-s3 and opensees-sp-s3 apps use appArgs, this app uses envVariables for the input.
It looks like enum_values in notes is a list for a pull-down menu, and, interestingly, it is missing OpenSeesMP – from the app definition in github, they Removed because OpenSeesMP is unable to use multiple cores, essentially making it SP
print('review each of these items:', TR_listKeys[myKey])
review each of these items: ['appArgs', 'containerArgs', 'schedulerOptions', 'envVariables']
app.jobAttributes.parameterSet.appArgs#
thisKey = 'appArgs'
TapisInput['jobAttributes']['parameterSet'][thisKey] = [] # it's a list, initialize as a list!
print(f'** app.jobAttributes.parameterSet.{thisKey} **')
if len(app_parameterSet[thisKey])>0:
for thisItem in app_parameterSet[thisKey]:
print(thisItem)
else:
print('.none.')
** app.jobAttributes.parameterSet.appArgs **
.none.
Nothing to input here!
app.jobAttributes.parameterSet.containerArgs#
thisKey = 'containerArgs'
TapisInput['jobAttributes']['parameterSet'][thisKey] = [] # it's a list, initialize as a list!
print(f'** app.jobAttributes.parameterSet.{thisKey} **')
if len(app_parameterSet[thisKey])>0:
for thisItem in app_parameterSet[thisKey]:
print(thisItem)
else:
print('.none.')
** app.jobAttributes.parameterSet.containerArgs **
.none.
# do nothing for this app.
app.jobAttributes.parameterSet.schedulerOptions#
thisKey = 'schedulerOptions'
TapisInput['jobAttributes']['parameterSet'][thisKey] = [] # it's a list, initialize as a list!
print(f'** app.jobAttributes.parameterSet.{thisKey} **')
if len(app_parameterSet[thisKey])>0:
for thisItem in app_parameterSet[thisKey]:
print(thisItem)
else:
print('.none.')
** app.jobAttributes.parameterSet.schedulerOptions **
.none.
There is no scheduler nor allocation used in OpenSees-Express.
NO NEED to add allocation to the scheduler option – not shown in the app schema#
# user_allocation = '-A DS-HPC1'; # you get this code from your allocation dashboard
# TapisInput['jobAttributes']['parameterSet']['schedulerOptions'].append({'name': 'TACC Allocation', 'arg': user_allocation})
app.jobAttributes.parameterSet.envVariables#
thisKey = 'envVariables'
TapisInput['jobAttributes']['parameterSet'][thisKey] = [] # it's a list, initialize as a list!
print(f'** app.jobAttributes.parameterSet.{thisKey} **')
if len(app_parameterSet[thisKey])>0:
for thisItem in app_parameterSet[thisKey]:
print(thisItem)
else:
print('.none.')
** app.jobAttributes.parameterSet.envVariables **
description: Choose the OpenSees binary to use.
inputMode: REQUIRED
key: mainProgram
notes:
enum_values: [
OpenSees: OpenSees,
OpenSeesSP: OpenSeesSP]
label: Main Program
value: OpenSees
description: The filename of the OpenSees TCL script to execute, e.g. "freeFieldEffective.tcl".
inputMode: REQUIRED
key: tclScript
notes:
inputType: fileInput
label: Main Script
value:
YES this app does have input here, we need to specify the execution program and the tcl script#
Note that the names of the labels for the keys here are key and value , not name and arg, and the keys are different from opensees-mp-s3
Make sure your tcl script is for a sequential analysis.
TapisInput['jobAttributes']['parameterSet']['envVariables'].append({"key": "mainProgram", "value": 'OpenSees'})
TapisInput['jobAttributes']['parameterSet']['envVariables'].append({"key": "tclScript", "value": 'Ex1a.Canti2D.Push.tcl'})
print(f'***** {myKey} TapisResult-type input ****')
if len(TR_TapisResultKeys[myKey])>0:
for hereKey in TR_TapisResultKeys[myKey]:
print(f'\n*** {hereKey} ***')
thisTapisResult = app_parameterSet[hereKey]
thisTapisResult_dict = thisTapisResult.__dict__
TR_dictKeys[hereKey] = []
TR_TapisResultKeys[hereKey] = []
TR_listKeys[hereKey] = []
for thisKey,thisValue in thisTapisResult_dict.items():
if isinstance(thisValue, dict):
TR_dictKeys[hereKey].append(thisKey)
if isinstance(thisValue, TapisResult):
TR_TapisResultKeys[hereKey].append(thisKey)
elif isinstance(thisValue, list):
TR_listKeys[hereKey].append(thisKey)
else:
print(f' {myKey}.{thisKey} = {thisValue}')
print(f'\n --- {hereKey} -- Nested Objects---')
print(f' {myKey}.{hereKey} dict-type input keys',TR_dictKeys[hereKey])
print(f' {myKey}.{hereKey} list-type input keys',TR_listKeys[hereKey])
print(f' {myKey}.{hereKey} TapisResult-type input keys',TR_TapisResultKeys[hereKey])
print('')
else:
print('-none')
***** parameterSet TapisResult-type input ****
*** archiveFilter ***
parameterSet.includeLaunchFiles = True
--- archiveFilter -- Nested Objects---
parameterSet.archiveFilter dict-type input keys []
parameterSet.archiveFilter list-type input keys ['includes', 'excludes']
parameterSet.archiveFilter TapisResult-type input keys []
*** logConfig ***
parameterSet.stdoutFilename =
parameterSet.stderrFilename =
--- logConfig -- Nested Objects---
parameterSet.logConfig dict-type input keys []
parameterSet.logConfig list-type input keys []
parameterSet.logConfig TapisResult-type input keys []
# nothing of interest to us here...
We are done with our tapis-job input#
print('TapisInput')
display(TapisInput)
TapisInput
{'jobAttributes': {'name': 'My first Tapis Job on opensees-express',
'appId': 'opensees-express',
'appVersion': 'latest',
'maxMinutes': 7,
'fileInputs': [{'name': 'Input Directory',
'sourceUrl': 'tapis://designsafe.storage.default/silvia/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Examples_OpenSees/BasicExamples'}],
'parameterSet': {'appArgs': [],
'containerArgs': [],
'schedulerOptions': [],
'envVariables': [{'key': 'mainProgram', 'value': 'OpenSees'},
{'key': 'tclScript', 'value': 'Ex1a.Canti2D.Push.tcl'}]}}}
print('TapisInput.jobAttributes')
display(TapisInput['jobAttributes'])
TapisInput.jobAttributes
{'name': 'My first Tapis Job on opensees-express',
'appId': 'opensees-express',
'appVersion': 'latest',
'maxMinutes': 7,
'fileInputs': [{'name': 'Input Directory',
'sourceUrl': 'tapis://designsafe.storage.default/silvia/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Examples_OpenSees/BasicExamples'}],
'parameterSet': {'appArgs': [],
'containerArgs': [],
'schedulerOptions': [],
'envVariables': [{'key': 'mainProgram', 'value': 'OpenSees'},
{'key': 'tclScript', 'value': 'Ex1a.Canti2D.Push.tcl'}]}}
submitted_job = t.jobs.submitJob(**TapisInput['jobAttributes'])
print(submitted_job)
_fileInputsSpec: None
_parameterSetModel: None
appId: opensees-express
appVersion: latest
archiveCorrelationId: None
archiveOnAppError: True
archiveSystemDir: /tmp/silvia/tapis-jobs-archive/2025-09-19Z/My first Tapis Job on opensees-express-f708c941-f974-454d-b869-3687568aba4e-007
archiveSystemId: cloud.data
archiveTransactionId: None
blockedCount: 0
cmdPrefix: None
condition: None
coresPerNode: 1
created: 2025-09-19T18:15:50.754968765Z
createdby: silvia
createdbyTenant: designsafe
description: opensees-express-latest submitted by silvia@designsafe
dtnInputCorrelationId: None
dtnInputTransactionId: None
dtnOutputCorrelationId: None
dtnOutputTransactionId: None
dtnSystemId: None
dtnSystemInputDir: None
dtnSystemOutputDir: None
dynamicExecSystem: False
ended: None
execSystemConstraints: None
execSystemExecDir: /tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007
execSystemId: wma-exec-01
execSystemInputDir: /tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007
execSystemLogicalQueue: None
execSystemOutputDir: /tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007
fileInputs: [{"name":"Input Directory","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/OpenSeesEXPRESS","envKey":"inputDirectory","autoMountLocal":true,"sourceUrl":"tapis://designsafe.storage.default/silvia/_ToCommunityData/OpenSees/TrainingMaterial/training-OpenSees-on-DesignSafe/Examples_OpenSees/BasicExamples","targetPath":"BasicExamples","notes":"{\"selectionMode\":\"directory\"}","optional":false,"srcSharedAppCtx":"","destSharedAppCtx":"wma_prtl"}]
id: 0
inputCorrelationId: None
inputTransactionId: None
isMpi: False
jobType: FORK
lastMessage: Job created
lastUpdated: 2025-09-19T18:15:50.754968765Z
maxMinutes: 7
memoryMB: 100
mpiCmd: None
name: My first Tapis Job on opensees-express
nodeCount: 1
notes: {"icon":"OpenSees","label":"OpenSees-EXPRESS (VM)","helpUrl":"https://www.designsafe-ci.org/user-guide/tools/simulation/#opensees-user-guide","category":"Simulation","isInteractive":false,"hideNodeCountAndCoresPerNode":true}
owner: silvia
parameterSet: {"appArgs":[],"containerArgs":[],"schedulerOptions":[],"envVariables":[{"key":"_tapisAppId","value":"opensees-express","description":null,"include":null,"notes":null},{"key":"_tapisAppVersion","value":"latest","description":null,"include":null,"notes":null},{"key":"_tapisArchiveOnAppError","value":"true","description":null,"include":null,"notes":null},{"key":"_tapisArchiveSystemDir","value":"/tmp/silvia/tapis-jobs-archive/2025-09-19Z/My first Tapis Job on opensees-express-f708c941-f974-454d-b869-3687568aba4e-007","description":null,"include":null,"notes":null},{"key":"_tapisArchiveSystemId","value":"cloud.data","description":null,"include":null,"notes":null},{"key":"_tapisCoresPerNode","value":"1","description":null,"include":null,"notes":null},{"key":"_tapisDynamicExecSystem","value":"false","description":null,"include":null,"notes":null},{"key":"_tapisEffectiveUserId","value":"silvia","description":null,"include":null,"notes":null},{"key":"_tapisExecSystemExecDir","value":"/tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007","description":null,"include":null,"notes":null},{"key":"_tapisExecSystemId","value":"wma-exec-01","description":null,"include":null,"notes":null},{"key":"_tapisExecSystemInputDir","value":"/tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007","description":null,"include":null,"notes":null},{"key":"_tapisExecSystemOutputDir","value":"/tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007","description":null,"include":null,"notes":null},{"key":"_tapisJobCreateDate","value":"2025-09-19Z","description":null,"include":null,"notes":null},{"key":"_tapisJobCreateTime","value":"18:15:50.754968765Z","description":null,"include":null,"notes":null},{"key":"_tapisJobCreateTimestamp","value":"2025-09-19T18:15:50.754968765Z","description":null,"include":null,"notes":null},{"key":"_tapisJobName","value":"My first Tapis Job on opensees-express","description":null,"include":null,"notes":null},{"key":"_tapisJobOwner","value":"silvia","description":null,"include":null,"notes":null},{"key":"_tapisJobUUID","value":"f708c941-f974-454d-b869-3687568aba4e-007","description":null,"include":null,"notes":null},{"key":"_tapisJobWorkingDir","value":"/tmp/tapis-silvia/2025-09-19Z/f708c941-f974-454d-b869-3687568aba4e-007","description":null,"include":null,"notes":null},{"key":"_tapisMaxMinutes","value":"7","description":null,"include":null,"notes":null},{"key":"_tapisMemoryMB","value":"100","description":null,"include":null,"notes":null},{"key":"_tapisNodes","value":"1","description":null,"include":null,"notes":null},{"key":"_tapisStderrFilename","value":"tapisjob.out","description":null,"include":null,"notes":null},{"key":"_tapisStdoutFilename","value":"tapisjob.out","description":null,"include":null,"notes":null},{"key":"_tapisSysBatchScheduler","value":"SLURM","description":null,"include":null,"notes":null},{"key":"_tapisSysHost","value":"wma-exec-01.tacc.utexas.edu","description":null,"include":null,"notes":null},{"key":"_tapisSysRootDir","value":"/","description":null,"include":null,"notes":null},{"key":"_tapisTenant","value":"designsafe","description":null,"include":null,"notes":null},{"key":"inputDirectory","value":"BasicExamples","description":"EnvKey from input file: Input Directory","include":true,"notes":"{}"},{"key":"mainProgram","value":"OpenSees","description":"Choose the OpenSees binary to use.","include":true,"notes":"{\"label\":\"Main Program\",\"enum_values\":[{\"OpenSees\":\"OpenSees\"},{\"OpenSeesSP\":\"OpenSeesSP\"}]}"},{"key":"tclScript","value":"Ex1a.Canti2D.Push.tcl","description":"The filename of the OpenSees TCL script to execute, e.g. \"freeFieldEffective.tcl\".","include":true,"notes":"{\"label\":\"Main Script\",\"inputType\":\"fileInput\"}"}],"archiveFilter":{"includes":[],"excludes":["opensees-express.zip","tapisjob.env"],"includeLaunchFiles":true},"logConfig":{"stdoutFilename":"tapisjob.out","stderrFilename":"tapisjob.out"}}
remoteChecksFailed: 0
remoteChecksSuccess: 0
remoteEnded: None
remoteJobId: None
remoteJobId2: None
remoteLastStatusCheck: None
remoteOutcome: None
remoteQueue: None
remoteResultInfo: None
remoteStarted: None
remoteSubmitRetries: 0
remoteSubmitted: None
sharedAppCtx: wma_prtl
sharedAppCtxAttribs: ['SAC_EXEC_SYSTEM_ID', 'SAC_ARCHIVE_SYSTEM_ID', 'SAC_EXEC_SYSTEM_INPUT_DIR', 'SAC_EXEC_SYSTEM_EXEC_DIR', 'SAC_EXEC_SYSTEM_OUTPUT_DIR', 'SAC_ARCHIVE_SYSTEM_DIR']
stageAppCorrelationId: None
stageAppTransactionId: None
status: PENDING
subscriptions: []
tags: []
tapisQueue: tapis.jobq.submit.DefaultQueue
tenant: designsafe
trackingId: None
uuid: f708c941-f974-454d-b869-3687568aba4e-007
visible: True
Once you have no errors you are done submitting the job.#
go to the job-status page on the web portal to monitor the your job.#
https://www.designsafe-ci.org/workspace/history
You will, likely, have to debug your OpenSees script…..
NOTE ONCE THE JOB HAS COMPLETED, go to view Output and pay attention to the location (path) of the output, it may not be in MyData, as expected.
print('done')
done