solprop.recipe.registry#

From recipe values to the objects a route takes: the registry and the decoder.

A recipe names an object by a type key, which must be an entry of the fixed REGISTRY (SEP 41 section 5.1): a named factory, a constructor, class method or module function, with the units of its keywords declared and any build context it receives declared too. Format 1 defines one context, the route’s time grid, so absorber = {type = "absorber"} builds solprop.boundaries.absorber(grid); on the free-space routes it is the grid keyword itself, and the absorber is a keyword of the settings table. A recipe never imports a module or evaluates code: a type outside the registry is refused, and the Python export is the way out.

decode() turns one value of a recipe into the Python value a keyword takes: quantities into numbers in the keyword’s unit, {re, im} into a complex number, {file, sha256} into an array read from a checked file, a table with a type into an object, and "none" into None where the keyword has that spelling.

Module Attributes

Context

What a factory may be given besides its keywords: format 1 defines the time grid.

UnitSpec

A keyword's unit: one unit string, a rule by element index, or none.

REGISTRY

The types a recipe may name, keyed by the type it writes (SEP 41 section 5.1).

Functions

build_object(table, context, path)

Build the object a table with a type key names.

coerce(target, name, value)

Give a decoded list the container its parameter's annotation asks for.

decode(value, unit, context, path)

Turn one recipe value into the Python value a keyword takes.

decode_keyword(entry, value, context)

Turn the value of one build keyword into what the constructor takes.

unit_of(entry, value)

Return the unit of the form a keyword's value takes.

Classes

BuildContext([base, allow_outside, grid, ...])

What decoding a recipe may read besides the recipe's own values.

Factory(name, target[, units, context, ...])

A registered type: what a type names, and how its keywords are read.

class solprop.recipe.registry.BuildContext(base=None, allow_outside=False, grid=None, embedded=<factory>, mode_fill=False)[source]#

Bases: object

What decoding a recipe may read besides the recipe’s own values.

Attributes:
basepathlib.Path or None

The recipe’s directory, against which data files resolve; None for a recipe from text, which then may hold no data file.

allow_outsidebool

Whether a data file may lie outside base.

gridsolprop.grid.TimeGrid or None

The route’s time grid, for a factory that declares it as its context; None until the route has built one.

embeddedcollections.abc.Mapping

Data files held in memory by the name the recipe refers to them by, as a run’s output embeds them (SEP 41, S2); read before the directory.

mode_fillbool

Whether a supplied mode may name its own fill (Factory.fill): on the planar route, whose slab channels carry the run’s fill. The guided routes rebind every supplied mode to their own fill, so there it may not.

Examples

>>> from solprop.recipe.registry import BuildContext
>>> BuildContext().grid is None
True
allow_outside: bool = False#
base: Path | None = None#
embedded: Mapping[str, bytes]#
grid: TimeGrid | None = None#
mode_fill: bool = False#
solprop.recipe.registry.Context#

What a factory may be given besides its keywords: format 1 defines the time grid.

alias of Literal[‘grid’]

class solprop.recipe.registry.Factory(name, target, units=<factory>, context=(), data_file=None, omitted=(), fill=())[source]#

Bases: object

A registered type: what a type names, and how its keywords are read.

Attributes:
namestr

The type a recipe writes, as "GaussianPulse" or "EnvelopeGrid.from_limits".

targetcallable

The constructor, class method or function it calls.

unitscollections.abc.Mapping

The unit of each keyword that has one (UnitSpec); a keyword not listed is read as it stands.

contexttuple of str

What it receives besides its keywords, passed first and positionally.

data_filestr or None

The keyword a {file, sha256} reference fills with the file’s text, for a loader such as load_spectrum; None otherwise.

omittedtuple of str

Keywords of the target that a recipe may not give, so that they are not part of the format: RungeKutta’s tableau, and a mode’s own fill, which the route replaces with its own.

filltuple of str

