Key processes¶
A run¶
Simulation.Run(system, run):
Compiler.Compilegives aCompiledRun.Executor.Executegives aRunResult.- The CLI or Python writes
summary.json,result.npzand CSVs.
Compile¶
In Compiler.Compile, in order:
- Check keys against
Schema(ULTRAFAST_STRICT_OPTIONS=1makes unknown keys errors). - Set up the air, and start beam tracking if there is a Gaussian
inputBeam. SystemGraph.Build: connect ports, order the elements, and verify. Its alerts join the warnings.- For each element, in graph order,
CompileElement. It reads the keys, and fillsPreandPost(linear terms),NonlinearorActive. In a Branched system each element starts from a copy of the tracked beam on its first linked input. - Work out probes and instrument locations.
BuildGridAndPulse: plan the grid and build the input pulse.- Pick the field mode of each nonlinear segment.
Material numbers: from a catalogue file to a run note¶
Catalogue(static, lazy) loads the embeddedData/materials/**/*.json, then the user's files ($ULTRAFAST_MATERIALS, default~/.ultrafast/materials); a user quantity replaces the built-in one and adds a notice.MaterialLibrary.BuiltIn(name)finds the entry (aliases included) and builds aMaterialfrom its quantities (CatalogueModels.Index,.Raman); the quantities it used are kept inMaterial.Used. An override (MaterialLibrary.Apply:n2,index,raman, ...) removes the quantity fromUsed.- The compiler, where an element really uses a property (
CompileBulk: the dispersion, and for a nonlinear element n2, the Raman response, the Kerr anisotropy, the rotational Raman data, the ionisation data), callsProvenanceLog.UseCatalogue. That puts the status and source intoprovenance.jsonand, for a value that is notverified(or comes from a user file), once into the run notes andProvenanceLog.UnverifiedInputs. RunResult.SummaryJsonwrites the list asunverifiedInputs. Gain media (GainMedium.Used, reported inGainCompiler) and crystals (CrystalModel.Used, inCrystalElement.Compile) follow the same path.- At the end of
Compiler.Compile,physics.strictMaterials(orULTRAFAST_STRICT_MATERIALS=1) turns every non-verified entry ofUnverifiedInputsinto oneModelException.
Execute: one element¶
Executor.RunElement(state, index, element, pulse, path), for a pulse on one path:
Prelinear terms.Active.Execute, or the nonlinear segment.Postlinear terms.- Add the element's phase and delay to the
PathLedger; record energies, losses, beam sizes and damage margins in anElementRecord.
A Sequential system calls this for each element in turn, in the loop in Executor.Execute.
A pumped gain element and its ASE¶
GainElement.Execute / AmplifierElement.Execute build a GainCrystal (N2 per slice and transverse cell), optionally attach an AseModel (aseModel), then
reach the state the pulse sees: GainRun.Train iterates the pulse train with the energy model (FastPass), each interval through GainRun.Between -> PumpCw / Decay. With an AseModel
every pumping or decay step asks AseModel.Rates(N2, N_t): per cell and frequency, forward and backward intensity through the slices at frozen N2 (exact slice step), the stimulated photons of each slice divided
by N2 are an extra decay rate of that slice (sub-stepped so the rate stays small within a step). GainRun.RecordAse then evaluates ModeOutput (the ASE in the signal mode) and the element reports the pedestal
strength. The pulse itself is amplified by Pass1D / PassSpatial as before; a stress map (ApplyStressPattern) acts on the spatial field after the gain.
Execute: a Branched system¶
Executor.RunGraph:
- Queue the run's pulse at the first element's default port, and virtual beams at theirs.
- Run the graph's stages in order.
RunNodetakes what is queued on each input port, merges it (Packets.Merge), callsRunElementonce per packet, and delivers each output to its link. - A multi-port element gives its other outputs as by-products named after their ports, so
RunElementneeds no special case.ActiveContext.InputPortsays which input it is on. - Output with no link goes to a virtual output.
- The strongest packet at
endbecomesRunResult.Output, and the budget is added up over the graph.graph.energyOutJcounts every packet atend.
A crystal's input angle comes from the arms: the compiler tracks a carrier and an x tilt per output port (PortOmega, PortTiltX). ThreeWave.At works out the directions, and each output port's path leaves with its own tilt.
Interference is not a step of its own. An element sees one packet at a time, which is exact for a linear element, and fields add when the packets on one port merge. Crystals are the exception: beams on their ports run together, because they mix.
A loop¶
Executor.RunLoop repeats a loop's stage. A packet sent back to an earlier element waits in that element's queue for the next pass, so no edge needs cutting. It stops by energy (ringDown), when shape and energy both repeat (steadyState), or at maxRoundTrips, and books what is left.
The frame and the ledger¶
A linear term removes its carrier phase φ₀ and group delay τ₀ from the field, so a pulse stays centred in its window. PhaseTerm.DispersionAt reports them in DispersionPoint.FramePhaseRad and FrameDelayS, and PathLedger.Add keeps them:
true field = stored field × exp(i (φ + (ω − ω₀) τ))
When packets merge, each is moved to a common frame by the difference of their ledgers. Tracked Gaussians take their mode overlap (Packets.Overlap); carried profiles add point by point (TransverseOps.Expand, CollapseVector). Active elements report phase through ctx.Own, and ctx.PathPhaseFactor scales it when the carrier changes.
Beam size and quality¶
A carried profile's size and M² are central moments: SecondMomentRadius measures about the centroid, and BeamQuality removes the mean tilt and the fitted curvature first. Curvature fits the phase step between neighbouring samples, so a vortex or the sign change between lobes does not disturb it.
Narrow lines and the tail¶
With a tail, Executor.SplitNarrow divides a solver's background through LinearTerm.TrySplitNarrow: the smooth part stays in the solver, and half of the narrow part goes exactly on main + tail before and after it. ApplyLinear sends polarisation-mixing terms of a vector profile to the full field, and everything else to the ledger.
Grids¶
GridPlanner.Plan sizes the time window for the pulse, the dispersion it will meet and satellites (Compiler.SatelliteRoom). An element that changes the carrier asks for a new grid through ActiveContext.DownstreamGrid.
Studies¶
Studies compile a variant per point. In a Sequential system, when only later elements change, Executor.ExecuteFrom reuses the earlier ones (prefix cache). In a Branched system the nominal run fills a GraphCache, keyed by a hash of everything feeding each element, and each point replays what matches. arrayOutputs keep instrument arrays per point.
Adding things¶
| to add | do |
|---|---|
| an option | declare it in Schema/BuiltIn*.cs, read it in the compiler, test, /regen, document |
| an element | add a spec, add its name to Compiler.ElementTypes, add a case in lower case in CompileElement |
| ports | .Ports(inputs, outputs) on the spec; implement IMultiPortElement; give other outputs by ctx.Byproduct(port, pulse) |
| a carrier phase | implement IReportsCarrierPhase and report through ctx.Own; otherwise paths through the element have PhaseKnown = false |