Skip to content

Commit 977a213

Browse files
sbryngelsonclaude
andcommitted
Merge upstream/master into ruff, resolve case.py conflict
Kept upstream patch type 13 reclassification (3D->2D) with ruff formatting. Auto-formatted new upstream example files for ruff import sorting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 734b708 + 92e751f commit 977a213

34 files changed

Lines changed: 1341 additions & 251 deletions

.github/scripts/clean-build.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
# Provides clean_build(): renames build/ aside and deletes it in the background.
3+
# mv is a metadata-only operation that succeeds even with stale NFS file handles,
4+
# unlike rm -rf which fails on ESTALE. The background delete is best-effort and
5+
# scoped to this job's PID to avoid races with concurrent matrix jobs.
6+
#
7+
# Usage: source .github/scripts/clean-build.sh
8+
# clean_build
9+
10+
clean_build() {
11+
# Clean up leftover stale directories from previous runs before adding a new one.
12+
rm -rf build.stale.* 2>/dev/null || true
13+
mv build "build.stale.$$" 2>/dev/null || true
14+
rm -rf "build.stale.$$" 2>/dev/null & disown
15+
}

.github/scripts/prebuild-case-optimization.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ case "$cluster" in
2222
*) echo "ERROR: Unknown cluster '$cluster'"; exit 1 ;;
2323
esac
2424

25-
rm -rf build
25+
source .github/scripts/clean-build.sh
26+
clean_build
2627

2728
. ./mfc.sh load -c "$flag" -m g
2829

.github/scripts/retry-sbatch.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
# Provides retry_sbatch(): submits a job script string via sbatch with retries.
3+
# Only retries on known transient SLURM/infrastructure errors (socket timeouts,
4+
# connection failures). Hard failures (bad account, invalid partition, QOS
5+
# violations) are not retried.
6+
#
7+
# Usage: source .github/scripts/retry-sbatch.sh
8+
# job_id=$(retry_sbatch "$script_contents")
9+
10+
retry_sbatch() {
11+
local script_contents="$1"
12+
local max_attempts=3
13+
local attempt=1
14+
local submit_output job_id last_output=""
15+
16+
while [ $attempt -le $max_attempts ]; do
17+
echo "sbatch attempt $attempt of $max_attempts..." >&2
18+
submit_output=$(printf '%s\n' "$script_contents" | sbatch 2>&1) || true
19+
job_id=$(echo "$submit_output" | grep -oE 'Submitted batch job ([0-9]+)' | grep -oE '[0-9]+$')
20+
if [ -n "$job_id" ]; then
21+
echo "$job_id"
22+
return 0
23+
fi
24+
last_output="$submit_output"
25+
echo "sbatch failed: $submit_output" >&2
26+
if ! echo "$submit_output" | grep -qiE "timed out|connection refused|connection reset|temporarily unavailable"; then
27+
echo "Non-transient sbatch failure — not retrying." >&2
28+
return 1
29+
fi
30+
if [ $attempt -lt $max_attempts ]; then
31+
echo "Transient error — retrying in 30s..." >&2
32+
sleep 30
33+
fi
34+
attempt=$((attempt + 1))
35+
done
36+
37+
echo "sbatch failed after $max_attempts attempts. Last error: $last_output" >&2
38+
return 1
39+
}
40+

.github/scripts/submit-slurm-job.sh

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ if [ "$device" = "cpu" ]; then
8585
case "$cluster" in
8686
phoenix)
8787
sbatch_device_opts="\
88-
#SBATCH -p cpu-small
89-
#SBATCH --ntasks-per-node=24
90-
#SBATCH --mem-per-cpu=2G"
88+
#SBATCH -p cpu-small,cpu-medium,cpu-large
89+
#SBATCH --ntasks-per-node=12
90+
#SBATCH --mem-per-cpu=8G"
9191
;;
9292
frontier|frontier_amd)
9393
sbatch_device_opts="\
@@ -161,8 +161,9 @@ rm -f "$output_file"
161161
# --- Module load mode (short form) ---
162162
module_mode=$([ "$device" = "gpu" ] && echo "g" || echo "c")
163163