A mode’s own fill keywords, which a recipe gives only where the context allows a mode its fill (BuildContext.mode_fill): the slab channels of the planar route, whose fill is the run’s.

Examples

>>> from solprop.recipe.registry import REGISTRY
>>> REGISTRY["GaussianPulse"].units["fwhm"]
's'
context: tuple[Literal['grid'], ...] = ()#
data_file: str | None = None#
fill: tuple[str, ...] = ()#
name: str#
omitted: tuple[str, ...] = ()#
target: Callable[[...], object]#
units: Mapping[str, str | Callable[[int], Unit] | None]#
solprop.recipe.registry.REGISTRY: Final[Mapping[str, Factory]] = {'BoundaryCheck': Factory(name='BoundaryCheck', target=<class 'solprop.monitor.BoundaryCheck'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'CoherentWall': Factory(name='CoherentWall', target=<class 'solprop.modes.capillary.CoherentWall'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'Constant': Factory(name='Constant', target=<class 'solprop.profiles.Constant'>, units={'span': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'EnvelopeGrid.from_limits': Factory(name='EnvelopeGrid.from_limits', target=<bound method EnvelopeGrid.from_limits of <class 'solprop.grid.EnvelopeGrid'>>, units={'length': 'm', 'reference_wavelength': 'm', 'wavelength_limits': 'm', 'trange': 's', 'dt': 's'}, context=(), data_file=None, omitted=(), fill=()), 'EnvelopePlasmaResponse': Factory(name='EnvelopePlasmaResponse', target=<class 'solprop.nonlinear.envelope_plasma.EnvelopePlasmaResponse'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'FieldGrid.from_limits': Factory(name='FieldGrid.from_limits', target=<bound method FieldGrid.from_limits of <class 'solprop.grid.FieldGrid'>>, units={'length': 'm', 'reference_wavelength': 'm', 'wavelength_limits': 'm', 'trange': 's', 'dt': 's'}, context=(), data_file=None, omitted=(), fill=()), 'FixedSchedule': Factory(name='FixedSchedule', target=<class 'solprop.stepper.schedule.FixedSchedule'>, units={'steps': 'm', 'z_start': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'FixedSchedule.uniform': Factory(name='FixedSchedule.uniform', target=<bound method FixedSchedule.uniform of <class 'solprop.stepper.schedule.FixedSchedule'>>, units={'z_start': 'm', 'z_end': 'm', 'dz': 'm', 'save_planes': 'm', 'hard_kinks': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'FreeGrid': Factory(name='FreeGrid', target=<class 'solprop.grid.FreeGrid'>, units={'radius_x': 'm', 'radius_y': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'GaussianPulse': Factory(name='GaussianPulse', target=<class 'solprop.pulses.GaussianPulse'>, units={'wavelength': 'm', 'energy': 'J', 'peak_power': 'W', 'phase': <function phase_unit>, 't0': 's', 'fwhm': 's', 'width': 's'}, context=(), data_file=None, omitted=(), fill=()), 'KerrResponse': Factory(name='KerrResponse', target=<class 'solprop.nonlinear.responses.KerrResponse'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'LineGrid': Factory(name='LineGrid', target=<class 'solprop.grid.LineGrid'>, units={'radius_x': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'Linear': Factory(name='Linear', target=<class 'solprop.profiles.Linear'>, units={'length': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'LowStorageRungeKutta': Factory(name='LowStorageRungeKutta', target=<class 'solprop.stepper.low_storage.LowStorageRungeKutta'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'MarcatiliMode': Factory(name='MarcatiliMode', target=<class 'solprop.modes.capillary.MarcatiliMode'>, units={'radius': 'm', 'phi': 'rad'}, context=(), data_file=None, omitted=('gas', 'pressure', 'temperature', 'susceptibility', 'mixture'), fill=()), 'MaterialSelection': Factory(name='MaterialSelection', target=<class 'solprop.physdata.selection.MaterialSelection'>, units={'carrier_wavelength': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'Mixture.air': Factory(name='Mixture.air', target=<bound method Mixture.air of <class 'solprop.physdata.mixtures.Mixture'>>, units={'pressure': 'bar', 'temperature': 'K', 'floor': 'bar', 'length': 'm', 'radius': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'Mixture.fill': Factory(name='Mixture.fill', target=<bound method Mixture.fill of <class 'solprop.physdata.mixtures.Mixture'>>, units={'pressure': 'bar', 'temperature': 'K', 'floor': 'bar', 'length': 'm', 'radius': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'Mixture.partial_pressures': Factory(name='Mixture.partial_pressures', target=<bound method Mixture.partial_pressures of <class 'solprop.physdata.mixtures.Mixture'>>, units={'temperature': 'K'}, context=(), data_file=None, omitted=(), fill=()), 'ModeCollection.slab': Factory(name='ModeCollection.slab', target=<bound method ModeCollection.slab of <class 'solprop.modes.collection.ModeCollection'>>, units={'half_gap': 'm', 'pressure': 'bar', 'temperature': 'K'}, context=(), data_file=None, omitted=('susceptibility', 'mixture'), fill=('gas', 'pressure', 'temperature')), 'Multistep': Factory(name='Multistep', target=<class 'solprop.stepper.multistep.Multistep'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'OptmatRate': Factory(name='OptmatRate', target=<class 'solprop.nonlinear.optmat_rates.OptmatRate'>, units={'wavelength': 'm', 'negligible_rate': 's^-1', 'maximum': 's^-1'}, context=(), data_file=None, omitted=(), fill=()), 'PIController': Factory(name='PIController', target=<class 'solprop.stepper.controller.PIController'>, units={}, context=(), data_file=None, omitted=('weights', 'lag', 'estimator_order'), fill=()), 'PiecewiseLinear': Factory(name='PiecewiseLinear', target=<class 'solprop.profiles.PiecewiseLinear'>, units={'positions': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'PlanarGrid': Factory(name='PlanarGrid', target=<class 'solprop.modes.slab.PlanarGrid'>, units={'radius_y': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'PlanarPulse': Factory(name='PlanarPulse', target=<class 'solprop.pulses.PlanarPulse'>, units={'waist': 'm', 'propz': 'm', 'displacement': 'm', 'tilt': 'rad', 'waist_x': 'm', 'offset_x': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'PlasmaResponse': Factory(name='PlasmaResponse', target=<class 'solprop.nonlinear.responses.PlasmaResponse'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'PressureGradient.from_stages': Factory(name='PressureGradient.from_stages', target=<bound method PressureGradient.from_stages of <class 'solprop.profiles.PressureGradient'>>, units={'length': 'm', 'start_bar': 'bar', 'end_bar': 'bar', 'floor': 'bar', 'radius': 'm', 'positions': 'm', 'pressures': 'bar'}, context=(), data_file=None, omitted=(), fill=()), 'PressureGradient.two_point': Factory(name='PressureGradient.two_point', target=<bound method PressureGradient.two_point of <class 'solprop.profiles.PressureGradient'>>, units={'length': 'm', 'start_bar': 'bar', 'end_bar': 'bar', 'floor': 'bar', 'radius': 'm', 'positions': 'm', 'pressures': 'bar'}, context=(), data_file=None, omitted=(), fill=()), 'RadialGrid': Factory(name='RadialGrid', target=<class 'solprop.grid.RadialGrid'>, units={'radius': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'RectMode': Factory(name='RectMode', target=<class 'solprop.modes.rectangular.RectMode'>, units={'half_width_x': 'm', 'half_width_y': 'm'}, context=(), data_file=None, omitted=('gas', 'pressure', 'temperature', 'susceptibility', 'mixture'), fill=()), 'RungeKutta': Factory(name='RungeKutta', target=<class 'solprop.stepper.runge_kutta.RungeKutta'>, units={}, context=(), data_file=None, omitted=('tableau',), fill=()), 'SechPulse': Factory(name='SechPulse', target=<class 'solprop.pulses.SechPulse'>, units={'wavelength': 'm', 'energy': 'J', 'peak_power': 'W', 'phase': <function phase_unit>, 't0': 's', 'fwhm': 's', 'width': 's'}, context=(), data_file=None, omitted=(), fill=()), 'SimulationSettings': Factory(name='SimulationSettings', target=<class 'solprop.driver.simulation.SimulationSettings'>, units={'save_planes': 'm', 'dz_max': 'm', 'dz_init': 'm', 'dz_min': 'm', 'evanescent_rate': 'm^-1'}, context=(), data_file=None, omitted=('fields', 'field_planes', 'resume_history', 'auto_init', 'density', 'prefactor_index', 'quadrature', 'components', 'adiabatic_accuracy', 'tdse_execution', '_default_integrator', '_route', '_default_controller'), fill=()), 'SlabMode': Factory(name='SlabMode', target=<class 'solprop.modes.slab.SlabMode'>, units={'half_gap': 'm', 'pressure': 'bar', 'temperature': 'K'}, context=(), data_file=None, omitted=('susceptibility', 'mixture'), fill=('gas', 'pressure', 'temperature')), 'SpatialAbsorber': Factory(name='SpatialAbsorber', target=<class 'solprop.spatial_absorber.SpatialAbsorber'>, units={'kappa': 'rad/m', 'angle': 'rad', 'discard_above': 'rad/m'}, context=(), data_file=None, omitted=(), fill=()), 'SpatialMedium': Factory(name='SpatialMedium', target=<class 'solprop.spatial.SpatialMedium'>, units={'pressure': 'bar', 'temperature': 'K'}, context=(), data_file=None, omitted=('susceptibility',), fill=()), 'SpatialPulse': Factory(name='SpatialPulse', target=<class 'solprop.pulses.SpatialPulse'>, units={'waist': 'm', 'propz': 'm', 'displacement': 'm', 'tilt': 'rad'}, context=(), data_file=None, omitted=(), fill=()), 'StatsSpec': Factory(name='StatsSpec', target=<class 'solprop.stats.StatsSpec'>, units={}, context=(), data_file=None, omitted=(), fill=()), 'TubeLattice': Factory(name='TubeLattice', target=<class 'solprop.modes.antiresonant.TubeLattice'>, units={'wall_thickness': 'm', 'tube_radius': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'absorber': Factory(name='absorber', target=<function absorber>, units={'boundary_length': 'm', 'walkoff': 's'}, context=('grid',), data_file=None, omitted=(), fill=()), 'default_spec': Factory(name='default_spec', target=<function default_spec>, units={}, context=(), data_file=None, omitted=(), fill=()), 'gamma3': Factory(name='gamma3', target=<function gamma3>, units={'wavelength': 'm'}, context=(), data_file=None, omitted=(), fill=()), 'load_spectrum': Factory(name='load_spectrum', target=<function load_spectrum>, units={'energy': 'J'}, context=(), data_file='path', omitted=(), fill=())}#

The types a recipe may name, keyed by the type it writes (SEP 41 section 5.1).

The documented user-facing types of the gnlse, capillary, waveguide, radial, free and planar routes. Every keyword of an entry is part of recipe format 1, so the registry starts small: adding an entry later is additive, removing one is not. RungeKutta takes no tableau in a recipe, whose default is the only one the interfaces document. The supplied modes (MarcatiliMode, RectMode, SlabMode and ModeCollection.slab) take their geometry, cladding and loss and not their fill, which the route sets; on the planar route a slab mode’s fill is the run’s, so there it names one (Factory.fill). A guide built from a provider has no table form: its knots and node rule are derived from the run’s grid and the provider’s own method.

The free-space routes register their grids (RadialGrid, FreeGrid, LineGrid, PlanarGrid), the medium (SpatialMedium, without a supplied susceptibility), the responses the free-space guide uses (KerrResponse, with gamma3 for optmat’s coefficient of a gas, EnvelopePlasmaResponse, PlasmaResponse and OptmatRate), the beams (SpatialPulse, PlanarPulse), their settings (SimulationSettings without the execution settings, which are [execution]’s, nor the fields a route’s builder sets; PIController without its per-bin weights, history lag and estimator order; default_spec) and the transverse boundary (SpatialAbsorber).

solprop.recipe.registry.UnitSpec = str | collections.abc.Callable[[int], solprop.recipe.quantity.Unit] | None#

A keyword’s unit: one unit string, a rule by element index, or none.

solprop.recipe.registry.build_object(table, context, path)[source]#

Build the object a table with a type key names.

Parameters:
tablecollections.abc.Mapping

The table: type, the factory’s keywords and, for a loader, file and sha256.

contextBuildContext

The recipe’s directory and the route’s grid.

pathstr

The table’s path.

Returns:
object

What the factory returns.

Raises:
RecipeError

If the type is not registered, a keyword is not the factory’s, or the factory needs a grid that is not yet built.

Examples

>>> from solprop.recipe.registry import BuildContext, build_object
>>> pulse = build_object(
...     {"type": "GaussianPulse", "wavelength": "800 nm", "fwhm": "30 fs",
...      "energy": "1 mJ"},
...     BuildContext(),
...     "pulses[0]",
... )
>>> pulse.fwhm
3e-14
solprop.recipe.registry.coerce(target, name, value)[source]#

Give a decoded list the container its parameter’s annotation asks for.

TOML has lists only; a parameter typed as a tuple takes a tuple and one typed as an array takes an array, so a value reaches the constructor as a script passes it.

Parameters:
targetcallable

The constructor or factory.

namestr

The parameter.

valueobject

The decoded value.

Returns:
object

value, as a tuple or a float array where the annotation asks for one.

Examples

>>> from solprop.grid import EnvelopeGrid
>>> from solprop.recipe.registry import coerce
>>> coerce(EnvelopeGrid.from_limits, "wavelength_limits", [4e-7, 2e-6])
(4e-07, 2e-06)
solprop.recipe.registry.decode(value, unit, context, path)[source]#

Turn one recipe value into the Python value a keyword takes.

Parameters:
valueobject

The value as TOML read it.

unitstr, callable or None

The keyword’s unit (UnitSpec).

contextBuildContext

The recipe’s directory and the route’s grid.

pathstr

The value’s parameter path, for errors.

Returns:
object

A number in the keyword’s unit, a complex number, an array, an object, a list of these, or the value as it stands.

Examples

>>> from solprop.recipe.registry import BuildContext, decode
>>> decode("800 nm", "m", BuildContext(), "wavelength")
8e-07
>>> decode({"re": 1.45, "im": 0.0}, None, BuildContext(), "cladding")
(1.45+0j)
solprop.recipe.registry.decode_keyword(entry, value, context)[source]#

Turn the value of one build keyword into what the constructor takes.

Parameters:
entryParameter

The keyword’s metadata.

valueobject

The value as TOML read it.

contextBuildContext

The recipe’s directory and the route’s grid.

Returns:
object

The decoded value; None for "none" where the keyword has the spelling.

Raises:
RecipeError

If the keyword’s forms cannot hold the value in a recipe.

Examples

>>> from solprop.recipe.registry import BuildContext, decode_keyword
>>> from solprop.recipe.metadata import parameters
>>> table = parameters("capillary")
>>> decode_keyword(table["pressure"], ["0 bar", "1.2 bar"], BuildContext())
[0.0, 1.2]
>>> decode_keyword(table["noise"], "none", BuildContext()) is None
True
solprop.recipe.registry.unit_of(entry, value)[source]#

Return the unit of the form a keyword’s value takes.

Parameters:
entryParameter

The keyword’s metadata.

valueobject

The value as TOML read it.

Returns:
str, callable or None

The unit of the matching form, or the element rule of an array form.