diff --git a/applications/simcenter-designsafe-vm/Dockerfile b/applications/simcenter-designsafe-vm/Dockerfile new file mode 100644 index 0000000..6500525 --- /dev/null +++ b/applications/simcenter-designsafe-vm/Dockerfile @@ -0,0 +1,33 @@ +## taccaci/designsafe-simcenter-vm:0.0.1 ## + +FROM ubuntu:20.04 + +LABEL maintainer="TACC-ACI-WMA " + +ARG DEBIAN_FRONTEND=noninteractive + +ENV TZ=Etc/UTC + +RUN apt-get update && apt-get install -y \ + python3-pip + +RUN pip install --upgrade \ + pip \ + setuptools \ + wheel + +RUN pip install \ + h5py \ + numpy \ + pandas \ + xarray \ + tables \ + netcdf4 \ + h5netcdf \ + scipy + +RUN useradd --create-home ubuntu + +USER ubuntu + +SHELL ["/bin/bash", "-c"] \ No newline at end of file diff --git a/applications/simcenter-designsafe-vm/app.json b/applications/simcenter-designsafe-vm/app.json new file mode 100644 index 0000000..9613950 --- /dev/null +++ b/applications/simcenter-designsafe-vm/app.json @@ -0,0 +1,100 @@ +{ + "id": "SimCenter-DesignSafeVM", + "version": "0.0.1", + "description": "An app for extracting the physics-based motion from OpenSees results.", + "owner": "${apiUserId}", + "enabled": true, + "runtime": "ZIP", + "runtimeVersion": null, + "runtimeOptions": null, + "containerImage": "tapis://cloud.data/corral/tacc/aci/CEP/applications/v3/simcenter-designsafe-vm/simcenter-designsafe-vm.zip", + "jobType": "FORK", + "maxJobs": -1, + "maxJobsPerUser": -1, + "strictFileInputs": true, + "jobAttributes": { + "description": "Extract the physics-based motion of your OpenSees results on a virtual machine.", + "dynamicExecSystem": false, + "execSystemConstraints": null, + "execSystemId": "wma-exec-01", + "execSystemExecDir": "${JobWorkingDir}", + "execSystemInputDir": "${JobWorkingDir}", + "execSystemOutputDir": "${JobWorkingDir}", + "execSystemLogicalQueue": "development", + "archiveSystemId": "cloud.data", + "archiveSystemDir": "HOST_EVAL($HOME)/tapis-jobs-archive/${JobCreateDate}/${JobName}-${JobUUID}", + "archiveOnAppError": true, + "isMpi": false, + "mpiCmd": null, + "cmdPrefix": null, + "parameterSet": { + "appArgs": [], + "schedulerOptions": [], + "envVariables": [ + { + "key": "inputScript", + "value": "", + "description": "The filename of the main OpenSees input script.", + "inputMode": "REQUIRED", + "notes": { + "label": "Main Script" + } + }, + { + "key": "dataDirectory", + "value": "", + "description": "The path directory containing your data to be processed. Defaults to My Projects if not provided.", + "inputMode": "INCLUDE_ON_DEMAND", + "notes": { + "label": "Data Directory", + "selectionMode": "directory" + } + }, + { + "key": "_UserProjects", + "value": "", + "description": "User Project UUIDs and Project IDs", + "inputMode": "INCLUDE_ON_DEMAND", + "notes": { + "isHidden": true + } + } + ], + "archiveFilter": { + "includes": [], + "excludes": [], + "includeLaunchFiles": true + } + }, + "fileInputs": [ + { + "name": "Input Directory", + "description": "The directory containing your main OpenSees script and all necessary files. Defaults to My Data if not provided.", + "inputMode": "OPTIONAL", + "autoMountLocal": true, + "sourceUrl": null, + "targetPath": "*", + "envKey": "inputDirectory" + } + ], + "fileInputArrays": [], + "nodeCount": 1, + "coresPerNode": 1, + "memoryMB": 256000, + "maxMinutes": 1440, + "subscriptions": [], + "tags": [] + }, + "tags": [ + "portalName: CEP", + "portalName: DesignSafe" + ], + "notes": { + "label": "SimCenter DesignSafe (VM)", + "helpUrl": "https://www.designsafe-ci.org/user-guide/tools/simulation/opensees/opensees/", + "hideNodeCountAndCoresPerNode": true, + "icon": null, + "category": "Analysis", + "isInteractive": false + } +} diff --git a/applications/simcenter-designsafe-vm/get_folder_mount_path.py b/applications/simcenter-designsafe-vm/get_folder_mount_path.py new file mode 100644 index 0000000..d7cc0bf --- /dev/null +++ b/applications/simcenter-designsafe-vm/get_folder_mount_path.py @@ -0,0 +1,44 @@ +import sys + + +def set_mount_path(inputFolder, corralPrefix, tapisPrefix): + inputDir = inputFolder.replace(tapisPrefix,'') + mountPath = corralPrefix + inputDir # /corral/projects/NHERI/shared/gedmonds/LAS/LAS_converted/" + return mountPath + + +def main(): + folderToMount = sys.argv[1] + + prefixArr = [ + [ + "/corral/main/projects/NHERI/shared/", # "SHARED_PREFIX" + "tapis://designsafe.storage.default/" #"SHARED_TAPISPREFIX" + ], + [ + "/corral/main/projects/NHERI/projects/", # "PROJECT_PREFIX" + "tapis://project-" # "PROJECT_TAPISPREFIX" + ], + [ + "/corral/main/projects/NHERI/published/", # "PUBLISHED_PREFIX" + "tapis://designsafe.storage.published" # "PUBLISHED_TAPISPREFIX" + ], + [ + "/corral/main/projects/NHERI/community/", # "COMMUNITY_PREFIX" + "tapis://designsafe.storage.community" # "COMMUNITY_TAPISPREFIX": + ] + ] + + mountPath = None + + for prefixGroup in prefixArr: + corralPrefix = prefixGroup[0] + tapisPrefix = prefixGroup[1] + if tapisPrefix in folderToMount: + mountPath = set_mount_path(folderToMount, corralPrefix, tapisPrefix) + break + + print(mountPath) + +if __name__ == "__main__": + main() diff --git a/applications/simcenter-designsafe-vm/tapisjob_app.sh b/applications/simcenter-designsafe-vm/tapisjob_app.sh new file mode 100644 index 0000000..a84d00e --- /dev/null +++ b/applications/simcenter-designsafe-vm/tapisjob_app.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +handle_error() { + local EXITCODE=$1 + echo "Phyiscs-based extractor job exited with an error status. $EXITCODE" >&2 + echo "$EXITCODE" > "${_tapisExecSystemOutputDir}/tapisjob.exitcode" + exit $EXITCODE +} + +set -x + +PBESCRIPT="${inputScript##*/}" +echo "PBESCRIPT is $PBESCRIPT" + +inputPath="`pwd`/${inputDirectory}" +INPUTDIR=${inputPath} +if [ -z ${inputDirectory} ]; then + INPUTDIR="/corral/projects/NHERI/shared/${_tapisJobOwner}" +fi + +#dataPath="`pwd`/${dataDirectory}" +DATADIR_NAME="${dataDirectory##*/}" +DATADIR=`python3 get_folder_mount_path.py $dataDirectory` + +DATADIR_MOUNT="--bind ${DATADIR}:/home/${_tapisJobOwner}/projects/$DATADIR_NAME" +if [ -z ${dataDirectory} || -z ${DATADIR} ]; then + + if [ -z ${DATADIR} ]; then + echo "No valid mount found for Data Directory specified in job request; defaulting to mounting your projects." + fi + projects_dir="$HOME/MyProjects" + + mkdir -p "$projects_dir" + IFS=' ' read -r -a projects <<< "${_UserProjects}" + for project in "${projects[@]}"; do + IFS=',' read -r uuid projectId <<< "$project" + target_path="/corral/main/projects/NHERI/projects/$uuid" + symlink_path="$projects_dir/$projectId" + + if [ -e "$target_path" ]; then + if [ ! -e "$symlink_path" ]; then + ln -s "$target_path" "$symlink_path" + echo "TACC: Project Links: Created symlink: $symlink_path -> $target_path" + else + echo "TACC: Project Links: Symlink already exists: $symlink_path" + fi + else + echo "TACC: Project Links: Target path does not exist: $target_path" + fi + done + DATADIR_MOUNT="--bind ${projects_dir}:/home/${_tapisJobOwner}/projects" +fi + +echo "Running $PBESCRIPT" + +apptainer run \ + --writable-tmpfs \ + --memory 5G \ + --bind $INPUTDIR:"/data/" \ + --bind "/corral/main/projects/NHERI/shared/${_tapisJobOwner}":"/home/${_tapisJobOwner}/MyData" \ + --bind /corral/main/projects/NHERI/public/projects:/home/NEES:ro \ + --bind /corral/main/projects/NHERI/community:/home/CommunityData:ro \ + --bind /corral/main/projects/NHERI/published:/home/NHERI-Published:ro \ + --bind /corral/main/projects/NHERI/projects \ + $DATADIR_MOUNT \ + docker://taccaci/designsafe-simcenter-vm:0.0.1 /bin/sh -c "cd /data; python3 /data/$PBESCRIPT" + +if [ ! $? ]; then + handle_error 1 + exit +fi