You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RFC: a hybrid geometric-acoustics solver (kind: "hybrid-ga"). Not a fifth tracer.
CRAM already has image source, Monte Carlo rays, beam trace (+ UTD), and ART. The missing product is what ODEON/CATT ship: deterministic early + stochastic middle + diffuse late, with one energy unit and a defined crossover. Today that is a splice inside the raytracer (#129) plus three other solvers the user must run by hand.
3D FDTD (#147) covers low-frequency wave physics. This solver covers high-frequency geometric physics. They share Surface / Source / Receiver; they do not share a grid.
Decision: orchestrate, don’t clone
Option
Verdict
Cone / frustum / pyramid tracing
No. Beam trace already does volumetric specular.
Phonon / sound-particle
No. That’s the raytracer with a different name.
Deterministic radiosity matrix
Later. ART is the MC version; a form-factor matrix is an ART upgrade, not a new solver.
Fix raytracer hybrid in place
Necessary (#129) but not a product. The flag still lives inside one MC solver, still first-receiver-only (#131), still peak-normalizes (#133).
Hybrid-GA: IS (+ UTD) early, MC rays optional middle, diffusion-equation late
v1. One tab, one IR, one LTP.
New tree src/compute/hybrid-ga/. It calls image-source / shared diffraction / ray energy histogram / a new ADE module. It does not copy arrivalPressure.
Three regimes
t = 0 ──────── t_early ──────── t_mix ──────── t_end
[ IS + UTD ] [ optional MC ] [ ADE tail ]
deterministic stochastic diffuse field
Early (order ≤ N, default N = 2). Image-source paths, 1/r² spreading (#122 / #99), energy R² from reflectionFunction, air on energy (10^(−α r / 10)), noMath.random sign (#124). Direct sound once. UTD from shared/diffraction with inboundarrivalDirection (#134). Skip IS across the same face twice; isInFrontOf must actually run (#127).
Middle (optional). Existing raytracer, but:
Drop paths with chainLength−1 ≤ N (filter, do not splicethis.paths).
Detection along the segment vs a sphere about the receiver center, t = dist_center / c. Receiver mesh out of the BVH.
Same initialSPL / spreading convention as IS so a first-order IS arrival and a lucky first-order ray would match — they must not both be deposited.
Late. Acoustic diffusion equation (Picaut / Valeau / Billon), not peak-normalized noise and not W-only HOA (#106 / #132).
∂w/∂t = D ∇²w − σ c w
D = λ c / 3, λ = 4V / S, σ = −ln(1 − ᾱ) / λ (or ᾱ / λ)
w is energy density (J/m³) per octave band. Voxelize the room coarse (cell ~ 0.5–1 m, not FDTD Δx). Neumann at walls with loss σ; coupling through apertures = shared faces. CPU is enough (10³–10⁴ cells). Source for ADE is the residual energy at t_mix from the histogram, not a new 500-unit blast (ART #116).
Crossfade: energy envelopes, 20–50 ms. Pressure IR for the tail = band-filtered noise × √(w(t) at receiver cell), independent per HOA channel if ambisonic.
Energy units (non-negotiable)
One convention for the whole solver:
Source: Lp @ 1 m → intensity I(1 m), then geometric spreading 1/r² on deterministic paths; MC uses hit rate × 1/N.
Reflection: reflectionFunction is already energyR². Multiply intensity by it. Do not abs for the energy path; IR pressure uses sign(R_pressure)·√energy or keep random sign only in the ADE tail.
Export WAV unnormalized. Display copy may peak-normalize. Two receivers comparable in SPL.
If IS, rays, and ADE disagree on a 10 m cube, α = 0.2, the solver is wrong — don’t ship the tab.
Module layout
src/compute/hybrid-ga/
index.ts # HybridGASolver extends Solver
early.ts # wrap ImageSourceSolver + UTD, apply spreading
middle.ts # run/read RayTracer paths with order filter + 1/N
diffusion/
ade.ts # CPU ADE, per-band w[cell]
voxelize-coarse.ts # occupancy at ~1 m (can share ideas with #147, not the grid)
assemble.ts # IR / LTP / optional FOA
__tests__/
spreading.spec.ts # 1 m vs 2 m vs 4 m direct
crossover.spec.ts # no double-count order ≤ N
ade-decay.spec.ts # cube T ≈ Eyring
Registry: registerSolverFactory("hybrid-ga", ...). Tab: N, t_mix (auto = mean free time 4V/(S c) or manual), enable MC middle, enable ADE, bands, assigned S/R/one room.
save() persists those IDs. No rooms[0], no findIDs() of the whole project (#123 / #135 / #143).
UI / results
One LTP chart with series: IS, UTD, MC (if on), ADE (if on), total. One IR play/download. Ambisonic: encode IS/UTD/MC by arrival direction; ADE tail all HOA channels, equal power, uncorrelated (#132).
Energy Decay (#139) should be able to take this IR from the store.
Validation
Free-field / empty-ish: direct SPL −6 dB per doubling of distance (air off).
Hard shoebox, N=1, MC off, ADE off: arrivals match analytic image sources in time and in 6 dB law; count = 1 + 6 first-order.
No double count: N=2, MC on → no stochastic path with order ≤ 2 in the IR; this.paths of the inner raytracer unchanged.
ADE cube: uniform α, T60 from w(t) at center within ~15 % of Eyring (air off).
Replacing ART (keep it; hybrid-GA does not shoot surface-to-surface form factors)
Random polarity on specular paths
Relationship to open bugs
Hybrid-GA consumes fixes, it does not wait for every geometric issue to close. Phase 0 should implement spreading in a sharedarrivalIntensity(path, r) used by IS and beam-trace (#99 / #122). Phase 2 must not copy raytracer splice. ADE is new code.
Acceptance (phase 3)
kind: "hybrid-ga" in the registry
Direct 1 m / 2 m / 4 m: −6 dB / −12 dB
Hard box N=1: 7 arrivals, no extras, no missing
MC on: stored ray paths unmodified; IR has each early path once
ADE T60 on a uniform cube ≈ Eyring
Exported WAV is not peak-normalized; two receivers differ
RFC: a hybrid geometric-acoustics solver (
kind: "hybrid-ga"). Not a fifth tracer.CRAM already has image source, Monte Carlo rays, beam trace (+ UTD), and ART. The missing product is what ODEON/CATT ship: deterministic early + stochastic middle + diffuse late, with one energy unit and a defined crossover. Today that is a
spliceinside the raytracer (#129) plus three other solvers the user must run by hand.3D FDTD (#147) covers low-frequency wave physics. This solver covers high-frequency geometric physics. They share
Surface/Source/Receiver; they do not share a grid.Decision: orchestrate, don’t clone
hybridin placeNew tree
src/compute/hybrid-ga/. It calls image-source / shared diffraction / ray energy histogram / a new ADE module. It does not copyarrivalPressure.Three regimes
Early (order ≤ N, default N = 2). Image-source paths, 1/r² spreading (#122 / #99), energy
R²fromreflectionFunction, air on energy (10^(−α r / 10)), noMath.randomsign (#124). Direct sound once. UTD fromshared/diffractionwith inboundarrivalDirection(#134). Skip IS across the same face twice;isInFrontOfmust actually run (#127).Middle (optional). Existing raytracer, but:
chainLength−1 ≤ N(filter, do not splicethis.paths).1/N_raysof source energy (Raytracer IR: no 1/N, peak-normalize, 10 cm mesh detector — N-dependent shape #133).t = dist_center / c. Receiver mesh out of the BVH.initialSPL/ spreading convention as IS so a first-order IS arrival and a lucky first-order ray would match — they must not both be deposited.Late. Acoustic diffusion equation (Picaut / Valeau / Billon), not peak-normalized noise and not W-only HOA (#106 / #132).
wis energy density (J/m³) per octave band. Voxelize the room coarse (cell ~ 0.5–1 m, not FDTDΔx). Neumann at walls with lossσ; coupling through apertures = shared faces. CPU is enough (10³–10⁴ cells). Source for ADE is the residual energy att_mixfrom the histogram, not a new 500-unit blast (ART #116).Crossfade: energy envelopes, 20–50 ms. Pressure IR for the tail = band-filtered noise × √(w(t) at receiver cell), independent per HOA channel if ambisonic.
Energy units (non-negotiable)
One convention for the whole solver:
Lp @ 1 m→ intensityI(1 m), then geometric spreading1/r²on deterministic paths; MC uses hit rate ×1/N.reflectionFunctionis already energyR². Multiply intensity by it. Do notabsfor the energy path; IR pressure usessign(R_pressure)·√energyor keep random sign only in the ADE tail.m = α_dB · ln(10)/10(RT60 Sabine/Eyring convert air dB/m with 20 log — 4mV is 2× too small #141 / ART applies pressure air absorption (20 log) to energy radiosity #118). Never 20 on energy.If IS, rays, and ADE disagree on a 10 m cube, α = 0.2, the solver is wrong — don’t ship the tab.
Module layout
Registry:
registerSolverFactory("hybrid-ga", ...). Tab: N,t_mix(auto = mean free time4V/(S c)or manual), enable MC middle, enable ADE, bands, assigned S/R/one room.save()persists those IDs. Norooms[0], nofindIDs()of the whole project (#123 / #135 / #143).UI / results
One LTP chart with series: IS, UTD, MC (if on), ADE (if on), total. One IR play/download. Ambisonic: encode IS/UTD/MC by arrival direction; ADE tail all HOA channels, equal power, uncorrelated (#132).
Energy Decay (#139) should be able to take this IR from the store.
Validation
this.pathsof the inner raytracer unchanged.T60fromw(t)at center within ~15 % of Eyring (air off).Physics tests, not greps.
Phases
Phase 0 is already more correct than today’s image-source tab. Phase 3 is the product.
Explicit non-goals (v1)
Relationship to open bugs
Hybrid-GA consumes fixes, it does not wait for every geometric issue to close. Phase 0 should implement spreading in a shared
arrivalIntensity(path, r)used by IS and beam-trace (#99 / #122). Phase 2 must not copy raytracersplice. ADE is new code.Acceptance (phase 3)
kind: "hybrid-ga"in the registry