feat: PML, facet-bilinear & port boundary operators (+ examples) - #39
Merged
Shizheng-Wen merged 11 commits intoJul 22, 2026
Conversation
NicolasCayuela
force-pushed
the
wave-pml-port-api
branch
from
July 4, 2026 00:50
d0afc48 to
4101917
Compare
… BC) Add the boundary and open-domain building blocks scalar-Helmholtz wave problems (acoustics, 2D optics) need but the library lacked. Each piece is validated against an analytic or known-good reference. - AnisotropicLaplaceElementAssembler: tensor-coefficient stiffness int (A grad u).grad v with complex A (A = I reproduces Laplace). - ScaledMassElementAssembler: coefficient mass int c N_i N_j. - FacetBilinearAssembler: boundary bilinear form int_Gamma c N_i N_j dS scattered to a SparseMatrix (Robin / impedance / port matrices). - pml.cartesian_pml: stretched-coordinate PML tensor + mass scaling; matches the 2D Hankel Green function to corr 0.994. - bc.robin_operator / bc.port_source: first-order absorbing / plane-wave -port boundary matrix and load (traveling-wave corr 0.98). Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
A 2D acoustic Helmholtz resonator (duct + neck + side cavity) driven by a plane-wave port, solved on TensorMesh with the new boundary operators. - port line mass and incident load via robin_operator / port_source, folded onto the volume layout for a single complex SparseMatrix - input impedance Z(f) over a frequency sweep, plus the pressure field at the 357 Hz resonance (cavity gain ~12x) - boundary-condition schematic figure Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
A 2D TM (E_z) silicon bus waveguide evanescently coupled to a silicon microdisk in silica, with a stretched-coordinate PML frame, on TensorMesh. - inline anisotropic-Helmholtz + PML assembler evaluating eps and the stretch per quadrature point for sharp Si/SiO2 interfaces; the native pml.cartesian_pml path is documented as the reusable equivalent - driven at the disk whispering-gallery resonance -> clean rim mode - Re(E_z) and |E_z| field figure Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
The Brillouin-zone paths and gradient dot-products used the LaTeX
negative-thin-space "\!", which GitHub's math renderer prints literally
as stray "!" ("M! - !\Gamma! - !X! - !M"). Drop it; write the k-paths
with arrows and switch "\rm" to "\mathrm".
Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
Document the two new wave examples in the style of the phononic-crystal README (model, workflow block, run, what the figure shows): - helmholtz_resonator: port-driven acoustic resonator; the port operators come from robin_operator / port_source. - optical_ring_resonator: waveguide-coupled microdisk with a PML frame; notes the cartesian_pml library path and the P1 WGM-resonance shift. Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
GitHub's KaTeX renderer rejects \operatorname ("macros are not allowed").
Write the PML stretch tensor with \mathrm{diag} instead.
Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
13 tests covering the new API against exact references: - AnisotropicLaplace: A=I equals Laplace, complex-A scaling, symmetry - ScaledMass: c=1 equals Mass, constant-c scaling - FacetBilinear: boundary mass = perimeter / edge length / coefficient - cartesian_pml: interior stretch = I, PML diag = (sy/sx, sx/sy), s_prod - robin_operator / port_source: boundary mass and load = perimeter, complex Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
Add a `plot_setup` function to the waveguide-microdisk coupler example that draws the physical model before the field solve, so a reader can see the problem definition at a glance. - render materials (Si core waveguide + microdisk, SiO2 cladding) with a Wong colorblind-safe palette - draw the PML frame as a grey border with a legend entry - mark the line-current launch on the bus waveguide - wire the figure into `run_demo` (saved before the solve) and add a `--setup-output` CLI flag Example-only; no public API is added. Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
Two standalone modal-analysis examples exercising the generalized eigenproblem on assembler-built sparse matrices, each validated against a closed-form reference (no external tools required). - examples/solid/beam_vibration_analysis: free-vibration eigenmodes of a clamped-free steel cylinder. LinearElasticity stiffness + consistent vector mass (scalar mass lifted to the 3 components), scipy eigsh, 3D deformed mode-shape gallery. Sanity-checked vs the Euler-Bernoulli cantilever bending frequencies. - examples/wave/wave_guide_mode: scalar mode analysis of a rectangular dielectric waveguide cross-section. Laplace + Mass + ScaledMass build the transverse Helmholtz eigenproblem (K - k0^2 M_eps) E = -beta^2 M E; guided E_pq modes plotted as field maps. Compared to the separable Marcatili slab approximation. Both are example-only; no public API is added. Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
Replace the isotropic line-current drive of the waveguide-microdisk coupler with a modal soft source, so the bus mode is launched cleanly toward the disk rather than radiating both ways from a current blob. - add slab_mode(): analytic fundamental even E_z slab mode, effective index from the even-mode dispersion kx*tan(kx*w/2) = gamma - add launch_field(): imprint the transverse profile on a thin plane at the PML/cladding interface, phased as a two-plane directional launch (-i, a quarter guided-wavelength ahead) so power travels +y - drop the source-disk geometry and src_r; the launch plane is src_y (= pml), matching an inlet at the absorbing frame - update plot_setup (launch line + arrow), docstrings, README Coupling into the whispering-gallery mode improves markedly (mean |E_z| disk/whole ~3.2); example run verified end to end. Signed-off-by: NicolasCayuela <241774098+NicolasCayuela@users.noreply.github.com>
…mespaces Maintainer touch-up on top of the rebase: tensormesh/bc.py becomes tensormesh/operator/boundary.py (robin_operator / port_source live with the other boundary operators - Condenser, BlochReducer) and tensormesh/pml.py becomes tensormesh/functional/pml.py (a coefficient- field builder, like the rest of tensormesh.functional). Top-level re-exports are unchanged: tensormesh.robin_operator / port_source / cartesian_pml keep working exactly as in the original PR. Also derive robin_operator's scalar-coefficient dtype from the mesh points (float64 mesh -> complex128/float64 coefficient) instead of torch's float32 default. Signed-off-by: Shizheng-Wen <wenshizheng0710@gmail.com>
Shizheng-Wen
force-pushed
the
wave-pml-port-api
branch
from
July 22, 2026 15:14
14fe7c9 to
ae1ea4b
Compare
Collaborator
|
Thanks Nicolas — this is exactly the missing piece for open-domain wave problems, and the validation table is a pleasure to review. Landing it ahead of the next release. Maintainer housekeeping I pushed to your branch (hope you don't mind):
Docs (API entries + gallery pages for the four examples) will follow in a separate PR on our side. Follow-ups we'd welcome anytime: the cross-layout |
Shizheng-Wen
added a commit
that referenced
this pull request
Jul 22, 2026
#48) Documentation for PR #39's API surface and examples: - New gallery chapter open_domain_wave.rst - the port-driven Helmholtz resonator (357 Hz fundamental, 12x cavity gain) and the PML-framed optical microdisk (whispering-gallery mode), with freshly generated figures and the four-line robin_operator / port_source recipe. - New gallery chapter modal_analysis.rst - rectangular-waveguide modes (Marcatili agreement ~1e-4) and cantilever-cylinder vibration (Euler-Bernoulli +3%), both generalized-eigenproblem workflows. - API reference: FacetBilinearAssembler + AnisotropicLaplace / ScaledMass builtins (assemble), robin_operator / port_source (operator), functional.pml section. - forms builtin table + boundary_conditions: Robin/port conditions are now first-class, the 'not built in' bullet retires. - README example/architecture rows; ROADMAP item 3 marks the PML half shipped (metamaterial TopOpt remains, with an autograd-through- boundary-ops prerequisite noted); example READMEs' source links follow the operator/functional namespace move; a handful of docstring cross-reference fixes surfaced by the API build. - zh_CN catalogs synced: 67 new entries translated and reviewed. Both builds clean; ZH adds zero warnings over the EN baseline. Signed-off-by: Shizheng-Wen <wenshizheng0710@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: open-domain wave FEM — PML, facet-bilinear & port boundary operators (+ examples)
Summary
Frequency-domain wave problems (acoustics, 2D optics) need two things the
library didn't have yet: a PML, and a way to
assemble boundary bilinear forms (Robin / impedance / plane-wave ports).
This PR adds both as small, composable pieces on top of the existing
complex-valued assembly, plus two worked examples.
Everything is scalar-Helmholtz, reuses
ElementAssembler/SparseMatrix, andadds no heavy dependencies.
What's added
Assemblers (
tensormesh.assemble.builtin,…facet_bilinear)AnisotropicLaplaceElementAssembler— tensor-coefficient stiffnessAsupported.ScaledMassElementAssembler— coefficient massFacetBilinearAssembler— boundary bilinear formSparseMatrix(the bilinear counterpart of
FacetAssembler, which only does linear forms).Helpers (
tensormesh.pml,tensormesh.bc)cartesian_pml— stretched-coordinate PML tensorscaling
robin_operator/port_source— first-order absorbing / plane-wave-portboundary matrix and incident load.
Examples using the new operators (
examples/wave/)helmholtz_resonator/— port-driven 2D acoustic resonator (uses the portoperators).
optical_ring_resonator/— waveguide-coupled Si microdisk with a PML frame(2D TM, whispering-gallery mode); now ships a setup figure showing the
materials, the line-current launch and the PML boundary.
Additional modal-analysis examples
These reuse the existing builtin assemblers (Laplace / Mass / ScaledMass /
LinearElasticity) with a SciPy generalized-eigenvalue solve — they do not use
the new PML / port operators, but round out the wave / vibration example set and
each validates against a closed-form reference.
examples/solid/beam_vibration_analysis/— free-vibration eigenmodes of aclamped-free steel cylinder (3D elasticity, consistent vector mass); 3D
deformed mode-shape gallery, checked vs the Euler-Bernoulli bending
frequencies.
examples/wave/wave_guide_mode/— scalar mode analysis of a rectangulardielectric waveguide,
Laplace + Mass +
ScaledMassElementAssembler; guidedchecked vs the separable Marcatili slab approximation.
Validation
Each piece is checked against an analytic or known-good reference:
AnisotropicLaplace(A = I)LaplaceElementAssembler0.0FacetBilinearboundary masscartesian_pmlrobin_operator/port_sourceUnit tests live in
tests/assemble/test_wave.py(13 tests, all passing) andassert the assembly-level identities above:
A = I⇒ Laplace,c = 1⇒ Mass,boundary mass = perimeter / edge length, the PML stretch tensor, and the Robin /
port boundary mass and load (real and complex coefficients).
Helmholtz resonator
Port-driven duct with a necked side cavity; input impedance over a sweep and the
pressure field at resonance.
Optical ring resonator (waveguide-coupled microdisk)
On resonance the disk lights up in a whispering-gallery mode (bright rim, the
waveguide depleted); the PML frame absorbs the radiated field.
Beam vibration (cantilever cylinder)
Free-vibration mode shapes: bending pairs (circular section ⇒ two planes), first
torsion and first axial mode.
Rectangular waveguide modes
The guided$E_{pq}$ transverse fields of a rectangular dielectric core.
Notes
robin_operatorhas a boundary-only sparsity pattern,so it's folded onto the volume layout before adding (
SparseMatrix.__add__needs matching layouts). A native cross-layout add would remove the fold — happy
to do it as a follow-up.
the PML is the higher-quality absorber for open domains.
examples/**/*.png); they regenerate byrunning each script.
cartesian_pml+AnisotropicLaplace(currently an inline per-quadrature assembler for sharperinterfaces); 3D PML.
Checklist
Signed-off-bynumpyinforward(); complex-safetests/—tests/assemble/test_wave.py, 13 tests, all passing