solprop.capabilities#

What solprop can do: for a route, a species and a results file (SEP 41, S8, S16).

A front end asks route() what a route takes: its keywords, the values of each enumerated one, and the combination rules its builder enforces, each with an id and a reason. Every refusal that enforces a rule raises with that id as its rule and the keywords as its parameter (SolpropError), and a test holds the raise sites to RULES. A refusal that depends on a value (grid coverage, a node budget, a material’s validity range) is not a rule: check() finds it.

It asks species() what a built-in gas brings: its ionisation models and Raman response with optmat’s status of each, and the ranges optmat records for it.

A front end that opens a results file asks output() what it may offer: the file’s basis, and for each derived quantity whether it is a physical flux or a coordinate quantity; which planes keep their field; which operations a reader admits and which it refuses, with the refusal the reader itself gives; and whether the file records a recipe and can continue. The refusals come from the same gates the readers raise from (solprop.output.capability.refusal_for(), reading_refusal() and the compressors’ basis check), so the statement and the refusal cannot drift. An operation is a statistic or an accessor of the run; the processing and plotting readers that would read a spatial file’s columns as modes are stated apart, in refused_readers, because energy the statistic is a physical flux there and energy the reader is refused.

Module Attributes

QuantityKind

Whether a derived quantity is a physical flux, or a quantity of the coordinates.

OPERATIONS

The operations a statement covers: the accessors whose admission depends on the basis, and compression, which needs orthogonal modal powers.

RULES

Every combination rule the builders enforce, by id.

Functions

output(source)

Say what a results file can give a reader (SEP 41, S16).

route(name)

Say what a route takes (SEP 41, S8).

species(name)

Say what a built-in gas brings to a run (SEP 41, S8).

Classes

IonisationModel(name, status, reason)

One ionisation-rate model optmat has for a species.

OutputCapabilities(basis, simulation_kind, ...)

What a results file can give a reader.

Quantity(kind, unit)

What a derived quantity of a file is.

RouteCapabilities(route, keywords, choices, ...)

What a route takes: its keywords, their choices and its combination rules.

Rule(id, keywords, reason, routes[, ...])

A combination of keywords, route or species that a builder refuses.

SpeciesCapabilities(name, molecule, ...)

What a built-in gas brings to a run, with optmat's status of each part.

class solprop.capabilities.IonisationModel(name, status, reason)[source]#

Bases: object

One ionisation-rate model optmat has for a species.

Attributes:
namestr

The name a selection gives it (MaterialSelection(ionisation=name)): "recommended" for optmat’s default, or a literature form.

statusstr

optmat’s status of the model, one of solprop.physdata.selection.STATUSES.

reasonstr

optmat’s reason for the status, where it gives one.

name: str#
reason: str#
status: str#
solprop.capabilities.OPERATIONS: Final = ('energy', 'peak_power', 'peak_intensity', 'physical_energy', 'reconstructed_flux', 'electric_field', 'compression')#

The operations a statement covers: the accessors whose admission depends on the basis, and compression, which needs orthogonal modal powers.

class solprop.capabilities.OutputCapabilities(basis, simulation_kind, quantities, field_planes, statistics_planes, refused, refused_readers, needs, recipe, resumability)[source]#

Bases: object

What a results file can give a reader.

Attributes:
basis{‘modal’, ‘general_modal’, ‘spatial’}

The file’s basis.

simulation_kindstr

The kind of run, as its record names it.

quantitiescollections.abc.Mapping

Quantity for power, energy, spectral_density and field.

field_planestuple of int

The saved planes that keep their field.

statistics_planestuple of int

The saved planes that keep only their statistics row.

refusedcollections.abc.Mapping

For each of OPERATIONS the file’s basis refuses, its refusal. A spatial file admits energy: its statistic is the beam’s physical energy.

refused_readerscollections.abc.Mapping

For each processing or plotting reader of COLUMN_READINGS that refuses the file, its refusal: on a spatial file every one of them, which would read its columns as modes, and on a general modal file the legacy energy and peak_power.

needscollections.abc.Mapping

For an operation a reader admits only with something the file does not hold, what it needs: a general modal file’s reconstruction needs its guide and reference tables, of which it holds the identities.

recipebool

Whether the file records the recipe it was built from.

resumabilitysolprop.output.resumable.Resumability or None

Whether it can continue, for a file given by its path.

admits(operation)[source]#

Return whether a reader admits an operation on this file.

