1c822d5Add modal-series and fd-second-order submissions for string-pluck-v1, with results workflowJeremy Magland 1"""Shared helpers for the example solvers: load the challenge, write wavs, write submission JSON."""
2from __future__ import annotations
4import json
5import platform
6from pathlib import Path
8import numpy as np
9from scipy.io import wavfile
12def load_challenge(path: Path) -> dict:
13 with open(path) as f:
14 return json.load(f)
17def write_wav(path: Path, samples_si: np.ndarray, challenge: dict) -> None:
18 """Write pointwise samples of the SI signal, divided by signal_scale, as float32."""
19 out = challenge["output"]
20 scaled = np.asarray(samples_si, dtype=np.float64) / float(out["signal_scale"])
21 path.parent.mkdir(parents=True, exist_ok=True)
22 wavfile.write(path, int(out["sample_rate_hz"]), scaled.astype(np.float32))
25def hardware_string() -> str:
26 cpu = platform.processor() or platform.machine()
27 try:
28 with open("/proc/cpuinfo") as f:
29 for line in f:
30 if line.startswith("model name"):
31 cpu = line.split(":", 1)[1].strip()
32 break
33 except OSError:
34 pass
35 return f"{cpu}, single thread, Python {platform.python_version()} + numpy {np.__version__}"
0e42022Regenerate for the equilibrium pluck initial condition (finger load of half-width 6 mm, scale 2.4 m/s)Jeremy Magland 38def pluck_load_q(p: dict) -> float:
39 """Total load Q (a slope, force over tension) giving pluck height h at x_p."""
40 return p["h"] / (p["x_p"] * (p["L"] - p["x_p"]) / p["L"] - p["w"] * (0.25 - 1.0 / np.pi**2))
1c822d5Add modal-series and fd-second-order submissions for string-pluck-v1, with results workflowJeremy Magland 43def initial_shape(x: np.ndarray, p: dict) -> np.ndarray:
0e42022Regenerate for the equilibrium pluck initial condition (finger load of half-width 6 mm, scale 2.4 m/s)Jeremy Magland 44 """u_0(x): static equilibrium under the raised-cosine finger load, from the challenge spec."""
45 L, xp, w = p["L"], p["x_p"], p["w"]
46 Q = pluck_load_q(p)
47 tri = np.where(x <= xp, x * (L - xp) / L, xp * (L - x) / L)
48 y = x - xp
49 cap = np.zeros_like(x)
50 m = np.abs(y) <= w
51 ym = y[m]
52 cap[m] = ym * (ym > 0) - (1.0 / (2.0 * w)) * ((ym + w) ** 2 / 2.0 - (w**2 / np.pi**2) * (1.0 + np.cos(np.pi * ym / w)))
53 return Q * (tri + cap)