Menu

runSimulation.sh

runSimulation.sh

#!/bin/bash
# # runSimulation.sh
#
# Run a single drop impact simulation from the repository root. The script
# creates `simulationCases/<CaseNo>/`, copies the parameter file and source,
# compiles the case, and runs it (serial or MPI).
#
# ## Usage
# `./runSimulation.sh [options] [params_file]`
#
# ## Inputs
# - `params_file`: parameter file (default: `default.params`)
# - `QCC_FLAGS`: extra `qcc` compiler flags
#
# ## Outputs
# - `simulationCases/<CaseNo>/` containing `case.params`, compiled executable,
#   and simulation results
#
# ## Related
# - `runParameterSweep.sh`
# - `src-local/parse_params.sh`

set -euo pipefail  # Exit on error, unset variables, pipeline failures

# ============================================================
# Configuration
# ============================================================
SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"

# Source project configuration
if [ -f "\${SCRIPT_DIR}/.project_config" ]; then
    source "\${SCRIPT_DIR}/.project_config"
else
    echo "ERROR: .project_config not found" >&2
    exit 1
fi

# Source parameter parsing library
if [ -f "\${SCRIPT_DIR}/src-local/parse_params.sh" ]; then
    source "\${SCRIPT_DIR}/src-local/parse_params.sh"
else
    echo "ERROR: src-local/parse_params.sh not found" >&2
    exit 1
fi

# ============================================================
# Usage Information
# ============================================================
usage() {
    cat <<EOF
Usage: \$0 [OPTIONS] [params_file]

Run single drop impact simulation from root directory.
Creates case folder in simulationCases/<CaseNo>/ based on parameter file.

Options:
    -c, --compile-only    Compile but don't run simulation
    -d, --debug           Compile with debug flags (-g -DTRASH=1)
    -m, --mpi             Enable MPI parallel execution
    --cores N             Number of MPI cores (default: 4, requires --mpi)
    -v, --verbose         Verbose output
    -h, --help           Show this help message

Parameter file mode (default):
    \$0 default.params

If no parameter file specified, uses default.params from current directory.

Environment variables:
    QCC_FLAGS     Additional qcc compiler flags

Examples:
    # Run with default parameters (serial)
    \$0

    # Run with MPI parallel execution (4 cores)
    \$0 --mpi

    # Run with MPI using 8 cores
    \$0 --mpi --cores 8 default.params

    # Compile only (check for errors)
    \$0 --compile-only

    # Debug mode with memory checking
    \$0 --debug default.params

For more information, see README.md
EOF
}

# ============================================================
# Parse Command Line Options
# ============================================================
COMPILE_ONLY=0
DEBUG_FLAGS=""
QCC_FLAGS="\${QCC_FLAGS:-}"
VERBOSE=0
MPI_ENABLED=0
MPI_CORES=4

while [[ \$# -gt 0 ]]; do
    case \$1 in
        -c|--compile-only)
            COMPILE_ONLY=1
            shift
            ;;
        -d|--debug)
            DEBUG_FLAGS="-g -DTRASH=1"
            shift
            ;;
        -m|--mpi)
            MPI_ENABLED=1
            shift
            ;;
        --cores)
            MPI_CORES="\$2"
            if ! [[ "\$MPI_CORES" =~ ^[0-9]+\$ ]] || [ "\$MPI_CORES" -lt 1 ]; then
                echo "ERROR: --cores requires a positive integer, got: \$MPI_CORES" >&2
                exit 1
            fi
            shift 2
            ;;
        -v|--verbose)
            VERBOSE=1
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        -*)
            echo "ERROR: Unknown option: \$1" >&2
            usage
            exit 1
            ;;
        *)
            break
            ;;
    esac
done

# ============================================================
# Detect OS and Verify MPI
# ============================================================
OS_TYPE=\$(uname -s)

# Verify MPI tools if MPI is enabled
if [ \$MPI_ENABLED -eq 1 ]; then
    if ! command -v mpicc &> /dev/null; then
        echo "ERROR: mpicc not found. MPI compilation requires mpicc (OpenMPI or MPICH)." >&2
        echo "       Install MPI tools or run without --mpi flag for serial execution." >&2
        exit 1
    fi
    if ! command -v mpirun &> /dev/null; then
        echo "ERROR: mpirun not found. MPI execution requires mpirun (OpenMPI or MPICH)." >&2
        echo "       Install MPI tools or run without --mpi flag for serial execution." >&2
        exit 1
    fi
fi

# ============================================================
# Determine Parameter File
# ============================================================
PARAM_FILE="\${1:-default.params}"

if [ ! -f "\$PARAM_FILE" ]; then
    echo "ERROR: Parameter file not found: \$PARAM_FILE" >&2
    exit 1
fi

[ \$VERBOSE -eq 1 ] && echo "Parameter file: \$PARAM_FILE"

# ============================================================
# Parse Parameters to Get CaseNo
# ============================================================
parse_param_file "\$PARAM_FILE"

CASE_NO=\$(get_param "CaseNo")

if [ -z "\$CASE_NO" ]; then
    echo "ERROR: CaseNo not found in parameter file" >&2
    exit 1
fi

# Validate CaseNo is 4 digits
if ! [[ "\$CASE_NO" =~ ^[0-9]{4}\$ ]] || [ "\$CASE_NO" -lt 1000 ] || [ "\$CASE_NO" -gt 9999 ]; then
    echo "ERROR: CaseNo must be 4-digit (1000-9999), got: \$CASE_NO" >&2
    exit 1
fi

CASE_DIR="simulationCases/\${CASE_NO}"