Parameters:
operationstr

One of OPERATIONS.

Returns:
bool

False when it is refused.

basis: Literal['modal', 'general_modal', 'spatial']#
field_planes: tuple[int, ...]#
needs: dict[str, str]#
quantities: dict[str, Quantity]#
recipe: bool#
refused: dict[str, str]#
refused_readers: dict[str, str]#
resumability: Resumability | None#
simulation_kind: str#
statistics_planes: tuple[int, ...]#
class solprop.capabilities.Quantity(kind, unit)[source]#

Bases: object

What a derived quantity of a file is.

Attributes:
kind{‘physical’, ‘coordinate’}

A physical flux, which sums to the pulse’s power and energy, or a quantity of the basis’s coordinates, which does not on a basis with cross terms.

unitstr

Its unit.

kind: Literal['physical', 'coordinate']#
unit: str#
solprop.capabilities.QuantityKind#

Whether a derived quantity is a physical flux, or a quantity of the coordinates.

alias of Literal[‘physical’, ‘coordinate’]

solprop.capabilities.RULES: Final[Mapping[str, Rule]] = mappingproxy({'grid-or-limits': Rule(id='grid-or-limits', keywords=('grid', 'wavelength_limits', 'trange'), reason="A run's grid is given, or built from wavelength_limits and trange: one way, and complete.", routes=('gnlse', 'capillary'), species_condition=None), 'pulses-or-pulse-keywords': Rule(id='pulses-or-pulse-keywords', keywords=('pulses', 'fwhm', 'width', 'energy', 'peak_power', 'phases', 'shape'), reason='pulses= describes every input pulse itself; the single-pulse keywords are then left unset.', routes=('gnlse', 'capillary'), species_condition=None), 'gauss-fwhm': Rule(id='gauss-fwhm', keywords=('fwhm', 'width', 'shape'), reason='A Gaussian input is specified by its intensity fwhm; width is the natural width of a sech**2 pulse.', routes=('gnlse', 'capillary'), species_condition=None), 'fwhm-or-width': Rule(id='fwhm-or-width', keywords=('fwhm', 'width'), reason='A sech**2 input takes exactly one of fwhm and width.', routes=('gnlse', 'capillary'), species_condition=None), 'energy-or-peak-power': Rule(id='energy-or-peak-power', keywords=('energy', 'peak_power'), reason='An input pulse takes exactly one of energy and peak_power.', routes=('gnlse', 'capillary'), species_condition=None), 'launch-per-pulse': Rule(id='launch-per-pulse', keywords=('launch', 'pulses'), reason='launch= is one mapping for every pulse, or a list of one mapping per pulse.', routes=('capillary', 'waveguide'), species_condition=None), 'launch-or-channel': Rule(id='launch-or-channel', keywords=('launch', 'channel'), reason='launch= generalises channel=, so a run names its modes with one of them.', routes=('waveguide',), species_condition=None), 'launch-or-incident': Rule(id='launch-or-incident', keywords=('launch', 'incident', 'coupling'), reason='An incident beam coupled under a model computes its own launch, so it is not given beside launch=.', routes=('waveguide',), species_condition=None), 'save-planes-or-n-save': Rule(id='save-planes-or-n-save', keywords=('save_planes', 'n_save'), reason='The saved planes are given as positions or as a count, not both.', routes=('gnlse', 'capillary'), species_condition=None), 'fields-planes-step-planes': Rule(id='fields-planes-step-planes', keywords=('fields', 'step_planes'), reason="fields='planes' names saved planes by index, and step_planes adds the march's own steps, so the plane count is not known in advance.", routes=('gnlse', 'capillary'), species_condition=None), 'absorber-scheme-needs-absorber': Rule(id='absorber-scheme-needs-absorber', keywords=('absorber', 'absorber_scheme'), reason='absorber=False switches the temporal collar off, leaving nothing for absorber_scheme to apply.', routes=('gnlse', 'capillary'), species_condition=None), 'seed-needs-noise': Rule(id='seed-needs-noise', keywords=('seed', 'noise'), reason='A noise seed needs a noise model to draw.', routes=('gnlse', 'capillary'), species_condition=None), 'low-storage-needs-schedule': Rule(id='low-storage-needs-schedule', keywords=('integrator', 'schedule'), reason='The low-storage march replays a prescribed schedule.', routes=('gnlse', 'capillary'), species_condition=None), 'low-storage-no-interpolation': Rule(id='low-storage-no-interpolation', keywords=('integrator', 'interpolate_planes'), reason="The low-storage march lands on its schedule's edges and has no dense output.", routes=('gnlse', 'capillary'), species_condition=None), 'integrator-backends': Rule(id='integrator-backends', keywords=('integrator', 'backend'), reason='The Multistep and low-storage integrators run on numpy_ref and native only.', routes=('gnlse', 'capillary'), species_condition=None), 'gnlse-envelope-grid': Rule(id='gnlse-envelope-grid', keywords=('grid',), reason="The GNLSE route propagates an envelope grid; a field-resolved run is the capillary route's.", routes=('gnlse',), species_condition=None), 'betas-beta-or-fibre': Rule(id='betas-beta-or-fibre', keywords=('betas', 'beta', 'fibre'), reason='The dispersion is given exactly once: as betas, as beta or by a fibre.', routes=('gnlse',), species_condition=None), 'gamma-or-n2': Rule(id='gamma-or-n2', keywords=('gamma', 'n2'), reason='The nonlinearity is given exactly once: as gamma or as n2.', routes=('gnlse',), species_condition=None), 'n2-needs-fibre-area': Rule(id='n2-needs-fibre-area', keywords=('n2', 'fibre'), reason='n2 needs a fibre that carries an effective area.', routes=('gnlse',), species_condition=None), 'fibre-carries-loss': Rule(id='fibre-carries-loss', keywords=('fibre', 'loss_db_per_m'), reason='A fibre carries its own loss, which is scaled on the mode.', routes=('gnlse',), species_condition=None), 'gnlse-carrier-kerr': Rule(id='gnlse-carrier-kerr', keywords=('materials',), reason="The GNLSE route keeps a constant gamma, so a selection's dispersive electronic policy is refused.", routes=('gnlse',), species_condition=None), 'raman-needs-kerr': Rule(id='raman-needs-kerr', keywords=('raman', 'kerr'), reason='A medium with a Raman response has an electronic one too, and on a field grid the Raman drive reaches the kernel through the Kerr term.', routes=('capillary',), species_condition=None), 'raman-needs-molecule': Rule(id='raman-needs-molecule', keywords=('raman', 'gas'), reason='A Raman response asked for explicitly needs a molecule: an atom has no Raman-active mode.', routes=('capillary',), species_condition='a molecule optmat curates'), 'capillary-needs-nonlinearity': Rule(id='capillary-needs-nonlinearity', keywords=('kerr', 'plasma'), reason='A capillary run with neither kerr nor plasma has no nonlinearity.', routes=('capillary',), species_condition=None), 'envelope-plasma-no-thg': Rule(id='envelope-plasma-no-thg', keywords=('envelope', 'plasma', 'thg'), reason='Envelope plasma cannot be combined with the third harmonic.', routes=('capillary',), species_condition=None), 'plasma-integration-field-grid': Rule(id='plasma-integration-field-grid', keywords=('plasma_integration', 'envelope'), reason="plasma_integration chooses the field-grid plasma column's rule; envelope plasma takes none.", routes=('capillary',), species_condition=None), 'plasma-integration-needs-plasma': Rule(id='plasma-integration-needs-plasma', keywords=('plasma_integration', 'plasma'), reason='plasma_integration needs the plasma on.', routes=('capillary',), species_condition=None), 'mixture-carries-pressure': Rule(id='mixture-carries-pressure', keywords=('gas', 'pressure', 'pressure_floor', 'vacuum_end'), reason='A Mixture carries its own pressure paths.', routes=('capillary',), species_condition='gas given as a Mixture'), 'mixture-carries-susceptibility': Rule(id='mixture-carries-susceptibility', keywords=('gas', 'susceptibility'), reason="A Mixture, a fraction mapping and air carry their species' susceptibilities.", routes=('capillary',), species_condition='gas given as a Mixture, a fraction mapping or air'), 'scalar-capillary-polarisation': Rule(id='scalar-capillary-polarisation', keywords=('polarisation',), reason='The capillary route is scalar and propagates one transverse component per mode; the polarisation is declared in the modes.', routes=('capillary',), species_condition=None), 'lattice-builds-modes': Rule(id='lattice-builds-modes', keywords=('lattice', 'modes'), reason='A tube lattice shapes the modes the route builds; given modes carry their own geometry.', routes=('capillary',), species_condition=None), 'lattice-full-model': Rule(id='lattice-full-model', keywords=('lattice', 'model'), reason='A tube lattice needs the full capillary model.', routes=('capillary',), species_condition=None), 'wall-keeps-taper-coupling': Rule(id='wall-keeps-taper-coupling', keywords=('wall', 'taper_coupling'), reason='A coherent wall block always carries the taper connection.', routes=('capillary',), species_condition=None), 'wall-cf4-numpy-ref': Rule(id='wall-cf4-numpy-ref', keywords=('wall', 'backend'), reason="A coherent wall with method='cf4' runs on numpy_ref alone.", routes=('capillary',), species_condition=None), 'wall-is-the-loss': Rule(id='wall-is-the-loss', keywords=('wall', 'loss'), reason='A coherent wall block is the loss model, so loss stays True beside it.', routes=('capillary',), species_condition=None), 'wall-full-model': Rule(id='wall-full-model', keywords=('wall', 'model'), reason='A coherent wall block needs the full capillary model.', routes=('capillary',), species_condition=None), 'wall-no-lattice': Rule(id='wall-no-lattice', keywords=('wall', 'lattice'), reason='A coherent wall block assumes one uniform wall, not a tube lattice.', routes=('capillary',), species_condition=None), 'mode-average-one-mode': Rule(id='mode-average-one-mode', keywords=('mode_average', 'modes'), reason='A mode-averaged run has exactly one mode.', routes=('capillary',), species_condition=None), 'mode-average-no-quadrature': Rule(id='mode-average-no-quadrature', keywords=('mode_average', 'n_r', 'n_theta', 'radial_rule'), reason='A mode-averaged run has no transverse rule to shape.', routes=('capillary',), species_condition=None), 'tdse-helium': Rule(id='tdse-helium', keywords=('tdse', 'gas'), reason="The TDSE response is helium's: the fill is optmat's helium alone, not a mixture and not preionised.", routes=('capillary',), species_condition='helium alone'), 'tdse-owns-electronic': Rule(id='tdse-owns-electronic', keywords=('tdse', 'kerr', 'mode_average', 'plasma', 'raman', 'preionisation', 'plasma_integration', 'ionisation_field_range', 'thg'), reason='The TDSE response is the whole electronic response, so the keywords of the others are left unset beside it.', routes=('capillary',), species_condition=None), 'tdse-execution-needs-tdse': Rule(id='tdse-execution-needs-tdse', keywords=('tdse_execution', 'tdse'), reason='tdse_execution is the resources of a TDSE response, which tdse declares.', routes=('capillary',), species_condition=None), 'tdse-double-precision': Rule(id='tdse-double-precision', keywords=('tdse_execution', 'precision'), reason='No backend runs the TDSE response in binary32.', routes=('capillary',), species_condition=None), 'tdse-numpy-ref': Rule(id='tdse-numpy-ref', keywords=('tdse_execution', 'backend'), reason='The TDSE response runs on numpy_ref alone, named explicitly.', routes=('capillary',), species_condition=None), 'tdse-multistep': Rule(id='tdse-multistep', keywords=('tdse_execution', 'integrator'), reason='The TDSE response steps with the Multistep integrator.', routes=('capillary',), species_condition=None)})#

