Menu

runParameterSweep.sh

runParameterSweep.sh

#!/bin/bash
#
# runParameterSweep.sh -- generate and (optionally) run a We-De sweep.
#
# Reads a sweep configuration (sweep-fixedBeta.params or
# sweep-fixedEc.params), forms the Cartesian product of SWEEP_WE x
# SWEEP_DE, assigns a deterministic CaseNo (We outer, De inner), derives
# the per-case parameters, and writes one case.params per case that is
# consumed by simulationCases/dropImpactVE.c via src-local/case-params.h
# (the single, shared parameter parser -- no parsing is duplicated here).

set -euo pipefail

SCRIPT_DIR=\$(cd "\$(dirname "\${BASH_SOURCE[0]}")" && pwd)
REPO_ROOT="\$SCRIPT_DIR"
CASE_FILE="\${REPO_ROOT}/simulationCases/dropImpactVE.c"
OUT_BASE="\${REPO_ROOT}/simulationCases/dropImpactVE"

usage() {
  cat <<'EOF'
Usage: runParameterSweep.sh [--config FILE] [--start N] [--end M] [--dry-run]

Options:
  --config FILE  Sweep config (default: sweep-fixedBeta.params).
                 Provided: sweep-fixedBeta.params, sweep-fixedEc.params.
  --start N      First CaseNo to run (default 1).
  --end M        Last CaseNo to run (default = total number of cases).
  --dry-run      Print the generated case plan and exit (no compile/run).
  --help         Show this help.

Each case is generated under simulationCases/dropImpactVE/<CaseNo>/case.params.
The binary is compiled once and reused across cases.
EOF
}

CONFIG="\${REPO_ROOT}/sweep-fixedBeta.params"
CASE_START=1
CASE_END=""
DRY_RUN=0

while [ "\$#" -gt 0 ]; do
  case "\$1" in
    --config) CONFIG="\$2"; shift 2 ;;
    --start)  CASE_START="\$2"; shift 2 ;;
    --end)    CASE_END="\$2"; shift 2 ;;
    --dry-run) DRY_RUN=1; shift ;;
    --help|-h) usage; exit 0 ;;
    *) echo "Unknown option: \$1"; usage; exit 1 ;;
  esac
done

[ -f "\$CONFIG" ] || { echo "Config not found: \$CONFIG"; exit 1; }

