Run OpenSeesPy App#
Submit Jobs to the NEW! OpenSeesPy App
by Silvia Mazzoni, DesignSafe, 2026
In this Notebook we are going to run a set of OpenSeesPy jobs. We will run different types of anaysis:
Single Process Thread
Multi-Process using concurrent futures (can run only on a single node)
Multi-Process multi-node using mpi4py
We are using previously-defined python function to streamline the process.
NEW: unique to OpenSeesPy#
UseMPI: Flag indicating whether the application should launch the main program with an MPI parallel-execution command (ibrun).
True: enable distributed-memory parallelism, allowing multi-core or multi-node execution. (Suitable for OpenSeesMP / OpenSeesSP / Python with mpi4py (OpenSeesPy)).
False: execution stays on one node. (Suitable for OpenSees, Python, or Python with concurrent.futures for one-node parallelism.)
Unique to OpenSeesPy on TACC#
The Tapis app uses the Stampede3-compiled OpenSeesPy build that matches the app’s Python environment. Using a pip-installed OpenSeesPy can lead to missing shared-library dependencies (e.g., unresolved .so files) at runtime.
To avoid this, include the following block at the top of your Python script. The conditional logic makes it portable: on Stampede3 it finds the compiled opensees.so, and elsewhere it falls back to the standard import.
# Import the local version of OpenSees, if it exists
if os.path.exists('opensees.so'):
import opensees as ops;
else:
import openseespy.opensees as ops
If you have a simple job, or to learn which output you need, you can submit a job via the web portal: https://designsafe-ci.org/workspace/designsafe-openseespy-s3
Initialize#
Initialize Python#
from pathlib import Path
import ipywidgets as widgets
Load Specialized Utilities Library#
I have developed a collection of python defs that are intended to simplify coding.
# We will use this utility often to view the utility functions:
OpsUtils.show_text_file_in_accordion(PathOpsUtils, 'show_text_file_in_accordion.py')
Examples-Files Location#
# We need to break the path up for Tapis because it uses different root paths
OpsScriptsPath_Base = 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic'
OpsScriptsPath_StorageSystem = 'CommunityData'
to run scripts locally on jupyter hub:#
# Define file paths in local/jupyter terms so we can view the input files here and we can check that the directory exists!
OpsScriptsPath_Local = os.path.join(OpsScriptsPath_StorageSystem, OpsScriptsPath_Base)
OpsScriptsPath_Local = '~/' + OpsScriptsPath_Local
OpsScriptsPath_Local = os.path.expanduser(OpsScriptsPath_Local)
print('OpsScriptsPath_Local:',OpsScriptsPath_Local)
Path_OpsScriptsPath_Local = Path(OpsScriptsPath_Local)
if Path_OpsScriptsPath_Local.is_dir():
print('\nDirectory Exists! \n Content:')
display(os.listdir(Path_OpsScriptsPath_Local))
else:
print('\nERROR!!! Directory DOES NOT Exist!')
OpsScriptsPath_Local: /home/jupyter/CommunityData/Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic
Directory Exists!
Content:
['Ex1a_verymany.Canti2D.Push.mp.tcl',
'Ex1a.Canti2D.Push.argv.tacc.runsList.txt',
'Ex1a.Canti2D.Push.argv.tcl.callPylauncher.py',
'Ex1a.Canti2D.Push.mpi4py.tacc.py',
'Ex1.Canti2D.Push.mpi.mod.tacc.py',
'Ex1a.Canti2D.Push.tacc.py',
'Ex1a.Canti2D.Push.mp.tcl',
'simpleSP.tcl',
'Ex1a.Canti2D.Push.mpi.tacc.py',
'Ex1a.Canti2D.Push.argv.tacc.py.callPylauncher.py',
'Ex1a.Canti2D.Push.py',
'Ex1a.Canti2D.Push.argv.tacc.callPylauncher.py',
'Ex1a.Canti2D.Push.mpi4py.py',
'Ex1a_many.Canti2D.Push.mp.tcl',
'Ex1a.Canti2D.Push.futures.tacc.py',
'Ex1a.Canti2D.Push.tcl',
'Ex1a.Canti2D.Push.argv.tcl.runsList.txt',
'Ex1a.Canti2D.Push.argv.tacc.py.runsList.txt',
'Ex1a.Canti2D.Push.argv.tcl',
'Ex1a.Canti2D.Push.futures.py',
'Ex1a.Canti2D.Push.mpi.py',
'Ex1a.Canti2D.Push.argv.tacc.py']
Connect to Tapis#
OpsUtils.show_text_file_in_accordion(PathOpsUtils, 'connect_tapis.py')
force_connect = False
# force_connect = True; # REMOVE do this only if you want to restart the clock on the token.
t=OpsUtils.connect_tapis(force_connect=force_connect)
-- Checking Tapis token --
Token loaded from file. Token is still valid!
Token expires at: 2026-02-14T21:44:08+00:00
Token expires in: 3:40:26.393652
-- AUTHENTICATED VIA SAVED TOKEN --
Initialize ALL-Job Input#
tapisInputAll = {}
SLURM-Specific Input#
tapisInputAll["maxMinutes"] = 6
tapisInputAll["execSystemId"] = "stampede3" # the app runs on stampede only
tapisInputAll["execSystemLogicalQueue"] = "skx-dev" # "skx", "skx-dev"
tapisInputAll["nodeCount"] = 1 # limits set by which compute nodes you use
tapisInputAll["coresPerNode"] = 48 # limits set by which compute nodes you use
tapisInputAll["allocation"] = "DS-HPC1"
tapisInputAll['archive_system']=OpsScriptsPath_StorageSystem # Options: MyData or Work
Common Input for All Jobs#
# The folder path needs to be converted into tapis-format, we have a utility for that, which is why we have to separate the system and the folder.
tapisInputAll['storage_system'] = 'CommunityData'
tapisInputAll['input_folder'] = 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic'
App-Specific Input#
app_id = "designsafe-openseespy-s3"
Define App-Info Input#
tapisInputAll["appId"] = app_id
tapisInputAll["appVersion"] = "latest" # always use latest in this Notebook Template
Check if App Exists#
If it exists you will see a version number.
current_app_version = OpsUtils.get_latest_app_version(t,app_id)
print('current_app_version',current_app_version)
current_app_version 1.2.15
Access App Schema on Tapis#
OpsUtils.show_text_file_in_accordion(PathOpsUtils, ['getAppLatestVersion.py','display_tapis_app_schema.py'])
appMetaData = t.apps.getAppLatestVersion(appId=app_id)
here_out = widgets.Output()
here_accordion = widgets.Accordion(children=[here_out])
# here_accordion.selected_index = 0
here_accordion.set_title(0, f'List the new app')
display(here_accordion)
with here_out:
OpsUtils.display_tapis_app_schema(appMetaData)
thisAppVersion = appMetaData.version
isPublic = appMetaData.isPublic
here_accordion.set_title(0, f'app schema: {app_id} -- version = {thisAppVersion} -- isPublic = {isPublic}')
Display All Output thus far#
display(tapisInputAll)
{'maxMinutes': 6,
'execSystemId': 'stampede3',
'execSystemLogicalQueue': 'skx-dev',
'nodeCount': 1,
'coresPerNode': 48,
'allocation': 'DS-HPC1',
'archive_system': 'CommunityData',
'storage_system': 'CommunityData',
'input_folder': 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic',
'appId': 'designsafe-openseespy-s3',
'appVersion': 'latest'}
Job-Execution Controls#
askConfirmJob = False
askConfirmMonitorRT = False
Input Files#
The scripts used in this notebook, with the .tacc.py in the filename have a logical process to check whether the TACC-compiles OpenSeesPy is available – recommended for TACC.
OpenSeesPy_script_PY = 'Ex1a.Canti2D.Push.py'
OpenSeesPy_script_PY_TACC = 'Ex1a.Canti2D.Push.tacc.py'
OpenSeesPy_script_PY_TACC_PyLauncher = 'Ex1a.Canti2D.Push.argv.tacc.pylauncher.py'
OpenSeesPy_script_PY_futures = 'Ex1a.Canti2D.Push.futures.py'
OpenSeesPy_script_PY_futures_TACC = 'Ex1a.Canti2D.Push.futures.tacc.py'
OpenSeesPy_script_PY_mpi = 'Ex1a.Canti2D.Push.mpi.py'
OpenSeesPy_script_PY_mpi_TACC = 'Ex1a.Canti2D.Push.mpi.tacc.py'
OpenSeesPy_script_PY_mpi4py = 'Ex1a.Canti2D.Push.mpi4py.py'
OpenSeesPy_script_PY_mpi4py_TACC = 'Ex1a.Canti2D.Push.mpi4py.tacc.py'
# PyLauncher Files:
PyLauncher_Input_Script = 'Ex1a.Canti2D.Push.argv.tacc.py.callPylauncher.py'
PyLauncher_FileList_File = 'Ex1a.Canti2D.Push.argv.tacc.py.runsList.txt'
OpenSeesPy_Script_wArgv = 'Ex1a.Canti2D.Push.argv.tacc.py'
A. OpenSeesPy: Single Thread#
Test File Locally#
# test here first
os.system(f'python {OpsScriptsPath_Local}/Ex1a.Canti2D.Push.tacc.py')
using OpenSeesPy wheel
['/home/jupyter/CommunityData/Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic/Ex1a.Canti2D.Push.tacc.py']
Analysis-0 execution done
Analysis-1 execution done
Analysis-2 execution done
Analysis-3 execution done
Analysis-4 execution done
Analysis-5 execution done
Analysis-6 execution done
Analysis-7 execution done
ALL DONE!!!
Process 0 Terminating
0
app input for this run#
# initalize
tapisInput = tapisInputAll.copy()
tapisInput['storage_system'] = OpsScriptsPath_StorageSystem
tapisInput['input_folder'] = OpsScriptsPath_Base
tapisInput['Main Script'] = 'Ex1a.Canti2D.Push.tacc.py' # ########################## unique
# NEW: unique to OpenSeesPy
# UseMPI: Flag indicating whether the application should launch the main program with an MPI parallel-execution command (ibrun).
# **True**: enable distributed-memory parallelism, allowing multi-core or multi-node execution. (Suitable for OpenSeesMP / OpenSeesSP / Python with mpi4py (OpenSeesPy)).
# **False**: execution stays on one node. (Suitable for OpenSees, Python, or Python with concurrent.futures for one-node parallelism.)
tapisInput['UseMPI'] = 'False' # ########################## unique
tapisInput['name'] = tapisInput['appId'] + '_' + "OpenSeesPyApp" + '_' + tapisInput['Main Script']
display(tapisInput)
{'maxMinutes': 6,
'execSystemId': 'stampede3',
'execSystemLogicalQueue': 'skx-dev',
'nodeCount': 1,
'coresPerNode': 48,
'allocation': 'DS-HPC1',
'archive_system': 'CommunityData',
'storage_system': 'CommunityData',
'input_folder': 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic',
'appId': 'designsafe-openseespy-s3',
'appVersion': 'latest',
'Main Script': 'Ex1a.Canti2D.Push.tacc.py',
'UseMPI': 'False',
'name': 'designsafe-openseespy-s3_OpenSeesPyApp_Ex1a.Canti2D.Push.tacc.py'}
Submit#
# -----------------------------------------------------
jobReturns = OpsUtils.run_tapis_job(t,tapisInput,get_job_metadata=True,get_job_history=True,get_job_filedata=True,askConfirmJob = askConfirmJob,askConfirmMonitorRT = askConfirmMonitorRT)
# -----------------------------------------------------
B. OpenSeesPy: Multi-Process using concurrent.futures#
keep UseMPI False
OpsUtils.show_text_file_in_accordion(OpsScriptsPath_Local, [OpenSeesPy_script_PY_futures_TACC])
Test File Locally#
# test here first --
os.system(f'python {OpsScriptsPath_Local}/{OpenSeesPy_script_PY_futures_TACC}')
['/home/jupyter/CommunityData/Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic/Ex1a.Canti2D.Push.futures.tacc.py']
Analysis-4 execution done with ok=0
Analysis-2 execution done with ok=0
Analysis-3 execution done with ok=0
Analysis-1 execution done with ok=0
Analysis-8 execution done with ok=0
Analysis-5 execution done with ok=0
Analysis-7 execution done with ok=0
Analysis-6 execution done with ok=0
[case 5], status=0
[case 1], status=0
[case 4], status=0
[case 3], status=0
[case 2], status=0
[case 6], status=0
[case 7], status=0
[case 8], status=0
All results:
{'case_id': 1, 'status': 0}
{'case_id': 2, 'status': 0}
{'case_id': 3, 'status': 0}
{'case_id': 4, 'status': 0}
{'case_id': 5, 'status': 0}
{'case_id': 6, 'status': 0}
{'case_id': 7, 'status': 0}
{'case_id': 8, 'status': 0}
ALL DONE!!!
Process 0 Terminating
0
app input for this run#
# initalize
tapisInput = tapisInputAll.copy()
tapisInput['storage_system'] = OpsScriptsPath_StorageSystem
tapisInput['input_folder'] = OpsScriptsPath_Base
tapisInput['Main Script'] = OpenSeesPy_script_PY_futures_TACC # ########################## unique
# NEW: unique to OpenSeesPy
# UseMPI: Flag indicating whether the application should launch the main program with an MPI parallel-execution command (ibrun).
# **True**: enable distributed-memory parallelism, allowing multi-core or multi-node execution. (Suitable for OpenSeesMP / OpenSeesSP / Python with mpi4py (OpenSeesPy)).
# **False**: execution stays on one node. (Suitable for OpenSees, Python, or Python with concurrent.futures for one-node parallelism.)
tapisInput['UseMPI'] = 'False' # keep false
tapisInput['name'] = tapisInput['appId'] + '_' + "OpenSeesPyApp" + '_' + tapisInput['Main Script']
display(tapisInput)
{'maxMinutes': 6,
'execSystemId': 'stampede3',
'execSystemLogicalQueue': 'skx-dev',
'nodeCount': 1,
'coresPerNode': 48,
'allocation': 'DS-HPC1',
'archive_system': 'CommunityData',
'storage_system': 'CommunityData',
'input_folder': 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic',
'appId': 'designsafe-openseespy-s3',
'appVersion': 'latest',
'Main Script': 'Ex1a.Canti2D.Push.futures.tacc.py',
'UseMPI': 'False',
'name': 'designsafe-openseespy-s3_OpenSeesPyApp_Ex1a.Canti2D.Push.futures.tacc.py'}
Submit#
# -----------------------------------------------------
jobReturns = OpsUtils.run_tapis_job(t,tapisInput,get_job_metadata=True,get_job_history=True,get_job_filedata=True,askConfirmJob = askConfirmJob,askConfirmMonitorRT = askConfirmMonitorRT)
# -----------------------------------------------------
C. OpenSeesPy: Multi-Process using mpi4py#
OpsUtils.show_text_file_in_accordion(OpsScriptsPath_Local, [OpenSeesPy_script_PY_mpi4py_TACC])
Test File Locally#
# test here first
os.system(f'mpiexec -np 4 python {OpsScriptsPath_Local}/{OpenSeesPy_script_PY_mpi4py_TACC}')
mpi4py -- python pid 1 of 4 started
mpi4py -- python pid 0 of 4 started
mpi4py -- python pid 2 of 4 started
mpi4py -- python pid 3 of 4 started
pid 1 of np=4 Analysis-1 execution done
pid 2 of np=4 Analysis-2 execution done
pid 0 of np=4 Analysis-0 execution done
pid 3 of np=4 Analysis-3 execution done
pid 1 of np=4 Analysis-5 execution done
pid 1 of np=4 ALL DONE!!!
pid 2 of np=4 Analysis-6 execution done
pid 2 of np=4 ALL DONE!!!
pid 0 of np=4 Analysis-4 execution done
pid 0 of np=4 ALL DONE!!!
pid 3 of np=4 Analysis-7 execution done
pid 3 of np=4 ALL DONE!!!
Process 0 Terminating
Process 0 Terminating
Process 0 Terminating
Process 0 Terminating
0
app input for this run#
# initalize
tapisInput = tapisInputAll.copy()
tapisInput['storage_system'] = OpsScriptsPath_StorageSystem
tapisInput['input_folder'] = OpsScriptsPath_Base
tapisInput['Main Script'] = OpenSeesPy_script_PY_mpi4py_TACC # ########################## unique
# NEW: unique to OpenSeesPy
# UseMPI: Flag indicating whether the application should launch the main program with an MPI parallel-execution command (ibrun).
# **True**: enable distributed-memory parallelism, allowing multi-core or multi-node execution. (Suitable for OpenSeesMP / OpenSeesSP / Python with mpi4py (OpenSeesPy)).
# **False**: execution stays on one node. (Suitable for OpenSees, Python, or Python with concurrent.futures for one-node parallelism.)
tapisInput['UseMPI'] = 'True'
tapisInput['name'] = tapisInput['appId'] + '_' + "OpenSeesPyApp" + '_' + tapisInput['Main Script']
display(tapisInput)
{'maxMinutes': 6,
'execSystemId': 'stampede3',
'execSystemLogicalQueue': 'skx-dev',
'nodeCount': 1,
'coresPerNode': 48,
'allocation': 'DS-HPC1',
'archive_system': 'CommunityData',
'storage_system': 'CommunityData',
'input_folder': 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic',
'appId': 'designsafe-openseespy-s3',
'appVersion': 'latest',
'Main Script': 'Ex1a.Canti2D.Push.mpi4py.tacc.py',
'UseMPI': 'True',
'name': 'designsafe-openseespy-s3_OpenSeesPyApp_Ex1a.Canti2D.Push.mpi4py.tacc.py'}
Submit#
# -----------------------------------------------------
jobReturns = OpsUtils.run_tapis_job(t,tapisInput,get_job_metadata=True,get_job_history=True,get_job_filedata=True,askConfirmJob = askConfirmJob,askConfirmMonitorRT = askConfirmMonitorRT)
# -----------------------------------------------------
OpenSeesPy: PyLauncher#
you can let pylauncher manage your analysis tasks – you can do this for both the TCL and Python versions of OpenSees.
Test Locally#
we can only test the analysis script
# test the script first
os.system(f'python {OpsScriptsPath_Local}/{OpenSeesPy_Script_wArgv} --NodalMass 11.59 --outDir outCase38') # adding the -h to show help
using OpenSeesPy wheel
['/home/jupyter/CommunityData/Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic/Ex1a.Canti2D.Push.argv.tacc.py', '--NodalMass', '11.59', '--outDir', 'outCase38']
we have input arguments
NodalMass 11.59
LColList [101, 121, 200, 240, 301, 360, 401, 481]
outDir outCase38
Lcol 101
2 0 101
<class 'int'>
Analysis-0 execution done
Lcol 121
2 0 121
<class 'int'>
Analysis-1 execution done
Lcol 200
2 0 200
<class 'int'>
Analysis-2 execution done
Lcol 240
2 0 240
<class 'int'>
Analysis-3 execution done
Lcol 301
2 0 301
<class 'int'>
Analysis-4 execution done
Lcol 360
2 0 360
<class 'int'>
Analysis-5 execution done
Lcol 401
2 0 401
<class 'int'>
Analysis-6 execution done
Lcol 481
2 0 481
<class 'int'>
Analysis-7 execution done
ALL DONE!!!
Process 0 Terminating
0
PyLauncher Input#
You need 3 files:
Your OpenSees-Analysis script needs to accept and process command-line arguments – these are your analysis variables. Example:
print(sys.argv)
if len(sys.argv)>1:
print('we have input arguments')
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--NodalMass", type=float, default=5.18)
parser.add_argument("--LCol", type=float, default=None) # NEW: single run value
parser.add_argument("--LColList", type=csv_floats, default=[101,121,200,240,301,360,401,481])
parser.add_argument("--outDir", type=str, default='outData_PY_tacc')
args = parser.parse_args()
NodalMass = args.NodalMass
LColList = args.LColList
outDir = args.outDir
if args.LCol is not None:
LColList = [args.LCol] # launcher mode: one case per process
else:
LColList = args.LColList # interactive mode: multiple cases
print('NodalMass',NodalMass)
print('LColList',LColList)
print('outDir',outDir)
your Main Script calls the PyLauncher:
Example:
import pylauncher
pylauncher.ClassicLauncher("Ex1a.Canti2D.Push.argv.tacc.runsList.txt",debug="host+job")
The Task File containing the commands. Example:
python Ex1a.Canti2D.Push.argv.tacc.py --NodalMass 11.59 --outDir outCase38
python Ex1a.Canti2D.Push.argv.tacc.py --NodalMass 11.79 --outDir outCase39
python Ex1a.Canti2D.Push.argv.tacc.py --NodalMass 11.99 --outDir outCase40
python Ex1a.Canti2D.Push.argv.tacc.py --LcolList 100,200,300 --outDir outCase41
python Ex1a.Canti2D.Push.argv.tacc.py --LcolList 105,205,305 --outDir outCase42
python Ex1a.Canti2D.Push.argv.tacc.py --LcolList 110,210,310 --outDir outCase43
....
app input for this run#
# initalize
tapisInput = tapisInputAll.copy()
tapisInput['storage_system'] = OpsScriptsPath_StorageSystem
tapisInput['input_folder'] = OpsScriptsPath_Base
tapisInput['Main Script'] = PyLauncher_Input_Script # ########################## unique, not the openseesPy script!!!
tapisInput['UseMPI'] = 'False'; # am using the classic launcher
tapisInput['name'] = tapisInput['appId'] + '_' + "OpenSeesPyApp" + '_' + tapisInput['Main Script']
display(tapisInput)
{'maxMinutes': 6,
'execSystemId': 'stampede3',
'execSystemLogicalQueue': 'skx-dev',
'nodeCount': 1,
'coresPerNode': 48,
'allocation': 'DS-HPC1',
'archive_system': 'CommunityData',
'storage_system': 'CommunityData',
'input_folder': 'Training/Computational-Workflows-on-DesignSafe/Examples/OpenSees_Basic',
'appId': 'designsafe-openseespy-s3',
'appVersion': 'latest',
'Main Script': 'Ex1a.Canti2D.Push.argv.tacc.py.callPylauncher.py',
'UseMPI': 'False',
'name': 'designsafe-openseespy-s3_OpenSeesPyApp_Ex1a.Canti2D.Push.argv.tacc.py.callPylauncher.py'}
Submit#
# -----------------------------------------------------
jobReturns = OpsUtils.run_tapis_job(t,tapisInput,get_job_metadata=True,get_job_history=True,get_job_filedata=True,askConfirmJob = askConfirmJob,askConfirmMonitorRT = askConfirmMonitorRT)
# -----------------------------------------------------
print('done!')
done!