Every combination rule the builders enforce, by id.

A test holds it to the source: every rule= given to an exception names a rule here, and every rule here is given at least once.

class solprop.capabilities.RouteCapabilities(route, keywords, choices, rules)[source]#

Bases: object

What a route takes: its keywords, their choices and its combination rules.

Attributes:
routestr

The route.

keywordstuple of str

Its build keywords, in the constructor’s order (solprop.recipe.metadata.parameters()).

choicescollections.abc.Mapping

For each enumerated keyword, its allowed values, the "none" spelling included where the keyword takes it.

rulestuple of Rule

The combination rules its builder enforces.

choices: Mapping[str, tuple[str, ...]]#
keywords: tuple[str, ...]#
route: str#
rules: tuple[Rule, ...]#
rules_for(keyword)[source]#

Return the rules that concern a keyword.

Parameters:
keywordstr

A build keyword of the route.

Returns:
tuple of Rule

The rules naming it, in RULES’ order.

class solprop.capabilities.Rule(id, keywords, reason, routes, species_condition=None)[source]#

Bases: object

A combination of keywords, route or species that a builder refuses.

Attributes:
idstr

A stable short identifier in kebab case, which the refusal carries as its rule.

keywordstuple of str

The keywords the rule concerns: build keywords of its routes, or keys of the [execution] table. A refusal’s parameter names those of them it found set, or missing.