# --- single key=value reader for the sweep config (not the sim params) ---
cfg() {
  awk -F= -v key="\$1" '
    { sub(/[#;].*/, "") }                 # strip comments
    {
      gsub(/^[ \t]+|[ \t]+\$/, "", \$1)
      if (\$1 == key) {
        v = \$2
        gsub(/^[ \t]+|[ \t]+\$/, "", v)
        print v
        exit
      }
    }' "\$CONFIG"
}

MODE=\$(cfg MODE)
SWEEP_WE=\$(cfg SWEEP_WE)
SWEEP_DE=\$(cfg SWEEP_DE)
OHS=\$(cfg Ohs)
BETA=\$(cfg beta)
EC=\$(cfg Ec)
MAXLEVEL=\$(cfg MAXlevel)
LDOMAIN=\$(cfg Ldomain)
TMAX=\$(cfg tmax)
BO=\$(cfg Bo)
THETA_INIT=\$(cfg thetaInit)
THETA_E=\$(cfg thetaE)
TTHETA=\$(cfg ttheta)
THETA_RATE=\$(cfg thetaRate)

[ -n "\$SWEEP_WE" ] && [ -n "\$SWEEP_DE" ] || { echo "SWEEP_WE/SWEEP_DE missing in \$CONFIG"; exit 1; }
[ "\$MODE" = "fixedBeta" ] || [ "\$MODE" = "fixedEc" ] || { echo "MODE must be fixedBeta or fixedEc"; exit 1; }
if [ "\$MODE" = "fixedBeta" ] && [ -z "\$BETA" ]; then echo "fixedBeta mode needs beta"; exit 1; fi
if [ "\$MODE" = "fixedEc" ] && [ -z "\$EC" ]; then echo "fixedEc mode needs Ec"; exit 1; fi

# total case count and validation
NWE=\$(echo \$SWEEP_WE | wc -w)
NDE=\$(echo \$SWEEP_DE | wc -w)
NTOT=\$((NWE * NDE))
[ -z "\$CASE_END" ] && CASE_END=\$NTOT
if [ "\$CASE_START" -lt 1 ] || [ "\$CASE_END" -gt "\$NTOT" ] || [ "\$CASE_START" -gt "\$CASE_END" ]; then
  echo "Invalid range: --start \$CASE_START --end \$CASE_END (total cases = \$NTOT)"; exit 1
fi

echo "Sweep mode: \$MODE   |   We x De = \${NWE} x \${NDE} = \${NTOT} cases"
echo "Running CaseNo \${CASE_START}..\${CASE_END}"
printf '%-7s %-7s %-9s %-9s %-9s %-9s\n' CaseNo We De Ec Oha lambda
echo "-----------------------------------------------------------------"

# derive + write one case.params per case
gen_case() {
  local no="\$1" we="\$2" de="\$3"
  local ec oha
  oha=\$(awk -v o="\$OHS" 'BEGIN{printf "%.6g", 0.01*o}')
  if awk -v d="\$de" 'BEGIN{exit !(d+0==0)}'; then
    ec=0                                   # De = 0 -> Newtonian
  elif [ "\$MODE" = "fixedBeta" ]; then
    ec=\$(awk -v o="\$OHS" -v b="\$BETA" -v d="\$de" 'BEGIN{printf "%.6g", o*(1-b)/(b*d)}')
  else
    ec="\$EC"
  fi
  local lam
  lam=\$(awk -v d="\$de" -v w="\$we" 'BEGIN{printf "%.6g", d*sqrt(w)}')

  local dir
  dir=\$(printf '%s/%03d' "\$OUT_BASE" "\$no")
  printf '%-7s %-7s %-9s %-9s %-9s %-9s\n' "\$no" "\$we" "\$de" "\$ec" "\$oha" "\$lam"

  [ "\$DRY_RUN" -eq 1 ] && return 0

  mkdir -p "\$dir"
  cat > "\$dir/case.params" <<EOF
# auto-generated by runParameterSweep.sh (\$MODE), CaseNo \$no
MAXlevel = \$MAXLEVEL
Ldomain = \$LDOMAIN
tmax = \$TMAX
We = \$we
Ohs = \$OHS
Oha = \$oha
De = \$de
Ec = \$ec
Bo = \$BO
purelyElastic = 0
thetaInit = \$THETA_INIT
thetaE = \$THETA_E
ttheta = \$TTHETA
thetaRate = \$THETA_RATE
EOF
}

# enumerate (deterministic: We outer, De inner)
declare -a PLAN_NO PLAN_DIR
no=0
for we in \$SWEEP_WE; do
  for de in \$SWEEP_DE; do
    no=\$((no + 1))
    [ "\$no" -lt "\$CASE_START" ] && continue
    [ "\$no" -gt "\$CASE_END" ] && continue
    gen_case "\$no" "\$we" "\$de"
    PLAN_NO+=("\$no")
    PLAN_DIR+=("\$(printf '%s/%03d' "\$OUT_BASE" "\$no")")
  done
done

if [ "\$DRY_RUN" -eq 1 ]; then
  echo "-----------------------------------------------------------------"
  echo "Dry run: \${#PLAN_NO[@]} case.params would be written under \$OUT_BASE/<CaseNo>/"
  exit 0
fi

# compile once, reuse the binary for every case (compile in the build dir
# with a local filename so qcc's intermediate files do not collide)
echo "Compiling dropImpactVE once ..."
mkdir -p "\$OUT_BASE"
cp "\$CASE_FILE" "\$OUT_BASE/dropImpactVE.c"
( cd "\$OUT_BASE" && qcc -I"\${REPO_ROOT}/src-local" -O2 -Wall \
    -disable-dimensions dropImpactVE.c -o dropImpactVE -lm )

for idx in "\${!PLAN_NO[@]}"; do
  dir="\${PLAN_DIR[\$idx]}"
  echo ">> CaseNo \${PLAN_NO[\$idx]} in \$dir"
  cp "\${OUT_BASE}/dropImpactVE" "\$dir/dropImpactVE"
  ( cd "\$dir" && ./dropImpactVE case.params )
done

echo "Sweep complete: \${#PLAN_NO[@]} cases."