164-
# --- Submit ---
165-
submit_output=$(sbatch <<EOT
164+
# --- Submit (with retries for transient SLURM errors) ---
165+
source "${SCRIPT_DIR}/retry-sbatch.sh"
166+
_sbatch_script=$(cat <<EOT
166167
#!/bin/bash
167168
#SBATCH -J ${job_prefix}-${job_slug}
168169
#SBATCH --account=${account}
@@ -192,12 +193,8 @@ $sbatch_script_contents
192193
EOT
193194
)
194195

195-
job_id=$(echo "$submit_output" | grep -oE '[0-9]+')
196-
if [ -z "$job_id" ]; then
197-
echo "ERROR: Failed to submit job. sbatch output:"
198-
echo "$submit_output"
199-
exit 1
200-
fi
196+
job_id=$(retry_sbatch "$_sbatch_script")
197+
unset _sbatch_script
201198

202199
echo "Submitted batch job $job_id"
203200
echo "$job_id" > "$id_file"

.github/workflows/common/bench.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ fi
2525
# Phoenix builds inside SLURM; Frontier pre-builds via build.sh on the login node.
2626
# Phoenix: always nuke stale builds (heterogeneous compute nodes → ISA mismatch risk).
2727
if [ "$job_cluster" = "phoenix" ]; then
28-
rm -rf build
28+
source .github/scripts/clean-build.sh
29+
clean_build
2930
fi
3031

3132
if [ ! -d "build" ]; then

.github/workflows/common/test.sh

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,26 @@ set -euo pipefail
88
source .github/scripts/gpu-opts.sh
99
build_opts="$gpu_opts"
1010

11+
# --- Phoenix TMPDIR setup ---
12+
# Phoenix compute nodes have a small /tmp. With 8 parallel test threads each
13+
# spawning MPI processes, it fills up and ORTE session dir creation fails.
14+
# Redirect TMPDIR to project storage, same as bench.sh.
15+
if [ "$job_cluster" = "phoenix" ]; then
16+
tmpbuild=/storage/project/r-sbryngelson3-0/sbryngelson3/mytmp_build
17+
currentdir=$tmpbuild/run-$(( RANDOM % 9000 ))
18+
mkdir -p $tmpbuild
19+
mkdir -p $currentdir
20+
export TMPDIR=$currentdir
21+
trap 'rm -rf "$currentdir" || true' EXIT
22+
fi
23+
1124
# --- Build (if not pre-built on login node) ---
1225
# Phoenix builds inside SLURM; Frontier pre-builds via build.sh on the login node.
1326
# Phoenix builds inside SLURM on heterogeneous compute nodes — always start fresh
1427
# to avoid SIGILL from stale binaries compiled on a different microarchitecture.
1528
if [ "$job_cluster" = "phoenix" ]; then
16-
rm -rf build
29+
source .github/scripts/clean-build.sh
30+
clean_build
1731
fi
1832

1933
if [ ! -d "build" ]; then

.github/workflows/frontier/build.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ build_opts="$gpu_opts"
2020

2121
. ./mfc.sh load -c $compiler_flag -m $([ "$job_device" = "gpu" ] && echo "g" || echo "c")
2222

23-
rm -rf build
23+
source .github/scripts/clean-build.sh
24+
clean_build
2425

2526
source .github/scripts/retry-build.sh
2627
if [ "$run_bench" == "bench" ]; then

docs/documentation/case.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,11 @@ The number has to be a positive integer.
267267
- `num_fluids` defines the total number of fluids defined in each of the patches.
268268
The number has to be a positive integer.
269269

270-
- `patch_icpp(j)%%geometry` defines the type of geometry of $j$-th patch by using an integer from 1 to 13.
270+
- `patch_icpp(j)%%geometry` defines the type of geometry of $j$-th patch by using an integer from 1 to 21.
271271
Definition of the patch type for each integer is listed in table [Patch Types](#patch-types).
272272

273273
- `[x,y,z]_centroid`, `length_[x,y,z]`, and/or `radius` are used to uniquely define the geometry of the patch with given type.
274-
Requisite combinations of the parameters for each type can be found in is listed in table [Patch types](#patch-types).
274+
Requisite combinations of the parameters for each type are listed in table [Patch types](#patch-types).
275275

276276
- `patch_icpp(j)%%alter_patch(i)` activates alternation of `patch(i)` with `patch(j)`.
277277
For instance, in a 2D simulation, when a cylindrical `patch(2)` is immersed in a rectangular `patch(1)`:
@@ -1087,8 +1087,8 @@ This boundary condition can be used for subsonic inflow (`bc_[x,y,z]%[beg,end]`
10871087
| 10 | Cylinder | 3 | Y | Requires `[x,y,z]_centroid`, `radius`, and `length_[x,y,z]`. |
10881088
| 11 | Sweep plane | 3 | Y | Not coordinate-aligned. Requires `x[y,z]_centroid` and `normal(i)`. |
10891089
| 12 | Ellipsoid | 3 | Y | Requires `[x,y,z]_centroid` and `radii(i)`. |
1090-
| 13 | N/A | N/A | N/A | No longer exists. Empty. |
1091-
| 14 | Spherical Harmonic | 3 | N | Requires `[x,y,z]_centroid`, `radius`, `epsilon`, `beta` |
1090+
| 13 | 2D modal (Fourier) | 2 | Y | Requires `x_centroid`, `y_centroid`, `radius`. Optional: `fourier_cos(n)`, `fourier_sin(n)` (n=1..10), `modal_clip_r_to_min`, `modal_r_min`, `modal_use_exp_form`. |
1091+
| 14 | 3D spherical harmonic | 3 | Y | Requires `x_centroid`, `y_centroid`, `z_centroid`, `radius`. Optional: `sph_har_coeff(l,m)` (l=0..5, m=-l..l). |
10921092
| 15 | N/A | N/A | N/A | No longer exists. Empty. |
10931093
| 16 | 1D bubble pulse | 1 | N | Requires `x_centroid`, `length_x` |
10941094
| 17 | Spiral | 2 | N | Requires `[x,y]_centroid` |
@@ -1102,6 +1102,19 @@ This includes types exclusive to one-, two-, and three-dimensional problems.
11021102
The patch type number (`#`) corresponds to the input value in `input.py` labeled `patch_icpp(j)%%geometry` where $j$ is the patch index.
11031103
Each patch requires a different set of parameters, which are also listed in this table.
11041104

1105+
**Geometry 13: 2D modal (Fourier):**
1106+
Boundary is at polar angle \f$\theta = \mathrm{atan2}(y - y_{\mathrm{centroid}}, x - x_{\mathrm{centroid}})\f$.
1107+
1108+
- **Additive form** (default, `modal_use_exp_form` false):
1109+
\f$R_{\mathrm{boundary}} = \mathrm{radius} + \sum_n \bigl[ \mathtt{fourier\_cos}(n)\cos(n\theta) + \mathtt{fourier\_sin}(n)\sin(n\theta) \bigr]\f$.
1110+
Coefficients are absolute: same units as `radius` (length).
1111+
If this formula gives \f$R_{\mathrm{boundary}} < 0\f$ at some \f$\theta\f$, it is clipped to zero.
1112+
With `modal_clip_r_to_min` true, if \f$R_{\mathrm{boundary}} <\f$ `modal_r_min` at some \f$\theta\f$, it is clipped to `modal_r_min`.
1113+
1114+
- **Exponential form** (`modal_use_exp_form` true):
1115+
\f$R_{\mathrm{boundary}} = \mathrm{radius} \times \exp\bigl( \sum_n [\ldots] \bigr)\f$.
1116+
Coefficients are relative (dimensionless); the sum scales the radius.
1117+
11051118
### Immersed Boundary Patch Types {#immersed-boundary-patch-types}
11061119

11071120
| # | Name | Dim. |
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env python3
2+
"""Minimal 2D acoustic case with geometry 13 (2D modal Fourier shape). Additive form."""
3+
4+
import json
5+
import math
6+
7+
Nx, Ny = 64, 64
8+
Lx, Ly = 8.0, 8.0
9+
dx = Lx / (Nx + 1)
10+
p_inf = 101325.0
11+
rho_inf = 1.0
12+
gam = 1.4
13+
# Patch 2: same pressure, higher sound speed (different gas) -> lower density
14+
c_ratio = 1.5
15+
rho2 = rho_inf / (c_ratio * c_ratio)
16+
c = math.sqrt(gam * p_inf / rho_inf)
17+
cfl = 0.3
18+
mydt = cfl * dx / c
19+
Tfinal = 10.0 / c
20+
Nt = int(Tfinal / mydt)
21+
22+
# Acoustic source (similar to 2D_acoustic_support5)
23+
t_cross = Lx / c # domain crossing time
24+
gauss_sigma_time = 0.03 * t_cross
25+
acoustic_delay = 0.15 * t_cross
26+
acoustic_loc_x = -3.5
27+
acoustic_loc_y = 0.0
28+
acoustic_aperture = 6.0
29+
acoustic_foc_length = 3.5
30+
31+
config = {
32+
"run_time_info": "T",
33+
"x_domain%beg": -4.0,
34+
"x_domain%end": 4.0,
35+
"y_domain%beg": -4.0,
36+
"y_domain%end": 4.0,
37+
"m": Nx,
38+
"n": Ny,
39+
"p": 0,
40+
"dt": mydt,
41+
"t_step_start": 0,
42+
"t_step_stop": Nt,
43+
"t_step_save": max(1, int(Nt / 10)),
44+
"num_patches": 2,
45+
"model_eqns": 2,
46+
"alt_soundspeed": "F",
47+
"num_fluids": 2,
48+
"mpp_lim": "F",
49+
"mixture_err": "F",
50+
"time_stepper": 3,
51+
"weno_order": 5,
52+
"weno_eps": 1.0e-16,
53+
"mapped_weno": "T",
54+
"null_weights": "F",
55+
"mp_weno": "F",
56+
"riemann_solver": 2,
57+
"wave_speeds": 1,
58+
"avg_state": 2,
59+
"bc_x%beg": -8,
60+
"bc_x%end": -8,
61+
"bc_y%beg": -8,
62+
"bc_y%end": -8,
63+
"format": 1,
64+
"precision": 2,
65+
"prim_vars_wrt": "T",
66+
"parallel_io": "T",
67+
"omega_wrt(3)": "T",
68+
"fd_order": 2,
69+
"patch_icpp(1)%geometry": 3,
70+
"patch_icpp(1)%x_centroid": 0.0,
71+
"patch_icpp(1)%y_centroid": 0.0,
72+
"patch_icpp(1)%length_x": 8.0,
73+
"patch_icpp(1)%length_y": 8.0,
74+
"patch_icpp(1)%vel(1)": 0.0,
75+
"patch_icpp(1)%vel(2)": 0.0,
76+
"patch_icpp(1)%pres": p_inf,
77+
"patch_icpp(1)%alpha_rho(1)": rho_inf,
78+
"patch_icpp(1)%alpha_rho(2)": 0.0,
79+
"patch_icpp(1)%alpha(1)": 1.0,
80+
"patch_icpp(1)%alpha(2)": 0.0,
81+
"patch_icpp(2)%geometry": 13,
82+
"patch_icpp(2)%x_centroid": 0.0,
83+
"patch_icpp(2)%y_centroid": 0.0,
84+
"patch_icpp(2)%radius": 1.0,
85+
"patch_icpp(2)%fourier_cos(1)": 0.2,
86+
"patch_icpp(2)%fourier_sin(1)": 0.1,
87+
"patch_icpp(2)%fourier_sin(3)": -0.3,
88+
"patch_icpp(2)%fourier_cos(5)": 0.5,
89+
"patch_icpp(2)%modal_clip_r_to_min": "T",
90+
"patch_icpp(2)%modal_r_min": 0.5,
91+
"patch_icpp(2)%vel(1)": 0.0,
92+
"patch_icpp(2)%vel(2)": 0.0,
93+
"patch_icpp(2)%pres": p_inf,
94+
"patch_icpp(2)%alpha_rho(1)": 0.0,
95+
"patch_icpp(2)%alpha_rho(2)": rho2,
96+
"patch_icpp(2)%alpha(1)": 0.0,
97+
"patch_icpp(2)%alpha(2)": 1.0,
98+
"patch_icpp(2)%alter_patch(1)": "T",
99+
"bc_x%grcbc_in": "F",
100+
"bc_x%grcbc_out": "T",
101+
"bc_x%grcbc_vel_out": "F",
102+
"bc_x%pres_out": p_inf,
103+
"bc_y%grcbc_in": "F",
104+
"bc_y%grcbc_out": "T",
105+
"bc_y%grcbc_vel_out": "F",
106+
"bc_y%pres_out": p_inf,
107+
"fluid_pp(1)%gamma": 1.0 / (gam - 1.0),
108+
"fluid_pp(1)%pi_inf": 0.0,
109+
"fluid_pp(2)%gamma": 1.0 / (gam - 1.0),
110+
"fluid_pp(2)%pi_inf": 0.0,
111+
# Acoustic source (similar to 2D_acoustic_support5)
112+
"acoustic_source": "T",
113+
"num_source": 1,
114+
"acoustic(1)%support": 5,
115+
"acoustic(1)%loc(1)": acoustic_loc_x,
116+
"acoustic(1)%loc(2)": acoustic_loc_y,
117+
"acoustic(1)%pulse": 2,
118+
"acoustic(1)%npulse": 1,
119+
"acoustic(1)%mag": 1.0,
120+
"acoustic(1)%gauss_sigma_time": gauss_sigma_time,
121+
"acoustic(1)%foc_length": acoustic_foc_length,
122+
"acoustic(1)%aperture": acoustic_aperture,
123+
"acoustic(1)%delay": acoustic_delay,
124+
}
125+
print(json.dumps(config))

0 commit comments

Comments
 (0)