reasonstr

Why the combination is refused, in a sentence a front end can show.

routestuple of str

The routes whose builders enforce it.

species_conditionstr or None

For a rule that depends on the fill, the species or form of gas it concerns; None for one that does not.

Examples

>>> from solprop.capabilities import RULES
>>> RULES["raman-needs-kerr"].keywords
('raman', 'kerr')
id: str#
keywords: tuple[str, ...]#
reason: str#
routes: tuple[str, ...]#
species_condition: str | None = None#
class solprop.capabilities.SpeciesCapabilities(name, molecule, ionisation, raman, raman_by_default, wavelength_range, temperature_range, records)[source]#

Bases: object

What a built-in gas brings to a run, with optmat’s status of each part.

Attributes:
namestr

The gas.

moleculebool

Whether it is molecular rather than a single atom.

ionisationtuple of IonisationModel

optmat’s rate models for it, the recommended one first; empty for a gas optmat has no rate for, whose plasma needs a rate of the caller’s own.

ramanstr

Its Raman response’s status (solprop.nonlinear.optmat_raman.raman_status()): "atom" (no Raman-active mode), "validated", "provisional" (taken only under MaterialSelection(raman="provisional")) or "none" (a molecule optmat has no response of).

raman_by_defaultbool

Whether raman=None turns the response on for a fill of it.

