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.

quoFEM Sensitivity Analysis Example

This example runs a quoFEM global sensitivity analysis with the SimCenter UQ engine on TACC Stampede3 using dapi, then post-processes the results in Python. The workflow performs Monte Carlo sampling (500 samples) of three PM4Sand constitutive model parameters (Dr, G0, hpo) in a cyclic direct simple shear simulation and reports Sobol sensitivity indices for the number-of-cycles engineering demand parameters (EDPs).

Try on DesignSafe

For general job submission concepts, see Jobs. For resource sizing, see DesignSafe Workflows.

How dapi handles SimCenter apps

The SimCenter Tapis apps (quoFEM, EE-UQ, WE-UQ backends such as simcenter-uq-stampede3) declare no fileInputs or envVariables in their app definitions — their interface contract lives inside the app’s wrapper script. dapi (>= 0.5.4) encodes that contract as an app profile that is applied automatically:

Complete Example

Step 1: Install and Import dapi

# Install dapi package
%pip install --user --upgrade dapi --quiet

# Import required modules
from dapi import DSClient
import os
import json

Step 2: Initialize Client

# Initialize DesignSafe client
ds = DSClient()

Authentication: dapi supports multiple authentication methods including environment variables, .env files, and interactive prompts. For detailed authentication setup instructions, see the authentication guide.

Step 3: Configure Job Parameters

# Job configuration parameters
job_name: str = "quofem-sensitivity-dapi"
app_id: str = "simcenter-uq-stampede3"  # SimCenter UQ application ID
tacc_allocation: str = "your-allocation"  # TACC allocation to charge
archive_system: str = "designsafe"  # Archive results to MyData
archive_path = None

# Resource configuration
queue: str = "skx-dev"
node_count: int = 1
cores_per_node: int = 48
memory_mb: int = 128000
max_job_minutes: int = 120

Step 4: Prepare the SimCenter Inputs

The input directory contains the quoFEM working files (tmp.SimCenter/templatedir/ with scInput.json, the per-sample driver script, and the OpenSees model files). prepare_inputs rewrites the workflow JSON’s backend paths (pass backend_dir=... to override the registered installation), reports the UQ workflow, and bundles the inputs.

ds_path = os.getcwd() + "/DS_input"

info = ds.jobs.prepare_inputs(app_id, ds_path, bundle=True)

rv_names = info["random_variables"]  # ['Dr', 'G0', 'hpo']
edp_names = info["edps"]  # ['nCycles010_1', ...]

# Stage the bundled directory (contains only tmpSimCenter.zip)
input_uri = ds.files.to_uri(info["staged_dir"])

Running from CommunityData or a published dataset? Read-only sources are handled automatically — the exact same code above works unchanged:

The local copy is reported as info["local_staged_dir"]. An input directory that already ships tmpSimCenter.zip is reused rather than recompressed — only its workflow JSON entry is rewritten.

Step 5: Generate Job Request

The SimCenter app profile applies the wrapper contract automatically — no manual envKey, targetPath, or environment variable setup is needed.

job_dict = ds.jobs.generate(
    app_id=app_id,
    input_dir_uri=input_uri,
    archive_system=archive_system,
    archive_path=archive_path,
    max_minutes=max_job_minutes,
    allocation=tacc_allocation,
    queue=queue,
    job_name=job_name,
    node_count=node_count,
    cores_per_node=cores_per_node,
    memory_mb=memory_mb,
)
print(json.dumps(job_dict, indent=2, default=str))

Step 6: Submit and Monitor

submitted_job = ds.jobs.submit(job_dict)  # the UUID is logged on submission

final_status = submitted_job.monitor(interval=30)
ds.jobs.interpret_status(final_status, submitted_job.uuid)
submitted_job.print_runtime_summary(verbose=False)

You can also watch the job in the DesignSafe portal under Workspace → Tools & Applications → Job Status.

Step 7: Retrieve and Analyze Results

The app wrapper gathers the UQ engine outputs (dakota.out, dakotaTab.out) into results.zip in the job archive. get_results() fetches and parses it in memory.

results = submitted_job.get_results()

samples = results.samples  # DataFrame: one row per realization
sobol = results.sobol_indices  # DataFrame: outputs x Sm/St indices per RV
print(samples.head())
print(sobol)

Step 8: Plot Sensitivity Indices

import matplotlib.pyplot as plt
import numpy as np

palette = ["#4269d0", "#efb118", "#ff725c"]  # fixed hue per random variable

fig, axes = plt.subplots(1, 2, figsize=(12, 4), sharey=True)
x = np.arange(len(sobol.index))
width = 0.25
for ax, prefix, title in zip(axes, ("Sm", "St"), ("Main", "Total")):
    for i, (rv, color) in enumerate(zip(rv_names, palette)):
        ax.bar(
            x + (i - 1) * width,
            sobol[f"{prefix}({rv})"],
            width,
            color=color,
            label=rv if prefix == "Sm" else None,
        )
    ax.set_title(f"{title} Sobol index")
    ax.set_ylim(0, 1)
    ax.set_xticks(x)
    ax.set_xticklabels(sobol.index, rotation=30, ha="right")
    ax.grid(alpha=0.25, axis="y")
    ax.set_axisbelow(True)
axes[0].legend(title="Random variable")
fig.tight_layout()
plt.show()

Archiving Results to a Project

By default results archive to MyData (tapis-jobs-archive/). To archive into a shared DesignSafe project instead — so collaborators can see the job and its outputs — point archive_system at the project system id:

archive_system = "project-<uuid>"  # from ds.projects.list()
archive_path = f"quoFEM_jobs/{job_name}/${{JobUUID}}"

Include ${JobUUID} (or ${JobCreateDate}) in the path so repeated runs don’t mix files in one folder. You must have write access to the project, and job.get_results() works unchanged.

Why Input Bundling Matters

The Tapis transfers service turns each staged file into its own queued task, so staging time scales with file count rather than bytes. Measured on the same job, same inputs:

Phase15 loose files1 bundled zip
STAGING_INPUTS10:580:46
TOTAL29:0916:23

prepare_inputs bundles by default for SimCenter apps because the wrapper natively unpacks tmpSimCenter.zip — the original input directory is never modified beyond the workflow-JSON patch.