Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

OpenSees ML, Sweep to Regression

An end-to-end machine learning workflow on DesignSafe. A PyLauncher sweep runs a 2D cantilever pushover in OpenSeesPy across a 75-point parameter grid, and a post-script fits a linear regression that recovers the physics from the results, all in one python-s3 job.

Try on DesignSafe

For PyLauncher basics, see PyLauncher Parameter Sweeps. For the app settings used here, see the Generic Python App.

The period equation as ground truth

The natural period of a cantilever with tip mass follows

T = 2π · sqrt(M · L³ / (3 · E · I))

so in log space the exponents are known exactly, 0.5 on mass, 1.5 on length, and -0.5 on modulus. The workflow sweeps NODAL_MASS x LCOL x EMOD (5 x 5 x 3 = 75 OpenSeesPy pushover runs), aggregates each task’s metrics.json, and fits log T against the log features with scikit-learn. Because the physics is exact, the regression must report coef ≈ [0.5, 1.5, -0.5] with R² = 1.0, which makes the example self-checking. Wrong plumbing cannot produce the right coefficients.

How one job does all of it

Each stage of the python-s3 job does one piece of the work.

StageSettingWhat it does here
StagingUNZIP_INPUTS: inputs.zipAll eight input files travel as one ZIP, one Tapis transfer instead of eight
Pre-scriptPRE_SCRIPT: setup.shCopies the TACC-compiled OpenSeesPy shared library into the job (handles both the opensees.so and older OpenSeesPy.so layouts)
EnvironmentPIP_REQUIREMENTS: requirements.txtInstalls scikit-learn and matplotlib into the temporary per-job environment
Maincall_pylauncher.pyPyLauncher dispatches the 75 cantilever.py tasks across the node’s cores
Post-scriptPOST_SCRIPT: ml_post.sh (required)Aggregates out_*/metrics.json, fits the regression, renders diagnostic plots

The sweep definition and submission are three notebook cells.

sweep = {
    "NODAL_MASS": [4.19, 4.39, 4.59, 4.79, 4.99],
    "LCOL": [100, 200, 300, 400, 500],
    "EMOD": [3600, 4000, 5000],
}

commands = ds.jobs.parametric_sweep.generate(command, sweep, str(input_dir))
job = ds.jobs.parametric_sweep.submit(...)

The placeholder is named EMOD rather than E on purpose. Token-style placeholders match anywhere a word appears, so a single-letter key like E also rewrites the --E flag in every generated command.

Results

The archived job contains ml_results/ with the fitted model as JSON, per-task predictions as CSV, and a diagnostics figure (histograms, predicted versus truth, residuals, coefficients). The notebook downloads and displays them.

job.download_output(
    "inputDirectory/ml_results/opensees_ml_diagnostics.png",
    "opensees_ml_diagnostics.png",
)

On Stampede3, the 75 tasks take about 42 seconds of task time, and the regression recovers [0.500, 1.500, -0.500] with R² = 1.0.

The same study as a DAG workflow

DS_OpenSees_ML_Workflow_DAG.ipynb splits the pipeline into two jobs, a 48-core sweep and a one-core training task, connected as an explicit graph with dapi.workflows. dapi points the training job’s input at the sweep’s archive through an output reference and resolves it to the real path before submitting, retraining costs one core instead of a resweep, and the run streams live per-task progress in the notebook. The Workflows guide covers the API.

Files

FileRole
DS_OpenSees_ML_Example.ipynbDefines the sweep, bundles inputs, submits, inspects outputs
DS_OpenSees_ML_Workflow_DAG.ipynbThe same study as a two-job DAG via dapi.workflows
cantilever.pyOne task, an OpenSeesPy pushover that writes metrics.json
aggregate_and_train.pyCollects all metrics.json and fits the regression
postprocess.pyRenders the diagnostics PDF and PNG
setup.shPre-script that stages TACC OpenSeesPy
ml_post.shPost-script that runs training and plotting on the compute node
requirements.txtExtra packages for the per-job environment

The example also runs locally without Tapis. The README shows the two-task local dry run.