wavelength_rangetuple of float

The vacuum wavelengths in m over which its linear model is valid; beyond them a run warns (MaterialRangeWarning).

temperature_rangetuple of float

The temperatures in K over which its equation of state is valid.

recordstuple of solprop.physdata.selection.MaterialRecord

The records of the optmat models a fill of it uses (linear, equation of state, transport and Kerr), each with its status.

ionisation: tuple[IonisationModel, ...]#
molecule: bool#
name: str#
raman: str#
raman_by_default: bool#
records: tuple[MaterialRecord, ...]#
temperature_range: tuple[float, float]#
wavelength_range: tuple[float, float]#
solprop.capabilities.output(source)[source]#

Say what a results file can give a reader (SEP 41, S16).

Parameters:
sourcestr, pathlib.Path or solprop.output.schema.Output

The file, or an opened handler (then nothing is said of resuming it).

Returns:
OutputCapabilities

The statement.

Raises:
solprop.exceptions.OutputError

If the file cannot be read or holds no run record.

Examples

>>> from solprop.capabilities import output
>>> from solprop.output.schema import _example_metadata
>>> from solprop.output import MemoryOutput
>>> handler = MemoryOutput()
>>> handler.open(_example_metadata())
>>> statement = output(handler)
>>> statement.basis, statement.quantities["energy"].kind
('modal', 'physical')
solprop.capabilities.route(name)[source]#

Say what a route takes (SEP 41, S8).

Parameters:
namestr

A route with parameter metadata (solprop.recipe.core.ROUTES).

Returns:
RouteCapabilities

Its keywords, the values of each enumerated one, and its combination rules.

Raises:
solprop.exceptions.DriverError

If the route has no parameter metadata, naming the routes that do.

Examples

>>> from solprop.capabilities import route
>>> capillary = route("capillary")
>>> capillary.choices["shape"], capillary.choices["noise"]
(('gauss', 'sech'), ('default', 'input', 'modified', 'none'))
>>> [rule.id for rule in capillary.rules_for("tdse_execution")][:2]
['tdse-execution-needs-tdse', 'tdse-double-precision']
solprop.capabilities.species(name)[source]#

Say what a built-in gas brings to a run (SEP 41, S8).

Parameters:
namestr or solprop.physdata.Gas

A built-in gas’s exact name, as the gas keyword takes it ("N2").

Returns:
SpeciesCapabilities

Its ionisation models and Raman response with optmat’s status of each, and the validity ranges optmat records for it.

Raises:
solprop.exceptions.MaterialError

If the name is not a built-in gas.

solprop.exceptions.DriverError

For air, which a capillary fills as nitrogen and oxygen: ask of each.

Examples

>>> from solprop.capabilities import species
>>> nitrogen = species("N2")
>>> nitrogen.raman, nitrogen.raman_by_default, nitrogen.ionisation[0].name
('validated', True, 'recommended')
>>> species("He").raman, species("N2O").raman
('atom', 'provisional')