#!/bin/bash
# runParameterSweep.sh - Generate deterministic case files and run them sequentially.
set -euo pipefail
SCRIPT_DIR="\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
if [ -f "\${SCRIPT_DIR}/src-local/parse_params.sh" ]; then
# shellcheck disable=SC1091
source "\${SCRIPT_DIR}/src-local/parse_params.sh"
else
echo "ERROR: src-local/parse_params.sh not found" >&2
exit 1
fi
usage() {
cat <<'EOF'
Usage: ./runParameterSweep.sh [OPTIONS] [sweep_file]
Run a deterministic parameter sweep from a sweep.params-style file.
Each generated case gets a 4-digit CaseNo, a generated case.params file,
and is executed sequentially via runSimulation.sh.
Options:
-n, --dry-run Show generated cases without running them
--skip-init Run only the main phase for each generated case
-v, --verbose Print extra progress information
-c, --compile-only Compile generated cases but do not run them
-m, --mpi Enable MPI for every generated case
--cores N Number of MPI ranks per case (default: 4)
-h, --help Show this help message
Examples:
./runParameterSweep.sh
./runParameterSweep.sh --dry-run sweep.params
./runParameterSweep.sh --mpi --cores 8 sweep.params
EOF
}
DRY_RUN=0
SKIP_INIT=0
VERBOSE=0
COMPILE_ONLY=0
MPI_ENABLED=0
MPI_CORES=4
while [[ \$# -gt 0 ]]; do
case "\$1" in
-n|--dry-run)
DRY_RUN=1
shift
;;
--skip-init)
SKIP_INIT=1
shift
;;
-v|--verbose)
VERBOSE=1
shift
;;
-c|--compile-only)
COMPILE_ONLY=1
shift
;;
-m|--mpi)
MPI_ENABLED=1
shift
;;
--cores)
if [ \$# -lt 2 ]; then
echo "ERROR: --cores requires a positive integer" >&2
exit 1
fi
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
;;
-h|--help)
usage
exit 0
;;
-*)
echo "ERROR: Unknown option: \$1" >&2
usage
exit 1
;;
*)
break
;;
esac
done
SWEEP_FILE="\${1:-sweep.params}"
if [ ! -f "\$SWEEP_FILE" ]; then
echo "ERROR: Sweep file not found: \$SWEEP_FILE" >&2
exit 1
fi
SWEEP_FILE_ABS="\$(cd "\$(dirname "\$SWEEP_FILE")" && pwd)/\$(basename "\$SWEEP_FILE")"
SWEEP_DIR="\$(dirname "\$SWEEP_FILE_ABS")"
echo "========================================="
echo "Jumping Drops - Parameter Sweep"
echo "========================================="
echo "Sweep file: \$SWEEP_FILE_ABS"
[ "\$DRY_RUN" -eq 1 ] && echo "Mode: Dry run"
echo
# shellcheck disable=SC1090
source "\$SWEEP_FILE_ABS"
if [ -z "\${BASE_CONFIG:-}" ]; then
echo "ERROR: BASE_CONFIG not defined in \$SWEEP_FILE_ABS" >&2
exit 1
fi
if [ -z "\${CASE_START:-}" ] || [ -z "\${CASE_END:-}" ]; then
echo "ERROR: CASE_START and CASE_END must be defined in \$SWEEP_FILE_ABS" >&2
exit 1
fi
validate_case_no "\$CASE_START" || exit 1
validate_case_no "\$CASE_END" || exit 1
if [ "\$CASE_END" -lt "\$CASE_START" ]; then
echo "ERROR: CASE_END must be >= CASE_START" >&2
exit 1
fi
if [[ "\$BASE_CONFIG" = /* ]]; then
BASE_CONFIG_PATH="\$BASE_CONFIG"
else
BASE_CONFIG_PATH="\${SWEEP_DIR}/\${BASE_CONFIG}"
fi
if [ ! -f "\$BASE_CONFIG_PATH" ]; then
echo "ERROR: Base configuration file not found: \$BASE_CONFIG_PATH" >&2
exit 1
fi
require_params "\$BASE_CONFIG_PATH" CaseNo Oh Bo MAXlevel tmax || exit 1
SWEEP_VARS=()
SWEEP_VALUES=()
while IFS='=' read -r key value; do
key="\$(trim_whitespace "\${key%%#*}")"
value="\$(trim_whitespace "\${value%%#*}")"
[ -z "\$key" ] && continue
if [[ "\$key" =~ ^SWEEP_([A-Za-z_][A-Za-z0-9_]*)\$ ]]; then
SWEEP_VARS+=("\${BASH_REMATCH[1]}")
SWEEP_VALUES+=("\$value")
fi
done < "\$SWEEP_FILE_ABS"
if [ "\${#SWEEP_VARS[@]}" -eq 0 ]; then
echo "ERROR: No SWEEP_* variables found in \$SWEEP_FILE_ABS" >&2
exit 1
fi
echo "Base configuration: \$BASE_CONFIG_PATH"
echo "Case range: \$CASE_START to \$CASE_END"
echo "Sweep variables:"
for i in "\${!SWEEP_VARS[@]}"; do
echo " \${SWEEP_VARS[\$i]} = \${SWEEP_VALUES[\$i]}"
done
echo
TEMP_DIR="\$(mktemp -d "\${TMPDIR:-/tmp}/jumping-drops-sweep.XXXXXX")"
trap 'rm -rf "\$TEMP_DIR"' EXIT
CASE_NUM="\$CASE_START"
COMBINATION_COUNT=0
CASE_FILES=()
generate_combinations() {
local depth="\$1"
shift
local current_values=("\$@")
local case_file
local values
local value_array=()
local val
local i
if [ "\$depth" -eq "\${#SWEEP_VARS[@]}" ]; then
case_file="\${TEMP_DIR}/case_\$(printf "%04d" "\$CASE_NUM").params"
cp "\$BASE_CONFIG_PATH" "\$case_file"
set_param_in_file "\$case_file" "CaseNo" "\$CASE_NUM"
for i in "\${!SWEEP_VARS[@]}"; do
set_param_in_file "\$case_file" "\${SWEEP_VARS[\$i]}" "\${current_values[\$i]}"
done
CASE_FILES+=("\$case_file")
COMBINATION_COUNT=\$((COMBINATION_COUNT + 1))
if [ "\$DRY_RUN" -eq 1 ] || [ "\$VERBOSE" -eq 1 ]; then
echo "Case \$(printf "%04d" "\$CASE_NUM"):"
for i in "\${!SWEEP_VARS[@]}"; do
echo " \${SWEEP_VARS[\$i]} = \${current_values[\$i]}"
done
echo
fi
CASE_NUM=\$((CASE_NUM + 1))
return
fi
values="\${SWEEP_VALUES[\$depth]}"
IFS=',' read -r -a value_array <<< "\$values"
for val in "\${value_array[@]}"; do
val="\$(trim_whitespace "\$val")"
if [ "\${#current_values[@]}" -eq 0 ]; then
generate_combinations \$((depth + 1)) "\$val"
else
generate_combinations \$((depth + 1)) "\${current_values[@]}" "\$val"
fi
done
}
generate_combinations 0
EXPECTED_COUNT=\$((CASE_END - CASE_START + 1))
if [ "\$COMBINATION_COUNT" -ne "\$EXPECTED_COUNT" ]; then
echo "ERROR: Generated \$COMBINATION_COUNT combinations, but CASE_START/CASE_END require \$EXPECTED_COUNT" >&2
exit 1
fi
echo "Generated \$COMBINATION_COUNT parameter combinations"
echo
if [ "\$DRY_RUN" -eq 1 ]; then
echo "Dry run complete. No simulations executed."
exit 0
fi
echo "========================================="
echo "Running Simulations"
echo "========================================="
echo "Total cases: \$COMBINATION_COUNT"
if [ "\$MPI_ENABLED" -eq 1 ]; then
echo "MPI ranks per case: \$MPI_CORES"
fi
if [ "\$SKIP_INIT" -eq 1 ]; then
echo "Init phase: skipped"
fi
echo
for param_file in "\${CASE_FILES[@]}"; do
require_params "\$param_file" CaseNo Oh Bo MAXlevel tmax || exit 1
CASE_NO="\$(get_param "CaseNo")"
validate_case_no "\$CASE_NO" || exit 1
RUN_ARGS=()
if [ "\$SKIP_INIT" -eq 1 ]; then
RUN_ARGS+=("--main-only")
elif [ -f "\${SCRIPT_DIR}/simulationCases/\${CASE_NO}/dumpInit" ]; then
echo "Case \$CASE_NO: dumpInit found, running main phase only"
RUN_ARGS+=("--main-only")
else
echo "Case \$CASE_NO: running init + main"
fi
[ "\$COMPILE_ONLY" -eq 1 ] && RUN_ARGS+=("--compile-only")
[ "\$MPI_ENABLED" -eq 1 ] && RUN_ARGS+=("--mpi" "--cores" "\$MPI_CORES")
[ "\$VERBOSE" -eq 1 ] && RUN_ARGS+=("--verbose")
"\${SCRIPT_DIR}/runSimulation.sh" "\${RUN_ARGS[@]}" "\$param_file"
done
echo
echo "========================================="
echo "Parameter Sweep Complete"
echo "========================================="
echo "Total cases: \$COMBINATION_COUNT"
echo "Case range: \$CASE_START to \$CASE_END"
echo "Output location: \${SCRIPT_DIR}/simulationCases"