Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
266 changes: 266 additions & 0 deletions SCRIPTS/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
#!/usr/bin/env bash
# Preflight checks for externals_xem2_XEM batch submission.
# For this to work, externals directory should be in /group/$USER/
# Usage: bash SCRIPTS/preflight.sh [target_tag]
set -u

# sets repo_root to the absolute path of the parent directory of the script’s location
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
scripts_dir="$repo_root/SCRIPTS"

# Key paths used by the workflow.
run_extern="$repo_root/run_extern"
externals_bin="$repo_root/externals_all"
make_grid="$repo_root/make_grid.f"
make_inputs="$scripts_dir/make_xem2_input_files.sh"
make_scripts="$scripts_dir/make_xem2_scripts.sh"
do_submit="$scripts_dir/do_farmsubmission.sh"
template_part1="$repo_root/INP/xem2_inp_part1.template"
hcswif_json="$repo_root/hcswif/json"
farm_out="/farm_out/${USER}/externals_xem2_XEM_OUT"

# Optional target tag (e.g., 12carbon22_hms) for target-specific checks.
target="${1-}"

# Buckets for errors (fail) and warnings (soft issues).
fails=()
warns=()

# Record helpers to keep output consistent.
# += appends a new element. "$1" is the first argument passed to the function. Quotes preserve spaces (critical!)
add_fail() { fails+=("$1"); }
add_warn() { warns+=("$1"); }

# Customize hardcoded user paths (replacing "gaskelld/GIT/").
fix_user="${USER-}"
if [[ -z "$fix_user" ]]; then
add_warn "USER not set; skipping hardcoded path fixups"
else
fix_files=(
"$scripts_dir/make_xem2_scripts.sh"
"$scripts_dir/do_farmsubmission.sh"
"$scripts_dir/do_src_farmsubmission.sh"
"$repo_root/hcswif/hcswif.py"
"$repo_root/run_extern"
)
for fix_file in "${fix_files[@]}"; do
if [[ -f "$fix_file" ]]; then
python - <<'PY' "$fix_file" "$fix_user"
import sys
path = sys.argv[1]
user = sys.argv[2]
needle = "gaskelld/GIT/"
repl = f"{user}/"
with open(path, "r") as f:
data = f.read()
if needle in data:
data = data.replace(needle, repl)
with open(path, "w") as f:
f.write(data)
PY
fi
done
fi

# Simple existence check for critical files.
check_file() {
local path="$1"
local label="$2"
if [[ ! -e "$path" ]]; then
add_fail "${label} missing: ${path}"
fi
}

# Executable bit check for runnable files.
check_exec() {
local path="$1"
local label="$2"
if [[ -e "$path" && ! -x "$path" ]]; then
add_warn "${label} not executable: ${path}"
fi
}

# Directory existence + writability checks for output locations.
check_dir_writable() {
local path="$1"
local label="$2"
if [[ ! -d "$path" ]]; then
# Try to create missing directories when possible.
if mkdir -p "$path" 2>/dev/null; then
add_warn "${label} created: ${path}"
else
add_warn "${label} missing: ${path}"
return
fi
fi
if [[ ! -w "$path" ]]; then
add_warn "${label} not writable: ${path}"
fi
}

# Basic path checks for expected files.
check_file "$run_extern" "run_extern"
check_file "$externals_bin" "externals_all"
check_file "$make_grid" "make_grid.f"
check_file "$make_inputs" "make_xem2_input_files.sh"
check_file "$make_scripts" "make_xem2_scripts.sh"
check_file "$do_submit" "do_farmsubmission.sh"
check_file "$template_part1" "xem2_inp_part1.template"

# Executable bit sanity.
check_exec "$run_extern" "run_extern"
check_exec "$externals_bin" "externals_all"

# Ensure output locations exist and are writable.
check_dir_writable "$repo_root/runout" "runout dir"
check_dir_writable "$hcswif_json" "hcswif/json"
check_dir_writable "$farm_out" "farm_out"
check_dir_writable "$repo_root/RUNPLAN" "RUNPLAN dir"
check_dir_writable "$repo_root/OUT" "out dir"

# Extract angle settings and runplan filename from make_grid.f.
grid_vals="$(python - <<'PY'
import re
vals = {"thetamin": None, "thetamax": None, "thetastep": None}
runplan_line = None
try:
with open("make_grid.f", "r") as f:
for line in f:
if line.lstrip().startswith("c"):
continue
if runplan_line is None and "RUNPLAN/" in line and "part1" in line:
runplan_line = line.strip()
m = re.search(r"\b(thetamin|thetamax|thetastep)\s*=\s*([0-9.+-]+)", line)
if m:
vals[m.group(1)] = m.group(2)
except FileNotFoundError:
pass
print("thetamin=" + (vals["thetamin"] or ""))
print("thetamax=" + (vals["thetamax"] or ""))
print("thetastep=" + (vals["thetastep"] or ""))
print("runplan=" + (runplan_line or ""))
PY
)"

# Unpack angle values for later consistency checks.
thetamin="$(printf "%s\n" "$grid_vals" | awk -F= '/^thetamin=/{print $2}')"
thetamax="$(printf "%s\n" "$grid_vals" | awk -F= '/^thetamax=/{print $2}')"
thetastep="$(printf "%s\n" "$grid_vals" | awk -F= '/^thetastep=/{print $2}')"
runplan_line="$(printf "%s\n" "$grid_vals" | awk -F= '/^runplan=/{print $2}')"

