Repository navigation
feat(lpse2d): spatially windowed electron distribution for HPE (stacked on #378) #387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
physicistphil
wants to merge
209
commits into
ergodicio:main
Choose a base branch
from
physicistphil:feat/lpse2d-hpe-spatial-fv
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
209 commits
Select commit
Hold shift + click to select a range
58b5038
lpse2d: LPSE term parity -- TPD coefficient, thermal noise, Landau fo…
physicistphil 87cd442
lpse2d: per-operation EPW energy ledger (terms.epw.energy_ledger)
physicistphil 32f8c44
lpse2d: spectral light propagator and collisional light absorption
physicistphil 77a545b
lpse2d: combined TPD+SRS solver (terms.epw.solver: combined)
physicistphil 91b8de1
lpse2d: LPSE deck translator, metrics/field readers, and LPSE-style d…
physicistphil 9ed9083
lpse2d: make the SRS source k-filter optional (LPSE lw.kFilter, off b…
physicistphil ffaf4b9
lpse2d: transverse projection of the SRS light sources and band-limit…
physicistphil 744eddb
lpse2d: oblique incidence of the pump (drivers.E0.angle)
physicistphil a829cce
lpse2d: the original LPSE density profiles (density.basis: lpse-<shape>)
physicistphil 9b78d9b
lpse2d: LPSE HPE controls and instruments (terms.hpe.*)
physicistphil 2c6db21
lpse2d: absolute-threshold bisection driver (adept._lpse2d.threshold)
physicistphil 704782c
lpse2d: separate absorber strength for the light fields (terms.light.…
physicistphil 2cba188
lpse2d: checkpoint / restart (save.checkpoint, restart.file) and LPSE…
physicistphil a498561
lpse2d: LPSE diagnostics (Poynting fields, Thomson probes) and therma…
physicistphil c94d7b4
lpse2d: pump beamlets, transverse profile, Kubo-Anderson bandwidth, p…
physicistphil 5c72f57
lpse2d: translator defaults grid.antiAliasing.range to LPSE's none wh…
physicistphil 01805c3
lpse2d: the combined solver damps the pump with the light absorber (n…
physicistphil e39a9fb
lpse2d: translator reads LPSE's hpe.metrics.fluxMetric.N / powerMetri…
physicistphil e3b5f8d
lpse2d: wrap the HPE power-metric prefix line; translator test uses L…
physicistphil 4c70767
lpse2d: LPSE particle-wall semantics for HPE; flux bins in keV; VminO…
physicistphil 3c42990
lpse2d: spectral light solver sub-cycles to one cell per sub-step for…
physicistphil abfd8ea
feat(lpse2d): parity harness package and reference-run resolver (plan…
physicistphil e47a671
fix(lpse2d): read_frames returns LPSE 2-D frames in their C-order (Nx…
physicistphil c592111
test(lpse2d): reverse-mode gradient tests through every solver path (…
physicistphil 8b83a50
feat(lpse2d): noise_calibrate: lpse ports LPSE's thermal-noise consta…
physicistphil f8fa145
fix(lpse2d): keep the FD light fields transverse; ledger of the proje…
physicistphil 0988524
feat(lpse2d): HPE energy-conservation multiplier ported from LPSE (pl…
physicistphil dbd74b5
feat(lpse2d): three-component light fields, E_z purely transverse on …
physicistphil bcd4b1a
feat(lpse2d): beam polarization angle for the pump, the beams and the…
physicistphil 27c4446
feat(lpse2d): in-plane magnetic field and p_z in the 2-D HPE push (pl…
physicistphil 6d6b503
feat(lpse2d): initial_perturbation -- LPSE's plane-wave initial condi…
physicistphil 9b4cbf9
feat(lpse2d): source windows, absorber/injector source suppression, I…
physicistphil 481cb44
feat(lpse2d): parity batch driver, run tags, and the plan-2 job matrices
physicistphil 7667936
fix(lpse2d): parity runs are float64 -- set JAX_ENABLE_X64 in the CLI…
physicistphil b3b89cb
feat(lpse2d): terms.light.snap_beam_ky -- launch an oblique pump at t…
physicistphil faa3151
fix(lpse2d): terms.light.transverse_fields defaults to false (plan 2 …
physicistphil 2056c0c
feat(lpse2d): light spectrum probes -- save.light_spectrum (plan 2 L.6)
physicistphil 206ebb3
test(lpse2d): s-pol no-TPD run compares the free-decay energy to roun…
physicistphil 1ebdb86
feat(lpse2d): FD light stencil order 2/4/6 and leftward pump beams (p…
physicistphil 81ff164
feat(lpse2d): FD injector for oblique, multiple and profiled pump bea…
physicistphil ae0bfe6
feat(lpse2d): resonance absorption of the pump at the critical surfac…
physicistphil 84b0886
feat(lpse2d): PML absorbing layers for the FD light fields (plan 2 L.2)
physicistphil 94f513f
feat(lpse2d): LPSE's absoluteThreshold search and its deck keys (plan…
physicistphil 887c926
test(lpse2d): the FD injector now takes an oblique leftward beam (pla…
physicistphil fa4736c
fix(lpse2d): refuse TPD + SRS on the separate-equation solver, as LPS…
physicistphil 078ec22
feat(lpse2d): quasilinear evolution of the electron distribution -- t…
physicistphil 658b30d
style(lpse2d): wrap a qle docstring line
physicistphil 43ee4bb
feat(lpse2d): finite-difference IAW solver with plasma-flow profiles …
physicistphil a7b34a0
fix(lpse2d): the tpd-srs-iaw example runs LPSE's combined formulation…
physicistphil bcee98d
feat(lpse2d): terms.light.one_way -- directional mask on the evolved …
physicistphil bea5d0b
fix(lpse2d): translator reads laser.pulseShape.* and bracketed vector…
physicistphil 0a9a3d0
feat(lpse2d): implicit Fokker-Planck step for the QLE; light-only dec…
physicistphil 3cdb700
fix(lpse2d): 1-D decks keep a periodic y boundary; compare() tolerate…
physicistphil 621b27b
fix(lpse2d): QLE gradient falls back to the plain difference at a pla…
physicistphil aee5f9b
fix(lpse2d): refuse y-face beams in the translator and grazing beams …
physicistphil dcc187e
style(lpse2d): wrap a translator message
physicistphil 28d150f
fix(lpse2d): the IAW density feeds back as Nelf * n_b(x), not as a fr…
physicistphil 7248740
fix(lpse2d): the general FD injector wrote only one of a leftward bea…
physicistphil cc90a23
chore(lpse2d): parity batches for the 2026-09-22 IAW-feedback and inj…
physicistphil 2fe551a
fix(lpse2d): translator maps laser.evolution.riseTime to drivers.E0.t…
physicistphil bd17d05
feat(lpse2d): pump launched from LPSE injector files -- drivers.E0.in…
physicistphil 8531b26
fix(lpse2d): translated decks open the pump envelope before t = 0
physicistphil 773e4b1
fix(lpse2d): translator anti-aliases at LPSE's default range 0.3334 w…
physicistphil 8de4751
fix(lpse2d): pulse shape is LPSE's power factor, applied on every pum…
physicistphil a01bea2
fix(lpse2d): beam super-Gaussian defaults to LPSE's order 4; order or…
physicistphil 549043a
fix(lpse2d): the fd IAW step is LPSE's -- drive sign, k-space cadence…
physicistphil dcbfa93
fix(lpse2d): combined solver damps and seeds noise as LPSE; separate …
physicistphil 6eda074
fix(lpse2d): absorbing layers per field and injector planes as LPSE; …
physicistphil 46833f9
fix(lpse2d): translator keys and defaults as the C++ reads them; LPSE…
physicistphil 8aa0fdf
fix(lpse2d): translated grids keep LPSE's node counts; fd-combined de…
physicistphil 6a73f94
feat(lpse2d): translate LPSE's own solver time steps; exact fd-light …
physicistphil 914a0d7
feat(lpse2d): LPSE step order with time-interpolated sources
physicistphil 8a95853
fix(lpse2d): combined solver drives the IAW with the whole field at wp0
physicistphil c8c8628
fix(lpse2d): IAW absorber acts over the IAW step
physicistphil d130908
fix(lpse2d): IAW band has its own wavenumber cap
physicistphil 36582f0
fix(lpse2d): LPSE IAW solvers window the drive after the Laplacian
physicistphil e8bfef4
fix(lpse2d): translated density on LPSE's nodes; LPSE's cell size
physicistphil 421e0ac
fix(lpse2d): rectangular dealias uses LPSE's anti-aliasing indices
physicistphil c1f7c77
fix(lpse2d): TPD source windowed after its k-space assembly
physicistphil 8ec5102
test(lpse2d): neutralise the pump's own absorber in the FD projection…
physicistphil 49a91e6
fix(lpse2d): light coupling sources suppressed as LPSE's calculateSou…
physicistphil b71fc8e
feat(lpse2d): HPE resonance at LPSE's expanded Bohm-Gross frequency
physicistphil f874833
feat(lpse2d): static pump swells by LPSE's constant factor by default
physicistphil e3d6b85
fix(lpse2d): absolute-threshold criterion as LPSE's checkIsAboveThres…
physicistphil b4828ad
fix(lpse2d): thermal-filamentation source as LPSE's
physicistphil 1a8194f
feat(lpse2d)!: native defaults follow LPSE (C++ conventions)
physicistphil df54490
feat(lpse2d): LPSE's combined solver on the FD light propagator
physicistphil 0001142
test(lpse2d): tests follow the C++ defaults; exchange mask in the rot…
physicistphil ffb03ff
fix(lpse2d): FD light absorbs each field at its own rate
physicistphil b866c23
test(lpse2d): restart test keeps the flat noise it checks
physicistphil 306fc34
test(lpse2d): s / p launch comparison at the same light step
physicistphil 350c32e
test(lpse2d): combined + FD light without an evolved pump is refused …
physicistphil 044784f
fix(lpse2d): combined solver keeps the k = 0 mode of E1, as LPSE
physicistphil 59e01d0
fix(lpse2d): combined solver builds its noise kick for the light sub-…
physicistphil 1e3cf4a
fix(lpse2d): combined solver injects the pump with the spectral injector
physicistphil f6b1e1a
fix(lpse2d): combined-mode field saves and Raman flux maps exclude th…
physicistphil 27418ef
fix(lpse2d): LPSE binary density files are not transposed twice
physicistphil f6c7429
fix(lpse2d): quasi-1D HPE push keeps periodic crossings for the wall …
physicistphil 278fda7
test(lpse2d): the test_010 beam-direction translation runs from a che…
physicistphil 4a3e3b1
Merge lpse2d/lpse-parity (PR #372 review fixes) into lpse2d/lpse-pari…
physicistphil 0c8008c
fix(lpse2d): FD-IAW resampling aligns the zero frequency on odd grids
physicistphil c715248
fix(lpse2d): parity CLI reads lpse.metrics in the flattened reference…
physicistphil b00d903
test(lpse2d): verify baseline test skips when its run is not on the t…
physicistphil f5dc1a4
test(lpse2d): pin the noise seed of the in-run QLE flattening test
physicistphil 3b582f6
test(lpse2d): QLE flattening is checked over the saved times, criteri…
physicistphil e6def50
feat(lpse2d): spatially windowed electron distribution for HPE (terms…
physicistphil 72b6df4
fix(lpse2d): combined solver injects the Raman seed (drivers.E1)
physicistphil c387f68
fix(lpse2d): HPE wall-energy bins count only energies inside their edges
physicistphil e5c7327
fix(lpse2d): one normal-incidence beam keeps its frequency shift and …
physicistphil 8588bf1
Merge lpse2d/lpse-parity (PR #372 review round 2) into lpse2d/lpse-pa…
physicistphil 033d613
docs(lpse2d): the combined-solver seed follows drivers.E1.polarization
physicistphil c214230
fix(lpse2d): QLE explicit update is reverse-mode differentiable
physicistphil aa48996
fix(lpse2d): laser-budget metrics follow the pump's entrance face
physicistphil 3ec2c78
Merge lpse2d/lpse-parity-plan2 (PR #372 / #378 review round 2) into f…
physicistphil 0a66613
feat(lpse2d): terms.hpe.hist_save_interval saves the HPE histograms o…
physicistphil c5fee59
fix(lpse2d): a disabled LPSE laser translates to no pump
physicistphil bc78533
fix(lpse2d): Raman flux diagnostics use the combined field's carrier
physicistphil de42398
Merge lpse2d/lpse-parity (PR #372 review round 3) into lpse2d/lpse-pa…
physicistphil 5eae480
fix(lpse2d): the E1 light-spectrum probe uses the combined carrier
physicistphil 3b810d8
fix(lpse2d): the FD combined solver injects the Raman seed (drivers.E1)
physicistphil 962e2a1
fix(lpse2d): FD-IAW PPM fluxes are upwinded per shared face
physicistphil 393d366
Merge lpse2d/lpse-parity-plan2 (PR #372 / #378 review round 3) into f…
physicistphil f687c21
fix(lpse2d): the combined E1 light-spectrum probe records the Raman l…
physicistphil c66ea56
Merge lpse2d/lpse-parity-plan2 (combined E1 light-spectrum probe) int…
physicistphil e71050f
fix(lpse2d): the IAW absorber damps over the whole strided IAW step
physicistphil 88a65bd
fix(lpse2d): the IAW and the Raman absorption see the combined light …
physicistphil bac837e
fix(lpse2d): combined runs skip the separate light solvers and their …
physicistphil fe8c40c
Merge lpse2d/lpse-parity (PR #372 review round 4) into lpse2d/lpse-pa…
physicistphil 5a56eb0
fix(lpse2d): combined-mode absorption follows the evolved ion density
physicistphil cc48019
fix(lpse2d): parity batch dry runs record nothing
physicistphil d67ffd4
Merge lpse2d/lpse-parity-plan2 (PR #372 / #378 review round 4) into f…
physicistphil f123c5e
fix(lpse2d): the deck translator runs LPSE's square cells
physicistphil 3fccddb
fix(lpse2d): the FD pump injector refuses the time controls it does n…
physicistphil a415138
Merge lpse2d/lpse-parity (PR #372 review round 5) into lpse2d/lpse-pa…
physicistphil d4fab87
fix(lpse2d): resonance absorption -- one enable predicate, absolute c…
physicistphil db0f76b
Merge lpse2d/lpse-parity-plan2 (PR #372 / #378 review round 5) into f…
physicistphil c440b7e
fix(lpse2d): the combined field is damped at the Raman absorption
physicistphil 6724180
fix(lpse2d): threshold bisection refuses a zero lower bound
physicistphil ae6a6da
Merge lpse2d/lpse-parity (PR #372 review round 6) into lpse2d/lpse-pa…
physicistphil 8b1430b
fix(lpse2d): the quasi-1D resonance-absorption filter keeps its passband
physicistphil 38d5783
fix(lpse2d): KAP phase histories are prefix-stable as the run grows
physicistphil 73c8b7d
fix(lpse2d): restart rebases the light-spectrum probes onto the resum…
physicistphil df8a935
fix(lpse2d): parity batch files keep every window; unknown job keys a…
physicistphil 7821ca1
fix(lpse2d): the LPSE threshold search refuses a zero starting intensity
physicistphil 499056a
Merge lpse2d/lpse-parity-plan2 (PR #372 / #378 review round 6) into f…
physicistphil 8f5668d
fix(lpse2d): a light-only PML run uses the pump's layer
physicistphil 49af206
Merge lpse2d/lpse-parity-plan2 (light-only PML layer) into feat/lpse2…
physicistphil 2577b05
fix(lpse2d): build the separate EPW solver only on the separate path
physicistphil 55fd66f
fix(lpse2d): refuse fd rotation coupling with the transverse projection
physicistphil 3a096b8
Merge branch lpse2d/lpse-parity (round-7 fixes) into lpse2d/lpse-pari…
physicistphil 4223609
fix(lpse2d): fd rotation refusal follows plan2's transverse_source de…
physicistphil adec784
fix(lpse2d): FD combined source takes the EPW window and the pump k-f…
physicistphil d24121d
docs(lpse2d): the combined depletion is unprojected
physicistphil 5f7b67b
Merge branch lpse2d/lpse-parity-plan2 (round-7 fixes) into feat/lpse2…
physicistphil 19cbe2e
fix(lpse2d): put the interpolated field save on a coarser grid, in x-…
physicistphil 7081182
test(lpse2d): gradient objective takes the EPW energy from the grid
physicistphil b28e711
Merge branch lpse2d/lpse-parity-plan2 (gradient-test objective) into …
physicistphil 952252c
fix(lpse2d): the spectral light band ignores the EPW wavenumber cap
physicistphil ca6ced8
fix(lpse2d): signed EPW dissipation with HPE allow_growth
physicistphil 7a22883
Merge branch lpse2d/lpse-parity (light band, signed dissipation) into…
physicistphil 70c57a7
Merge branch lpse2d/lpse-parity-plan2 (light band, signed dissipation…
physicistphil 359f9cd
fix(lpse2d): signed windowed EPW dissipation with HPE allow_growth
physicistphil ae81e7d
fix(lpse2d): an empty HPE window initialises to a zero histogram
physicistphil 9119fd2
fix(lpse2d): combined-mode EPW dissipation and lw heating use the com…
physicistphil d9f0d46
Merge branch lpse2d/lpse-parity (combined-mode damping in diagnostics…
physicistphil 8bd768f
test(lpse2d): combined boundary-loss diagnostic uses the combined layer
physicistphil 1f35493
fix(lpse2d): the FD combined light step obeys the combined bound with…
physicistphil 72f578b
fix(lpse2d): the FD IAW fine-grid band is the coarse mask mode by mode
physicistphil 1467202
Merge branch lpse2d/lpse-parity-plan2 (round-8 fixes) into feat/lpse2…
physicistphil 3e8f4f9
docs(lpse2d): HPE gamma limits bound the rate before the energy-conse…
physicistphil 957a791
Merge branch lpse2d/lpse-parity (HPE limit docs) into lpse2d/lpse-par…
physicistphil 0d50e67
fix(lpse2d): the dissipation diagnostic follows the evolved QLE Landa…
physicistphil 8a0ec7d
Merge branch lpse2d/lpse-parity-plan2 (HPE limit docs, QLE dissipatio…
physicistphil 971f999
fix(lpse2d): HPE gamma limits bound the applied rate after energy con…
physicistphil a694ba0
Merge branch lpse2d/lpse-parity (HPE limits after energy conservation…
physicistphil 1d2bac5
fix(lpse2d): the FD IAW restriction to the EPW grid keeps only the IA…
physicistphil 56e4c86
Merge branch lpse2d/lpse-parity-plan2 (HPE limits after energy conser…
physicistphil 9eb7ff5
fix(lpse2d): saved EPW ex/ey without the stray 4/(nx ny) factor
physicistphil 984f0f6
docs(lpse2d): scope the saved-field Fourier identities to full-grid s…
physicistphil 86606c8
test(lpse2d): run the slow HPE trapping and smoke regressions again
physicistphil c25d7eb
Merge branch lpse2d/lpse-parity (slow HPE tests) into lpse2d/lpse-par…
physicistphil 4d193ad
test(lpse2d): HPE linear closure is measured against the configured L…
physicistphil 85f29cc
fix(lpse2d): FD-combined inverse-Landau growth is exponential; FD IAW…
physicistphil 827531b
Merge branch lpse2d/lpse-parity-plan2 (round 9, slow HPE tests) into …
physicistphil df43c9b
test(lpse2d): HPE smoke test with enough particles for its kpeak check
physicistphil 8dee419
Merge branch lpse2d/lpse-parity (HPE smoke test particles) into lpse2…
physicistphil 88a78bd
Merge branch lpse2d/lpse-parity-plan2 (HPE smoke test particles) into…
physicistphil 5686b91
docs(lpse2d): list the intentional departures from LPSE
physicistphil 69231fb
Merge branch lpse2d/lpse-parity-plan2 (LPSE departures table) into fe…
physicistphil 360d65f
docs(lpse2d): single trailing newline in the overview (end-of-file-fi…
physicistphil e7977c3
Merge branch lpse2d/lpse-parity-plan2 (overview trailing newline) int…
physicistphil bca2538
docs(lpse2d): complete the LPSE departures list from an audit; fix wr…
physicistphil a99157e
Merge branch lpse2d/lpse-parity-plan2 (LPSE departures audit) into fe…
physicistphil cddb6dc
docs(lpse2d): the windowed HPE options in the LPSE departures list
physicistphil e2bc460
Merge PR #388 (fix/lpse2d-interpolated-field-save, 984f0f6) into lpse…
physicistphil 71fbc43
Merge branch lpse2d/lpse-parity (PR #388 merge) into lpse2d/lpse-pari…
physicistphil 856df24
Merge branch lpse2d/lpse-parity-plan2 (PR #388 merge) into feat/lpse2…
physicistphil a85fbd5
test(lpse2d): full-grid field saves where tpd.yaml's save spacing is …
physicistphil 4f4c124
Merge branch lpse2d/lpse-parity (full-grid saves in tests) into lpse2…
physicistphil 02df382
test(lpse2d): PR #388's field-save tests and the light-spectrum probe…
physicistphil 8ed3491
Merge branch lpse2d/lpse-parity-plan2 (PR #388 test adaptations) into…
physicistphil 5119513
fix(lpse2d): refresh the prescribed pump before the IAW and EPW read it
physicistphil 30493b4
docs(lpse2d): the prescribed-pump timing in Departures from LPSE
physicistphil 75d3399
fix(lpse2d): HPE flux parity compares the same time window on both sides
physicistphil 7099c40
fix(lpse2d): parity verify fails on a non-finite stored or recomputed…
physicistphil a62b52c
fix(lpse2d): refuse Poynting maps on a single-point x save grid at setup
physicistphil f0a35b2
fix(lpse2d): windowed epw_dissipation is the energy the damping step …
physicistphil 04b432a
Merge branch lpse2d/lpse-parity (Poynting save-grid check) into lpse2…
physicistphil 2239faa
Merge branch lpse2d/lpse-parity-plan2 (pump timing, parity harness, P…
physicistphil File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| """LPSE's combined TPD + SRS solver with the finite-difference light propagator (inventory A15). | ||
|
|
||
| With ``lw.solver = combined`` and ``{laser|raman}.solver = fd`` LPSE advances the combined field -- | ||
| the Raman class, carrier ``wpe``, whose transverse part is the Raman light and whose longitudinal | ||
| part is the EPW -- with its FD Schroedinger step, the grad-div coefficient carrying the Bohm-Gross | ||
| term (``SchrodingerSolver3::step_2d``, ``gradDivCoeff = dt 3 Ve^2 / (2 wpe) - Cdt``): | ||
|
|
||
| dE1/dt = i c^2/(2 wp0) [lap E1 - grad div E1] + i 3 vte^2/(2 wp0) grad div E1 | ||
| + i wp0/2 (1 - n/n_env) E1 + unified source, | ||
|
|
||
| the pump with the same FD step, and the sources from FD derivatives (``LightSolver::calculateSources``: | ||
| ``divergence`` / ``gradE`` with the solver's stencil). Landau damping and the EPW noise act on the | ||
| longitudinal part in k-space once per Langmuir-wave step (``LwSolver::advanceLW_combinedTPDandSRS_FD``); | ||
| the spectral combined solver (``combined.py``) does both per light sub-step, as LPSE's spectral path. | ||
|
|
||
| This class is ``CoupledLight`` (the FD pump, its injectors and absorbers, the staggered sub-steps) | ||
| with the combined field's operator and the unified couplings of ``CombinedSolver`` in FD form. | ||
| """ | ||
|
|
||
| import numpy as np | ||
| from jax import Array | ||
| from jax import numpy as jnp | ||
|
|
||
| from adept._lpse2d.core.combined import CombinedSolver | ||
| from adept._lpse2d.core.light import CoupledLight | ||
| from adept._lpse2d.core.vector import fft2c, ifft2c, k_dot | ||
|
|
||
|
|
||
| class FDCombinedSolver(CoupledLight): | ||
| """The combined field E1 and the evolved pump E0 on the FD light propagator.""" | ||
|
|
||
| def __init__(self, cfg: dict): | ||
| if not cfg["terms"].get("light", {}).get("pump_depletion", False): | ||
| raise ValueError( | ||
| "terms.epw.solver: combined with terms.light.solver: fd needs terms.light.pump_depletion " | ||
| "(LPSE's FD combined decks evolve the pump; the static pump runs on the spectral combined solver)" | ||
| ) | ||
| super().__init__(cfg) | ||
| # the k-space pieces of the combined formulation: band, potential, Landau rate, noise | ||
| self.spectral = CombinedSolver(cfg) | ||
| # the separate SRS / TPD couplings of CoupledLight are replaced by the unified ones | ||
| self.srs_enabled = False | ||
| self.tpd_enabled = False | ||
| self.sources_on = self.spectral.sources_on | ||
| derived = cfg["units"]["derived"] | ||
| vte_sq = derived["vte_sq"] | ||
| # the combined field: carrier wp0, Bohm-Gross longitudinal dispersion | ||
| self.diffraction_coeff = 1j * self.c**2 / (2.0 * self.wp0) | ||
| self.grad_div_coeff = 1j * 3.0 * vte_sq / (2.0 * self.wp0) | ||
| self.linear_coeff = 1j * self.wp0 / 2.0 * (1.0 - self.n_over_env) | ||
| self.n_over_nc1 = self.n_over_env # the Raman class's critical density is n_env here | ||
| self.w_carrier1 = self.wp0 | ||
| # LPSE's double-exponential layer on the combined field (setupAbsorbingBoundaries_doubleExponential) | ||
| grid = cfg["grid"] | ||
| self.sub_boundary = grid.get("combined_absorbing_boundaries", grid["absorbing_boundaries"]) ** ( | ||
| 1.0 / self.n_sub | ||
| ) | ||
| self.delta_w = self.w0 - 2.0 * self.wp0 | ||
| self.source_coeff = self.spectral.source_coeff # -i e / (4 me w0) | ||
| self.depletion_coeff = self.spectral.depletion_coeff if hasattr(self.spectral, "depletion_coeff") else None | ||
| if self.depletion_coeff is None: | ||
| self.depletion_coeff = 1j * self.e / (2.0 * self.me * self.w0) | ||
| self.rho_factor = 1.0 - self.w0 / self.wp0 | ||
| # the unified source's EPW-side restrictions, as CombinedSolver.unified_source applies them: the | ||
| # x-space EPW source mask (terms.epw.source_window and the EPW's absorber / injector-row | ||
| # suppression) and the k-filter on the pump entering it (terms.epw.source.srs_k_filter, LPSE | ||
| # lw.kFilter in LwSolver::make_E0E1, on the FD path as on the spectral one) | ||
| self.epw_source_mask = self.spectral.source_mask | ||
| e0_filter = np.asarray(self.spectral.E0_filter) | ||
| self.E0_filter = None if bool(np.all(e0_filter == 1.0)) else jnp.asarray(e0_filter) | ||
| # Landau damping / noise once per EPW step (the LW step) | ||
| self.noise_enabled = self.spectral.noise_enabled | ||
| if self.noise_enabled: | ||
| from adept._lpse2d.core.epw import noise_kick_spectrum | ||
|
|
||
| self.noise_kick_lw = jnp.asarray(noise_kick_spectrum(cfg, dt=self.dt, nu_coll=self.spectral.nu_raman)) | ||
| print("combined solver on the FD light propagator (LPSE lw.solver = combined, raman.solver = fd)") | ||
|
|
||
| def seed_carrier(self) -> float: | ||
| """The seed (``drivers.E1``) enters the combined field at its carrier ``wp0``: LPSE's Raman | ||
| class injects at ``W1 = wpe`` in combined mode (``LightSolver.cpp:2404-2406``), as the | ||
| spectral ``CombinedSolver`` does.""" | ||
| return self.wp0 | ||
|
|
||
| # ------------------------------------------------------------ FD pieces -- | ||
|
|
||
| def _div(self, E: Array) -> Array: | ||
| return self._dx(E[..., 0]) + self._dy(E[..., 1]) | ||
|
|
||
| def _grad_div(self, E: Array) -> list[Array]: | ||
| ex, ey = E[..., 0], E[..., 1] | ||
| out = [self._d2x(ex) + self._dxdy(ey), self._dxdy(ex) + self._d2y(ey)] | ||
| if E.shape[-1] == 3: | ||
| out.append(jnp.zeros_like(ex)) | ||
| return out | ||
|
|
||
| def unified_source_fd(self, t: float, E0: Array, E1: Array) -> Array: | ||
| """``-i e/(4 me w0) e^{-i dw t} [grad(E0 . E1*) + (1 - w0/wp0) E0 (div E1)*]`` with the FD | ||
| stencils (LPSE ``gradE`` / ``divergence``), the pump k-filtered and the result multiplied by | ||
| the EPW source mask as in ``CombinedSolver.unified_source``.""" | ||
| if self.E0_filter is not None: | ||
| E0 = ifft2c(fft2c(E0) * self.E0_filter[..., None]) | ||
| scalar = jnp.sum(E0 * jnp.conj(E1), axis=-1) | ||
| grad = [self._dx(scalar), self._dy(scalar)] | ||
| if E1.shape[-1] == 3: | ||
| grad.append(jnp.zeros_like(scalar)) | ||
| div1 = self._div(E1) | ||
| term = jnp.stack(grad, axis=-1) + self.rho_factor * E0 * jnp.conj(div1)[..., None] | ||
| if self.epw_source_mask is not None: | ||
| term = term * self.epw_source_mask[..., None] | ||
| return self.source_coeff * jnp.exp(-1j * self.delta_w * t) * term | ||
|
|
||
| def unified_depletion_fd(self, t: float, E1: Array) -> Array: | ||
| """``i e/(2 me w0) e^{+i dw t} E1 div E1`` with the FD divergence.""" | ||
| return self.depletion_coeff * jnp.exp(1j * self.delta_w * t) * E1 * self._div(E1)[..., None] | ||
|
|
||
| def coupled_rhs( | ||
| self, | ||
| t: float, | ||
| E0: Array, | ||
| E1: Array, | ||
| laplacian_phi: Array, | ||
| pump_args: dict, | ||
| seed_args: dict | None, | ||
| iaw_density: Array | None = None, | ||
| phi_k: Array | None = None, | ||
| couple: bool = True, | ||
| patterns: list[Array] | None = None, | ||
| ) -> tuple[Array, Array]: | ||
| # pump: CoupledLight's propagation, detuning and injector rows (its SRS / TPD terms are off) | ||
| k_e0 = self.pump_rhs(t, E0, E1, laplacian_phi, pump_args, iaw_density, None, couple=False, patterns=patterns) | ||
| linear_coeff = self.linear_coeff | ||
| if iaw_density is not None: | ||
| linear_coeff = linear_coeff - 1j * self.wp0 / 2.0 * iaw_density * self.iaw_feedback | ||
| comps = [E1[..., i] for i in range(E1.shape[-1])] | ||
| k_e1 = [ | ||
| self.diffraction_coeff * cc + self.grad_div_coeff * gd + linear_coeff * e | ||
| for cc, gd, e in zip(self.curl_curl(E1), self._grad_div(E1), comps, strict=True) | ||
| ] | ||
| if seed_args is not None: | ||
| # the FD injector rows of the separate Raman solver, at the combined carrier wp0 | ||
| k_e1 = self.add_seed_rows(k_e1, self.calc_seed_source(t, seed_args)) | ||
| k_e1 = jnp.stack(k_e1, axis=-1) | ||
| if self.sources_on: | ||
| depletion = self.unified_depletion_fd(t, E1) | ||
| source = self.unified_source_fd(t, E0, E1) | ||
| if not isinstance(self.source_mask0, float): | ||
| depletion = depletion * self.source_mask0[..., None] | ||
| if not isinstance(self.source_mask1, float): | ||
| source = source * self.source_mask1[..., None] | ||
| k_e0 = k_e0 + depletion | ||
| k_e1 = k_e1 + source | ||
| return k_e0, k_e1 | ||
|
|
||
| # ------------------------------------------------------- the LW step -- | ||
|
|
||
| def langmuir_step(self, t: float, E1: Array, gamma_landau) -> Array: | ||
| """Landau damping and noise on the longitudinal part, once per EPW step (LwSolver:: | ||
| advanceLW_combinedTPDandSRS_FD): ``E_L -= E_L (1 - exp(-gamma dt))`` (``gamma dt`` below 1e-3) | ||
| and ``E += -i k phi_noise`` on the retained band.""" | ||
| import jax | ||
|
|
||
| band = self.spectral.band | ||
| e_k = fft2c(E1) | ||
| rate_dt = gamma_landau * self.dt | ||
| # LPSE's linearised coefficient only for |gamma dt| < 1e-3: a negative (inverse-Landau) rate of | ||
| # any size takes the exponential, -expm1(-gamma dt) (LPSE tests the signed rate, PR #378 review) | ||
| coeff = jnp.where(jnp.abs(rate_dt) < 1.0e-3, rate_dt, -jnp.expm1(-rate_dt)) * band | ||
| kx, ky = self.spectral.kx[:, None], self.spectral.ky[None, :] | ||
| k_dot_e = k_dot(e_k, self.spectral.kx, self.spectral.ky) | ||
| e_long = [kx * k_dot_e * self.spectral.one_over_k_sq, ky * k_dot_e * self.spectral.one_over_k_sq] | ||
| comps = [e_k[..., 0] - coeff * e_long[0], e_k[..., 1] - coeff * e_long[1]] | ||
| if self.noise_enabled: | ||
| step = jnp.round(t / self.dt).astype(int) | ||
| key = jax.random.fold_in(self.spectral.noise_key, step) | ||
| phases = 2.0 * np.pi * jax.random.uniform(key, (self.nx, self.ny)) | ||
| phi_kick = self.noise_kick_lw * jnp.exp(1j * phases) * band | ||
| comps = [comps[0] - 1j * kx * phi_kick, comps[1] - 1j * ky * phi_kick] | ||
| if e_k.shape[-1] == 3: | ||
| comps.append(e_k[..., 2]) | ||
| return ifft2c(jnp.stack(comps, axis=-1)) | ||
|
|
||
| def __call__(self, t: float, y: dict, driver_args: dict, E0_fn=None) -> tuple[Array, Array, Array]: | ||
| """One EPW step: the LW step on the longitudinal part, then the light sub-steps. Returns | ||
| ``(E0, E1, phi_k)`` like ``CombinedSolver``.""" | ||
| if self.spectral.evolved_landau: | ||
| gamma_landau = y["gamma_L"] | ||
| elif self.spectral.landau_enabled: | ||
| gamma_landau = self.spectral.landau_rate | ||
| else: | ||
| gamma_landau = jnp.zeros_like(self.spectral.k_sq) | ||
| E1 = self.langmuir_step(t, y["E1"], gamma_landau) | ||
| zero_phi = jnp.zeros((self.nx, self.ny), dtype=jnp.complex128) | ||
| E0, E1 = CoupledLight.__call__( | ||
| self, t, y["E0"], E1, zero_phi, driver_args["E0"], driver_args.get("E1"), y.get("iaw_density") | ||
| ) | ||
| return E0, E1, self.spectral.potential(E1) |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Restrict the linearized Landau update to small rate magnitudes
In
FDCombinedSolver.langmuir_step,jnp.where(rate_dt < 1.0e-3, rate_dt, 1.0 - jnp.exp(-rate_dt))takes the approximation for every negative rate, not just rates close to zero. This is reachable:__call__passes the state'sgamma_Lwhenself.spectral.evolved_landauis enabled, and QLE feedback or HPE withallow_growth: truecan supply negative rates.For a retained longitudinal mode, this makes the multiplier
1 - gamma_landau * dtinstead ofexp(-gamma_landau * dt)even when the growth per EPW step is substantial. Growth is consequently underestimated and acquires an unintended timestep dependence, unlike the exponential update in the spectral combined solver and the signed exponential energy-transfer diagnostic.Test the magnitude (
abs(rate_dt)) for the small-argument branch, or use-jnp.expm1(-rate_dt)throughout. Add a retained-mode regression with a non-small negative rate that checks the longitudinal exponential gain and preservation of the transverse component.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 85f29cc.
FDCombinedSolver.langmuir_stepnow uses LPSE's linearised coefficient only for |γ dt| < 1e-3, and-expm1(-γ dt)otherwise. Small damping keeps LPSE's coefficient, so test_022 and similar decks are unchanged; a negative rate of any size outside that regime grows exponentially. LPSE tests the signed rate, so this departs from it for large negative rates.Regression:
test_fd_combined.py::test_langmuir_step_grows_negative_rates_exponentially, on a retained longitudinal mode at γ dt = −0.1, −5e-4, 5e-4 and 0.1, compared with exp or linear at rtol 1e-12. Before the fix the −0.1 case gave 1.1 instead of e^0.1 (0.47 % low). Fixed on plan2 (#378), merged here in 827531b.