echo "========================================="
echo "Drop Impact Simulation - Single Case"
echo "========================================="
echo "Case Number: \$CASE_NO"
echo "Case Directory: \$CASE_DIR"
echo "Parameter File: \$PARAM_FILE"
if [ \$MPI_ENABLED -eq 1 ]; then
    echo "Execution Mode: MPI Parallel (\$MPI_CORES cores)"
else
    echo "Execution Mode: Serial"
fi
echo ""

# ============================================================
# Create Case Directory
# ============================================================
if [ ! -d "\$CASE_DIR" ]; then
    echo "Creating case directory: \$CASE_DIR"
    mkdir -p "\$CASE_DIR"
else
    echo "Case directory exists (will use restart if available)"
fi

# Copy parameter file to case directory for record keeping
cp "\$PARAM_FILE" "\$CASE_DIR/case.params"

# Change to case directory
cd "\$CASE_DIR"
[ \$VERBOSE -eq 1 ] && echo "Working directory: \$(pwd)"

# ============================================================
# Compilation
# ============================================================
SRC_FILE_ORIG="../dropImpact.c"
SRC_FILE_LOCAL="dropImpact.c"
EXECUTABLE="dropImpact"

echo ""
echo "========================================="
echo "Compilation"
echo "========================================="

# Check if source file exists
if [ ! -f "\$SRC_FILE_ORIG" ]; then
    echo "ERROR: Source file \$SRC_FILE_ORIG not found" >&2
    exit 1
fi

# Copy source file to case directory for compilation
# This avoids qcc issues with relative paths and keeps a record
cp "\$SRC_FILE_ORIG" "\$SRC_FILE_LOCAL"
echo "Copied source file to case directory"

# Compile
echo "Compiling \$SRC_FILE_LOCAL..."

if [ \$MPI_ENABLED -eq 1 ]; then
    # MPI parallel compilation
    if [ "\$OS_TYPE" = "Darwin" ]; then
        # macOS
        [ \$VERBOSE -eq 1 ] && echo "Compiler: CC99='mpicc -std=c99' qcc"
        [ \$VERBOSE -eq 1 ] && echo "Include paths: -I../../src-local"
        [ \$VERBOSE -eq 1 ] && echo "Flags: -Wall -O2 -D_MPI=1 -disable-dimensions \$DEBUG_FLAGS \$QCC_FLAGS"

        CC99='mpicc -std=c99' qcc -I../../src-local \
            -Wall -O2 -D_MPI=1 -disable-dimensions \
            \$DEBUG_FLAGS \$QCC_FLAGS \
            "\$SRC_FILE_LOCAL" -o "\$EXECUTABLE" -lm
    else
        # Linux
        [ \$VERBOSE -eq 1 ] && echo "Compiler: CC99='mpicc -std=c99 -D_GNU_SOURCE=1' qcc"
        [ \$VERBOSE -eq 1 ] && echo "Include paths: -I../../src-local"
        [ \$VERBOSE -eq 1 ] && echo "Flags: -Wall -O2 -D_MPI=1 -disable-dimensions \$DEBUG_FLAGS \$QCC_FLAGS"

        CC99='mpicc -std=c99 -D_GNU_SOURCE=1' qcc -I../../src-local \
            -Wall -O2 -D_MPI=1 -disable-dimensions \
            \$DEBUG_FLAGS \$QCC_FLAGS \
            "\$SRC_FILE_LOCAL" -o "\$EXECUTABLE" -lm
    fi
else
    # Serial compilation
    [ \$VERBOSE -eq 1 ] && echo "Compiler: qcc"
    [ \$VERBOSE -eq 1 ] && echo "Include paths: -I../../src-local"
    [ \$VERBOSE -eq 1 ] && echo "Flags: -O2 -Wall -disable-dimensions \$DEBUG_FLAGS \$QCC_FLAGS"

    qcc -I../../src-local \
        -O2 -Wall -disable-dimensions \
        \$DEBUG_FLAGS \$QCC_FLAGS \
        "\$SRC_FILE_LOCAL" -o "\$EXECUTABLE" -lm
fi

if [ \$? -ne 0 ]; then
    echo "ERROR: Compilation failed" >&2
    exit 1
fi

echo "Compilation successful: \$EXECUTABLE"

# Exit if compile-only mode
if [ \$COMPILE_ONLY -eq 1 ]; then
    echo ""
    echo "Compile-only mode: Stopping here"
    cd ../..
    exit 0
fi

# ============================================================
# Execution
# ============================================================
echo ""
echo "========================================="
echo "Execution"
echo "========================================="

# Use the copied parameter file
echo "Running simulation with case.params"

if [ -f "restart" ]; then
    echo "Restart file found - simulation will resume from checkpoint"
fi

echo ""
echo "Starting simulation..."
echo "========================================="

# Run simulation
if [ \$MPI_ENABLED -eq 1 ]; then
    [ \$VERBOSE -eq 1 ] && echo "Command: mpirun -np \$MPI_CORES ./dropImpact case.params"
    mpirun -np \$MPI_CORES ./dropImpact case.params
else
    [ \$VERBOSE -eq 1 ] && echo "Command: ./dropImpact case.params"
    ./dropImpact case.params
fi

EXIT_CODE=\$?

echo "========================================="
if [ \$EXIT_CODE -eq 0 ]; then
    echo "Simulation completed successfully"
    echo "Output location: \$CASE_DIR"
else
    echo "Simulation failed with exit code \$EXIT_CODE"
fi
echo "========================================="

# Return to root directory
cd ../..

exit \$EXIT_CODE