# Compute how many angle settings you should have, based on the angle range and step size defined in make_grid.f
expected_angles=""
if [[ -n "$thetamin" && -n "$thetamax" && -n "$thetastep" ]]; then
expected_angles="$(python - <<PY
import math
tmin=float("$thetamin")
tmax=float("$thetamax")
tstep=float("$thetastep")
n=int((tmax - tmin)/tstep) + 1
print(n)
PY
)"
fi

# Parse make_xem2_input_files.sh or make_xem2_scripts.sh for min_angle and number of loop iterations.
parse_csh_vals() {
local path="$1"
python - <<'PY' "$path"
import re,sys
min_angle = None
count = None
with open(sys.argv[1], "r") as f:
for line in f:
if line.strip().startswith("#"):
continue
m = re.search(r"set\s+min_angle\s*=\s*([0-9.+-]+)", line)
if m:
min_angle = m.group(1)
m = re.search(r"while\s*\\(\\$i\\s*<=\\s*([0-9]+)\\)", line)
if m:
count = m.group(1)
break
print("min_angle=" + (min_angle or ""))
print("count=" + (count or ""))
PY
}

# Compare input file generator angles with make_grid.f.
if [[ -e "$make_inputs" ]]; then
input_vals="$(parse_csh_vals "$make_inputs")"
input_min="$(printf "%s\n" "$input_vals" | awk -F= '/^min_angle=/{print $2}')"
input_cnt="$(printf "%s\n" "$input_vals" | awk -F= '/^count=/{print $2}')"
if [[ -n "$thetamin" && -n "$input_min" && "$thetamin" != "$input_min" ]]; then
add_warn "make_xem2_input_files.sh min_angle (${input_min}) != make_grid.f thetamin (${thetamin})"
fi
if [[ -n "$expected_angles" && -n "$input_cnt" && "$expected_angles" != "$input_cnt" ]]; then
add_warn "make_xem2_input_files.sh loop count (${input_cnt}) != expected angles (${expected_angles})"
fi
fi

# Compare make_xem2_scripts.sh generator angles with make_grid.f.
if [[ -e "$make_scripts" ]]; then
script_vals="$(parse_csh_vals "$make_scripts")"
script_min="$(printf "%s\n" "$script_vals" | awk -F= '/^min_angle=/{print $2}')"
script_cnt="$(printf "%s\n" "$script_vals" | awk -F= '/^count=/{print $2}')"
if [[ -n "$thetamin" && -n "$script_min" && "$thetamin" != "$script_min" ]]; then
add_warn "make_xem2_scripts.sh min_angle (${script_min}) != make_grid.f thetamin (${thetamin})"
fi
if [[ -n "$expected_angles" && -n "$script_cnt" && "$expected_angles" != "$script_cnt" ]]; then
add_warn "make_xem2_scripts.sh loop count (${script_cnt}) != expected angles (${expected_angles})"
fi
fi

# Ensure submission time respects the 1-day production limit.
if [[ -e "$do_submit" ]]; then
time_val="$(awk '/--time[[:space:]]+[0-9]+/ {for(i=1;i<=NF;i++) if($i=="--time") print $(i+1)}' "$do_submit" | head -n1)"
if [[ -n "$time_val" && "$time_val" -gt 86400 ]]; then
add_warn "do_farmsubmission.sh --time (${time_val}) exceeds 86400 seconds"
fi
fi

# Target-specific checks: ensure script count matches expected angle count/bin size.
if [[ -n "$target" ]]; then
scripts_glob=( "$repo_root/hcswif/FARM_SCRIPTS"/*_"${target}"_part1*.sh )
if [[ ${#scripts_glob[@]} -eq 1 && ! -e "${scripts_glob[0]}" ]]; then
add_warn "no FARM_SCRIPTS for target ${target}"
else
script_count="${#scripts_glob[@]}"
if [[ -n "$expected_angles" && "$script_count" -ne "$expected_angles" ]]; then
add_warn "script count (${script_count}) != expected angles (${expected_angles}) for target ${target}"
fi
first_script="${scripts_glob[0]}"
if [[ -e "$first_script" ]]; then
run_path="$(awk 'NR==2{print $1}' "$first_script")"
if [[ -n "$run_path" && "$run_path" != "$repo_root/run_extern" ]]; then
add_warn "FARM_SCRIPTS run_extern path differs: ${run_path}"
fi
fi
fi
fi

# Final report.
if [[ ${#fails[@]} -eq 0 && ${#warns[@]} -eq 0 ]]; then
echo "Preflight OK: no issues found."
exit 0
fi

if [[ ${#fails[@]} -gt 0 ]]; then
echo "Preflight errors:"
for f in "${fails[@]}"; do
echo " - ${f}"
done
fi

if [[ ${#warns[@]} -gt 0 ]]; then
echo "Preflight warnings:"
for w in "${warns[@]}"; do
echo " - ${w}"
done
fi

# Non-zero exit on errors.
if [[ ${#fails[@]} -gt 0 ]]; then
exit 2
fi
exit 0