Output with the area mean of the acceleration removed; experiment harness
5 changed files+46−0
solutions/cymbal2-crash-v1/hermite-modal/modal-g8-f20k-m4.h5added+3−0View file
@@ -0,0 +1,3 @@
1+version https://git-lfs.github.com/spec/v1
2+oid sha256:787ad18190912fa0377ba369b8cca312e22c4941b4d27d0aaad1c73a4f5772fa
3+size 39392238
solutions/cymbal2-crash-v1/hermite-modal/modal-g8-f20k-m4.wavadded+0−0View file
Binary file not shown.
solvers/hermite-fem/experiment.pyadded+40−0View file
@@ -0,0 +1,40 @@
1+"""Run the modal solver on a variant of a challenge for listening experiments (not submissions): overrides of the
2+parameters, the strikes and the duration are applied to a copy of challenge.json written beside the outputs.
3+Usage: python experiment.py <challenge.json> <experiments-dir> <name> [--set key=value ...] [--strikes t:x,t:x,...]
4+ [--seconds S] [--spacing 0.008] [--fmax 20000] [--m 4] [--device cuda]
5+Example: python experiment.py crash.json ../../experiments thickbell --set thickness_center=0.002 --strikes 0:0.04,1.2:0.12,2.4:0.185 --seconds 3.6"""
6+import json
7+import subprocess
8+import sys
9+from pathlib import Path
10+
11+ch = json.load(open(sys.argv[1]))
12+exp_dir = Path(sys.argv[2])
13+name = sys.argv[3]
14+args = sys.argv[4:]
15+passthrough = []
16+i = 0
17+while i < len(args):
18+ if args[i] == "--set":
19+ k, v = args[i + 1].split("=", 1)
20+ ch["parameters"][k] = json.loads(v)
21+ i += 2
22+ elif args[i] == "--strikes":
23+ ch["strikes"] = [{"t": float(t), "x": float(x), "y": 0.0} for t, x in (s.split(":") for s in args[i + 1].split(","))]
24+ i += 2
25+ elif args[i] == "--seconds":
26+ ch["output"]["num_samples"] = int(round(ch["output"]["sample_rate_hz"] * float(args[i + 1])))
27+ ch["fields"]["shape"][0] = int(round(ch["fields"]["frame_rate_hz"] * float(args[i + 1]))) + 1
28+ i += 2
29+ else:
30+ passthrough.append(args[i])
31+ i += 1
32+ch["challenge_id"] = f"{ch['challenge_id']}-{name}"
33+out = exp_dir / name
34+out.mkdir(parents=True, exist_ok=True)
35+cj = out / "challenge.json"
36+json.dump(ch, open(cj, "w"), indent=1)
37+here = Path(__file__).resolve().parent
38+cmd = [sys.executable, str(here / "modal_shell.py"), str(cj), str(out), "--tag", name, *passthrough]
39+print(" ".join(cmd), flush=True)
40+sys.exit(subprocess.call(cmd))
solvers/hermite-fem/fem_shell.pymodified+2−0View file
@@ -429,6 +429,7 @@ class ShellFEM:
429429 # ---- Rayleigh integral bookkeeping
430430 dsteps = T64(self.ray_tau / dt).view(-1) # (C*G,) delay in steps
431431 rc = T64(self.ray_c).view(-1)
432+ wmean = T64(np.tile(self.wq, C) / self.area) # (C*G,) quadrature weights over the area: the area mean of a Gauss-point field
432433 out = torch.zeros(nsamp, dtype=torch.float64, device=dev)
433434 # ---- strikes
434435 nst = len(self.strikes)
@@ -500,6 +501,7 @@ class ShellFEM:
500501 q.add_(dt * v)
501502 # Rayleigh integral: scatter the Gauss-point accelerations onto the retarded output samples
502503 ag = (acc[cell_dofs_w] @ NwT).reshape(-1) # (C*G,)
504+ ag = ag - torch.dot(wmean, ag) # the output uses the acceleration with its area mean removed (MODEL.md)
503505 idx, val = ray_c(n, ag)
504506 out.index_add_(0, idx, val)
505507 if out_direct is not None: # the same integral without retardation, for checking the scatter above
solvers/hermite-fem/modal_shell.pymodified+1−0View file
@@ -167,6 +167,7 @@ class ModalShell:
167167 L = dmax + 1
168168 Nw = T64(fem.Nw) # (G, 16)
169169 Psi = torch.einsum("gi,cin->cgn", Nw, Phi[ops["cell_dofs_w"]]).reshape(-1, N) # (C*G, N) w of the modes at the Gauss points
170+ Psi = Psi - (T64(np.tile(fem.wq, fem.n_cells) / fem.area) @ Psi)[None, :] # remove each mode's area mean: the output of MODEL.md
170171 Gt = torch.zeros((L, N), dtype=torch.float64, device=dev)
171172 for j, off in enumerate([-1, 0, 1, 2]):
172173 Gt.index_add_(0, T64i(d0 + off), T64(cq * lag[:, j])[:, None] * Psi)