Changelog#
Unreleased#
Added#
The desktop front end’s distribution,
solprop-gui, built fromgui/in this repository and installed withpip install "solprop[gui]"at solprop’s own version. Only its skeleton is in place: thesolprop-guicommand opens a placeholder window. Its design isdocs/dev/gui/design.md.The first performance-history rows (
benchmarks/history/), recorded at 0.1.0 on six CPU and three GPU hardware sets, with numba runs beside the native ones on three of them;docs/dev/benchmarking.mdlists how each is run. The history takes any backend: each row names it, and rows compare only within one backend; a run the history recorded no longer makes the next run in that checkout dirty. The benchmark harness names an AMD GPU from its PCI IDs where ROCm reports only “AMD Radeon Graphics”.One table of the route defaults,
solprop.driver.defaults.ROUTE_DEFAULTS, with the functions that resolve each default (default_rtol,default_integrator,default_controller,default_scheme,resolve_noise); the run record’s newdefaultsentry (DefaultsResolution), the start-of-run summary and the settings’ display say which of the integrator,rtoland the collar scheme took the route’s default, and the summary names a Multistep carrier frame (#87).Recipes (SEP 41):
solprop.recipe.Recipereads a TOML description of agnlseorcapillaryrun, with quantities in their units ("125 um","1.2 bar"), checks it (check(), each issue at its parameter path), builds and runs it, saves it and exports it as the Python script it stands for (to_python()).solprop.recipe.parametersgives each keyword’s forms, units, display unit, default rule and manual section. Thesolpropcommand (alsopython -m solprop) runs, checks and exports recipes, and forwardssolprop scan. Example recipes ship with the package (Recipe.examples()). Errors and warnings that concern a value carry itsparameter; writing TOML takes thetomli-wdependency.solprop.grid.suggestproposeswavelength_limitsandtrangefrom the input pulses, as a starting point. A run built from a recipe records it in a top-levelrecipegroup of its output, as written and as resolved (the recipe that rebuilds the run);Simulation.run(stop=...)takes a flag that stops the run, writes its state and returns, andsolprop runstops cleanly on Ctrl-C.solprop resumeandsolprop.recipe.resume_filecontinue a run from its file alone, andsolprop.output.resumabilitysays whether a file can continue, from where and with whichresume=, whether it can rebuild itself, and, on request, what the gate would compare.run_scan(stop=)takes the same flag: Local and Pool stop the point in flight, Pool through a message on the worker pipe, Batch between groups; a stopped point is not committed and a rerun computes it; Queue and Slurm refuse it.run_scan(progress=)accepts asolprop.log.ScanReporter(and aWatchingScanReporterfor each point’s run, relayed from Pool workers). A recipe’s[scan]table givesRecipe.scan(), ready forrun_scan.Simulation.estimate()reports a run’s memory (admitted before anything is allocated), output size and the cost of one right-hand-side evaluation;solprop.capabilities.outputsays what a results file can give: its basis, which quantities are physical fluxes, which planes keep fields, which operations its readers refuse and why. A reporter that implementsobserve_plane(solprop.log.PlaneObserver) is shown each saved plane as a lazy view, whose spectrum, field, power and spectral density come from the processing functions, fetched from a device only on request; a failing observer is detached and the run completes. See recipes and the command line.Design solvers for any guide (SEP 41):
solprop.tools.solve_parameterfinds every value of a guide or fill parameter (a radius, a pressure, a core size, a wall thickness) that meets a target,ZDW,RDWorSolitonOrder, discarding sign changes through a wall resonance, and returns them asDesignRootswith the attainable range when there is none.tools.radius_zdwinvertszdwin the radius;tools.soliton_peak_powerandtools.pulse_energygive the pulse for a soliton order. A recipe’s[pins]table sets a value from such a target (Recipe.resolve(),Recipe.pin_roots()), solved at each build and each scan point and recorded with the run.Recipe.modes()andRecipe.preview_input()preview the mode set and the launched pulse before a run.solprop.capabilities.routelists a route’s keywords, their values and the rules that couple them, each refusal naming its rule (SolpropError.rule), andsolprop.capabilities.specieswhat a gas admits: its ionisation models with their status, its Raman response, and its data ranges.solprop.pulses.load_spectrumreads a measured spectrum from a text file (frequency or wavelength, power density, and an optional phase) into aDataPulse. A wrapped phase column is unwrapped only where the power is above a noise floor, a phase in the FFT sign convention of a Luna.jl file is read withphase_convention="fft", and a wrapped column sampled too coarsely to unwrap is warned on (SEP 41).prop_capillary,prop_waveguideand theirSimulationconstructors takelaunch=, which puts each pulse into chosen modes with a share and an optional phase:launch={"HE1,1": 0.9, "HE1,2": {"share": 0.1, "phase": 1.57}}, or a list with one mapping per pulse ofpulses=. Modes are named aschannel=names them; the shares are energy fractions, and on a guide that declares a change of basis a coordinate split scaled to the pulse’s energy under the flux metric. The run records the launch asconfiguration["modal_launch"], which a resume compares; a run without one records nothing and resumes as before (SEP 41).SpatialPulsetakesdisplacement=(d_x, d_y)andtilt=(theta_x, theta_y), ascoupling.Alignmentdefines them, applied at the launch plane on a Cartesian grid with the energy set after them; a tilt the grid’s band, window or time window cannot carry is refused, and so is either on a radial grid, a y component on a line grid, aPlanarPulseand a guide’s incident beam. A record without them continues as an unmoved beam (SEP 41).prop_waveguideandSimulation.from_waveguidetakeinterpolate_planesandstep_planes, andprop_capillary,prop_waveguideand their constructors takepolarisation_taper, asprop_gnlsedoes;step_planesdefaults toNoneon every interface that takes it, which onprop_gnlsebehaves as its0did. The guide lists the deliberate differences between the routes’ keywords (SEP 41).Simulation.from_radial,from_freeandfrom_planartakeabsorberandabsorber_scheme, asprop_radial,prop_freeandprop_planarand the modalfrom_*do.solprop.solexportswavelength_to_axis, which the plots’ documentation sends the reader to.resume="force"continues a file across changed physics, in the same file, while the stored arrays’ layout agrees, the statistics fit and work is left: the Adams history restarts, the session’s execution record marks the file, and later sessions and readers respect the mark (SEP 42, ADR-063).RunMetadata.compareandsolprop.output.compatibilityhold the comparison;RunOutcome.resumesandRunOutcome.forced_positionsread back how each session continued its file.Cross-section drawings in the library (SEP 41):
solprop.plotting.geometry.outlinegives the outline of a capillary, antiresonant, step-index, graded-index, rectangular or planar guide, or of a radial, Cartesian, line or planar grid, as shapes in metres;draw(ax, guide, palette=...)paints it in Matplotlib, andthumbnail(route, representation, size)makes a small figure of a route for a front end’s picker. Colours come from an explicitPalette(LIGHT,DARK). The user guide’s cross-section figures draw their outlines from it.Recipes on the free-space routes (SEP 41):
radial,freeandplanarrecipes write the grids, medium, responses, beam and settings as tables of registered types (RadialGrid,FreeGrid,LineGrid,PlanarGrid,SpatialMedium,KerrResponsewithgamma3,EnvelopePlasmaResponse,PlasmaResponse,OptmatRate,SpatialPulse,PlanarPulse,SimulationSettings,PIController,default_spec,SpatialAbsorber), the absorber undersettings; the Cartesian and planar tutorials and the planar compressor ship as examples;Recipe.preview_input()gives a free-space beam’s power, energy and fluence at the launch plane, andRecipe.modes()the planar route’s slab channels.
Changed#
solprop.recipeloads its names on first use (PEP 562), assolpropitself does, sosolprop.recipe.quantity,paths,documentanderrorsimport without the driver, the backends, h5py or SciPy: about 40 ms rather than half a second, for a front end that parses quantities in a process that never runs a simulation.A run asking for the
zdwstatistic is no longer stopped when its band holds no zero-dispersion point: the plane recordsnanwith a warning. A directstats.zdwcall still raises, nowNoZeroDispersionError, aModeError.Corrected the mixtures guide on spatial fluence and on who refuses a z table, three worked examples’ prose, how
ReferenceState.temperaturereads where a source declares none, and what the antiresonant guide claims for the dispersion width’s fit to the finite-element solutions.A resume compares the stored and requested run records path by path instead of requiring equal configuration hashes (SEP 42, ADR-063). The layout and the physics must agree, numbers to 1e-11 per element, so a run resumes after an upgrade that adds a field with a declared neutral value, after an optmat release that changed no number it uses, or on another platform; a changed tolerance or integrator continues an unfinished run, with a warning and a record of it, and a finished file is returned only under the numerics every session ran. optmat models are compared by the digest of their numbers, so a changed optmat version, model name or status no longer refuses a resume. A refusal names what differs by class and part of the record, and says what to do instead. An Adams history continues across a changed call budget or safety factor, an Adams file continues under Runge-Kutta, and the interpolated-plane refusal is decided by the file, whatever
interpolate_planesthe resuming run asks for.source=infield_planeis checked by the same comparison.A scan rerun reuses a finished point whose record differs only in rounding, a neutral value or provenance, and refuses one whose numerics or physics changed, naming what; the
QueueandSlurmexecutors keep exact identity (SEP 42). The output schema is bumped only for a change an older reader would misread (ADR-063).backend="auto"now warns, once per process, whenever it choosesnumba_cpu: whensolprop-nativeis not installed as well as on a processor below its wheel’s baseline. The warning says how to install the native backend; an explicitbackend="numba_cpu"does not warn. The README and manual now recommend the native backend by default and describenumba_cpuas its slower fallback, with the 0.1.0 measurements. They no longer advisenumba_cpufor scans of one-thread workers, which the history does not support, and they now say that only GPU builds refuse z-tabulated problems.Planned work has moved to GitHub issues, labelled by type, priority and area: the roadmap now lists only SEPs (ADR-062). Issue templates and
CONTRIBUTING.mdsay what an issue or a pull request should hold and what to check before opening one.The README,
CITATION.cffand the citing page name solprop’s Zenodo DOI, 10.5281/zenodo.23854248, which covers every release.
Changed (breaking)#
A free-space run’s transverse boundary is a
SpatialAbsorber(SEP 40), given asSimulationSettings.spatial_absorberor thespatial_absorberkeyword ofprop_radial,prop_free,prop_planarand theirSimulation.from_*builders. By default every radial, Cartesian, line and planar run carries a designed safety ramp on its outer 10 % of transverse wavenumbers, folded exactly into the operator, radial and planar included;spatial_absorber=Falseremoves it. A real-space collar is designed on request, in the grid’s room outside its requested size, from a protected band (kappa,angle, or the launch) and its width, not from the run length, and is refused by name where it cannot meet its budget.TransverseCollar,transverse_collar_rate,SimulationSettings.transverse_absorberandAbsorber.spatialare removed, andAbsorber.collar_lengthno longer sets a real-space rate. Kernel schema 41.Every spatial grid takes
collar_fractionin place ofwindow_factor, default 0, and puts the room it asks for outside the requested size on every geometry (SEP 40).RadialGrid’sradiusis now the requested aperture, the Dirichlet boundary is atouter_radius = radius * (1 + collar_fraction), andn_rcounts every sample; the transform, the nodes and the effective area use the outer radius. Fractions of 1 and more are allowed.FreeGrid(rx, nx, ry, ny)andLineGridlose their inert 10 % margin, so their spacing at the same sample count changes; givecollar_fraction=0.1for the old grid.RadialGrid(R, n_r)keeps its wall and spacing.A free-space run whose light reaches its transverse boundary now warns or stops, where it used to log and carry on (SEP 40). The boundary monitor reads the outer 5 % of the grid on every backend. It warns above 1e-6 of the launch energy and stops at 1e-4, naming a size and a sample count to try, and
BoundaryCheck.transverse_budgetsets those levels apart from the time window’s. The safety ramp’s removal is judged at the same levels. Its monitor record reports atransverse_status, withramp_removedandspatial_removed. Reads are at mostmin(L/16, 4g/v)apart, a ceiling on the step, so a low-source run takes a few more steps. A launch with more than 1e-4 of its energy in the ramp’s band is refused.Names and keywords with two meanings or two spellings are settled, with no aliases;
docs/dev/conventions.mdsection 7 gives the rule for each:prop_2d,time_1dandspectrogramtaketime_range, nottrange, which stays the scalar window length onprop_*.DEFAULT_TRANGEisDEFAULT_TIME_RANGE.stats.zdwandmodes.base.zdwtakewavelength_limits, notwavelength_range, as doeswavelength_to_axis.wavelength_rangeis now only the attribute that says where a data model is valid.bandpassandPassbandtakespec_range, notlimits, as the plots and accessors do;Passband.limitsisPassband.spec_range. A call passing the pair by position is unaffected.band_coherencetakesdb_min, notfloor_db.plotting.energyandplotting.statsareplot_energyandplot_stats, the names they already had insolprop.sol.backends.protocol.StepHistoryisBackendHistory. Itszis each attempt’s end andoutput.schema.StepHistory’s is its start, so the two no longer share a name.RUN_SUMMARY_ANNOTATIONmoves fromdriver.summarytooutput.schema, beside the other annotation names. The stored name is still"run_summary".
mode_average=Truebesidetdse=is refused by name when the simulation is built. It used to build, then fail at translation with a message that did not name it.resumetakesFalse,True,"interpolated"or"force", and any other value is aValueError: a mistyped word used to read asTrue. An appendingHDF5Outputholds the file’s own record as itsmetadataafter a resume, not the one offered, and compares the two instead of requiring equal hashes (SEP 42).The output schema is 2: a
DataPulserecords itsspectral_phase(below), and every field of a recorded dataclass is recorded, at its default too. This release refuses a file written by 0.1.0, and every configuration hash moves, so a 0.1.0 run cannot be resumed; read such a file with 0.1.0.DataPulse.from_timeis replaced by the classTimeDataPulse: writeTimeDataPulse(t=t, field=field, energy=energy). It holds the time record and has noomega,spectrumorspectral_phase. The method kept the spectrum at the record’s own bin spacing and splined its magnitude and phase, which cannot follow the fringes of pulses far apart: pulses at 100 and 600 fs in a record of 800 fs, of relative amplitude −0.9, came out 85 % wrong with no warning. The class evaluates the record’s transform exactly at the grid’s frequencies, 3e-12 from the direct sum on that case, less its mean: a baseline is an offset of the instrument, not part of the field.
Fixed#
A gallery page re-run on its own keeps its
MaterialRangeWarning: the docs build’s import ofsolprop.solsilenced it for every script after.A scan derived from one cached simulation, the scans guide’s own recipe, now gives every point its own noise seed.
with_input(noise="keep")andwith_mixturekeep the ability to draw unseeded noise again, and the draw replaces only the initial noise of the shared translation. Before, such points shared the base’s draw, depended on the worker underPool, could not be rerun in a new process and failed underbase_seed. Resuming a derived, unseeded run now takes the file’s seed, as a constructor’s does, instead of being refused.One object returned by a scan builder for every point no longer collapses a noise ensemble under
Batch, where every member ran on the last point’s noise while the file recorded each point’s own seed. The draw now goes on a copy, and the builder’s object is left as it is. A file written by such a scan holds the wrong noise for its recorded seeds and a rerun does not detect it: run those points again.A scan file whose points’ run records declare another output schema, a 0.1.0 scan among them, is refused by name, in the words a run file is refused with, when it is read (
ScanOutput,read_point) or resumed or collected (run_scan, a queue’s collector). Before, only the scan container’s own version was checked, and that stayed 1, so such a scan was read as if its layout were this release’s.Under input noise, the boundary monitor subtracts at most one floor from the collar’s removal: the floor is put in once at the launch and cannot lose more. It subtracted the floor’s rate over the whole run, about 37 floors under the default collar, so a removal of a few 1e-3 read as zero, with no warning, and the split scheme’s subtraction grew with the number of steps (#126).
A resumed run’s monitor record continues from the latest execution that kept one: a second hard kill before the monitor started, or an execution with
boundary_check=None, no longer restarts the totals, and the stretch run unwatched is listed underunwatched. The signed removal is kept and restored, so a stop and resume reads as the whole run; the quiet collar’s state is restored; and a resume on a held row no longer counts the resumed state in the next row’s guard maxima (#128).BoundaryCheck(quiet_collar=True)no longer records the collar as left out on the Cartesian, line and planar routes, whose stages keep it, or caps their steps for nothing (#94).A scan point keeps its closing boundary-monitor record, without the per-plane rows, under every executor (
read_point(...)["boundary_monitor"]); a batched point no longer stores the launch’s zeros; and aBoundaryWarningraised as an error fails its own batched point alone, as underLocal(#132).The native backend’s HIP build compiles again under its default warnings-as-errors: ROCm’s Clang refused a thread-local variable that only an OpenMP build uses (0.1.0 needed
-DSOLPROP_NATIVE_WARNINGS_AS_ERRORS=OFF).Resuming a file that holds only its launch plane, from an
input-noise run stopped before its first saved plane, no longer adds the noise draw to the stored field a second time, which had doubled the noise amplitude for the rest of the run on every backend.set_initialtakesresume=Truefor a stored field, which moves the kernel schema to 40: rebuild solprop-native. It moves neither the output schema nor the resume key.DataPulse.from_spectral_densityinterpolates the phase it is given. It used to wrap the phase into the complex spectrum and unwrap it again, which cannot follow a change of more than pi between samples, so a strongly chirped pulse was built wrongly with no warning: 20 000 fs² of group-delay dispersion on a 0.4 nm axis at 800 nm came out 13.5 % away from the converged spectrum, on a 1.6 nm axis 100 %. ADataPulsegiven a complexspectrumstill unwraps it, and its docstring states that limit.A measured field in time puts a pulse where it sat in the record (
TimeDataPulse, above).DataPulse.from_timeunwrapped the phase, and a pulse more than half the record’s length fromt = 0changed its phase by more than pi per bin, so it was moved by one record length with no warning: at 500 fs in a record from 0 to 800 fs it came out at −300 fs.
[0.1.0] - 2026-10-31#
The first public release of solprop and of its compiled backend, solprop-native,
which is released at the same version. Releases are 0.x for now: a minor release may
break the API, the output schema or the resume key, and lists every such break under
“Changed (breaking)” (ADR-058).
Added#
Propagation equations. The unidirectional pulse propagation equation (UPPE) and the generalised nonlinear Schrödinger equation (GNLSE), for the real electric field or a complex envelope, in waveguides and in free space.
Waveguides.
Gas-filled hollow capillaries, with resolved modes (the default) or mode-averaged (
mode_average=Trueonprop_capillaryandprop_waveguide). Tapers, pressure gradients and gas flow are supported, as is a coherent wall that couples the modes.Antiresonant hollow-core fibres (the Vincetti model).
Step-index, graded-index, rectangular and planar guides.
Modes tabulated from arrays or from external mode solvers.
A general modal route for any guide that supplies its own electric and magnetic mode fields, including guides that change along their length.
Free space. Radial, Cartesian and line grids with the exact (non-paraxial) diffraction operator; planar guides can be resolved across their width. Launches, shot noise, energies, fluences and intensities measure the medium’s axial Poynting flux at the plane, so a requested energy is the physical energy there (SEP 34). Far-field and aperture statistics.
Media and responses.
Gases, gas mixtures, glasses and metals from the optmat material library.
Kerr and third-harmonic nonlinearity, including a dispersive electronic response.
Photoionisation with atomic and molecular rate models, and plasma on field and envelope grids.
Rotational and vibrational Raman responses, scalar and vector.
Quantum shot noise, on by default (SEP 37): one photon per mode with a random phase, added to the input field (
noise="input") and recorded with its seed. A resumed run draws the same realisation again, and a scan draws each point’s from a seed of its own.noise=Noneswitches it off andnoise="modified"keeps the realisation apart from the field. The Cartesian, line and hybrid planar grids, slab mode sets, the TDSE response and single precision carry none by default and record why. Spectral densities can subtract the one-photon floor (noise_floor="subtract").An optional helium TDSE atomic response for research. Its native backend is not yet publicly available.
Integrators and their defaults.
Two adaptive integrators: a low-call exponential Adams method (
Multistep()) and an embedded interaction-picture Runge–Kutta pair (RungeKutta()), with a low-storage Runge–Kutta variant and fixed schedules. The linear propagation is integrated exactly.Each interface has its own default integrator and tolerances, chosen by measurement:
RungeKutta()forprop_gnlse(rtol1e-7),prop_waveguide(1e-5) andprop_free(1e-4);Multistep()forprop_capillary(1e-6),prop_radialandprop_planar(1e-5). The choosing-a-stepper page tabulates them and says when to choose another.Multistep(carrier_frame="scalar"), an opt-in carrier frame for the Adams history that is the fastest method measured on dense, multimode, Raman-active runs such as a comb-to-continuum capillary (SEP 35).Backends agree with each other to bounded, rounding-level differences.
Time-window boundaries (SEP 21). Every interface absorbs at the edges of the time window and of the spectrum. The temporal collar lies outside the requested window: the grid widens to
trange / (1 - 2 collar_fraction)(collar_fraction0.1), so the whole request is flat, and transform sizes default tofft_size="auto", the cheapest 2-3-5-smooth size up to the next power of two.absorber=Falseswitches the soft collars off and keeps the same grid. A boundary monitor, on by default (boundary_check=on everyprop_*function andSimulation.from_*constructor), warns (BoundaryWarning) and then stops the run (BoundaryError) when light above the shot-noise floor reaches the window’s edge, and records the energy the collar removed.Coupling simulations (SEP 31).
solprop.couplinghands a field from one simulation to the next. AFieldPlaneholds a coherent field at one plane with its reference and provenance;couplemaps it into another simulation’sInputPortunder a declaredCouplingModeland returns an energy report;transportcarries a free-space plane through lenses, apertures, masks, mirrors, interfaces and spectral filters;Outcouplesynthesises a guide’s exit field. An incident beam launches into a general modal guide with the share of its energy that the guide captures. Each step is admitted against an error budget and refuses what it cannot certify.Backends.
numpy_ref, the NumPy reference, against which every other backend is tested within derived error bounds, andnumba_cpu, compiled with Numba. The backends come in tiers: new features land onnumpy_refandnative;numba_cpuis mature and takes a new feature only where its design says so;mlxis experimental.The optional native backend, the solprop-native distribution: C++20 kernels on Kokkos for multi-core CPUs (OpenMP) and for NVIDIA and AMD GPUs (CUDA, HIP).
pip install "solprop[native]"installs a precompiled CPU wheel on macOS on Apple silicon, Linux on x86-64 and aarch64, and Windows on x86-64.backend="auto"selects it when installed, except on a processor below the wheel’s CPU baseline, where it usesnumba_cpuand warns once (SEP 29). On the native backend the exponential Adams march runs in compiled code, many steps per call (SEP 39).One thread budget per process, which every pool of both CPU backends stays within: the smallest of the CPU affinity mask, the physical cores that hold it, the control group’s CPU quota and
SOLPROP_THREADS(ADR-052).Single precision (SEP 33):
precision="single"holds the state, the transforms and the responses in binary32, with the controller in binary64.numpy_refruns it as the reference, and themlxbackend (pip install "solprop[mlx]") on the Apple GPU. A survival record states which problems keep their accuracy.
Running and recording.
HDF5 output with periodic snapshots and provenance, and resuming an interrupted run, which continues the Adams history.
A summary at the start of every run of what it is about to compute (the code, the machine, the backend, the grid, the medium, the responses, the noise, the integrator, the absorber and the output), and at its end the wall time, CPU time and peak memory. Each execution records its machine and what it spent in the file.
Statistics along the propagation.
Parameter scans on process pools and Slurm clusters.
Analysis and tools.
Processing and plotting. Spectral ranges and band-pass windows are given in the units of any spectral axis (
spec_range,Passband).Ensemble coherence by spectral band, and replay of a run’s accepted steps (
FixedSchedule.from_history), to compare two precisions or backends on one discretisation.Soliton and dispersive-wave design tools.
Pulse compressors: Taylor phase, chirped mirrors, materials, gratings and prisms.
An interactive session through
import solprop.sol as sol.
Performance tracking for developers (SEP 38):
python -m benchmarks tracktimes about a dozen representative workloads, with each interface’s defaults, at one thread and at the production width, and records them by hardware;python -m benchmarks abcompares two builds.Documentation. An introduction with executable lessons, a user guide, a long-form primer on the physics and numerics, focused tutorials and worked examples, the API reference, and a developer’s guide with the design records (SEPs and ADRs).
Changed (breaking)#
Nothing was released before 0.1.0. For scripts and files from a development checkout:
Output files of development versions are not read. The output schema restarts at 1 (
solprop.output.SCHEMA_VERSION); keep the development install that wrote such a file to read it. The kernel contract’s version isKERNEL_SCHEMA_VERSION, separate from it.Defaults changed late in development, so a development script’s results change: shot noise is on; each interface takes its own integrator and tolerances; the temporal collar lies outside the requested window, which widens the grid (
collar_placement="inside"rebuilds the former geometry); transform sizes arefft_size="auto"; and the boundary monitor is on.Renamed or removed.
prop_gnlse,prop_capillary,prop_waveguideand theirSimulation.from_*constructors takewavelengthandshape, notlambda0andpulseshape, andresponses_fortakeswavelength, as the pulses and the design tools already did. The plots’wavelength_rangeisspec_range, in the units of their axis, andsolprop.plotting.style.spectral_limitsis gone (wavelength_to_axisconverts a wavelength range);bandpasstakeslimits, notwavelength_limits;poynting_spectrumis gone (beam_spectrumis the axial-flux spectrum); thesame_basisfunction is gone (usecouple); andsolprop.driverno longer exportspropagate_fixed_rk4, the tests’ fixed-step oracle.Multistep()refuses thePIControllersettings it does not read.
Known limitations#
The
mlxbackend is experimental. Its Metal kernels are tested by hand on a Metal device, not in continuous integration, and its coverage is not measured there. The single-precision survival record is the reference implementation’s (onmlxfor two cases). On the defaultfft_size="auto"grids MLX’s GPU transform runs four to eight times slower than on powers of two, so a run can be slower than binary64native: build single-precision grids withfft_size="pow2". The guide’s speed table predates the current grids and is marked so.Free space has no accurate transverse absorber. By default a radial grid’s edge is a hard wall and a Cartesian or line grid wraps around; the optional real-space collar (an absorber given to a spatial run,
Absorber.spatial) removes only part of the light that reaches it, at a large cost in steps, and the boundary monitor watches the time window only. Size the transverse window so that the light stays clear of its edge. SEP 40 (a draft) rebuilds these boundaries.A run is resumed by the release that wrote it. A run’s configuration hash covers every recorded field, so a release that adds one changes every hash, and within 0.x the output schema may change too: finish a run with the release that started it.
GPU builds are made from source. The wheels are for CPUs; CUDA and HIP builds of solprop-native are built from the repository (building the native backend).
CPython 3.14 only. solprop-native, pyFFTW and h5py have no wheels for 3.15 yet.
macOS: import solprop before pyFFTW. pyFFTW’s macOS wheel starts its own OpenMP runtime, so the native backend refuses to load in a process that imported pyFFTW first; solprop itself imports them in the working order.