Materials: responsibilities and callers#
The material interface describes optical properties. A material supplies real
refractive index n(wavelength, **kwargs) and dimensionless extinction
coefficient k(wavelength, **kwargs), with wavelength in micrometers. The
propagation model controls travel through the medium; surface interaction models
control refraction, reflection and coatings. Neither belongs in a dispersion
equation. Spatial GRIN propagation remains a separate, unimplemented feature.
Choosing and constructing a material#
Type |
Responsibility and state owner |
Construction and principal callers |
|---|---|---|
|
Property interface, subclass registry, propagation attachment, cache context and native deserialization dispatch. No optical equation. |
Subclassed by material implementations; queried by all runtime consumers. |
|
Constant index and extinction in live backend arrays. Index 1 is air only when extinction is zero. |
Constructed by users, |
|
d-line index/Abbe specification. The selected polynomial or Buchdahl model owns the live parameters and prediction equation. |
Users, glass optimization and readers with index/Abbe data. Polynomial evaluation is limited to 0.38–0.75 µm; Buchdahl has no declared limits. |
|
e-line index/Abbe specification using the e-line Buchdahl model. Shares parameter delegation with the d-line wrapper. |
Users and readers with e-line specifications; the same tracing and
persistence callers as |
|
Existing refractiveindex.info YAML adapter. Owns live coefficients, sampled arrays and thermal fields, and retains filename-based JSON. Shared helpers evaluate its equations and interpolation. |
Users with a YAML file and the |
|
Catalog resolution, name/reference identity and lookup provenance on top
of |
|
|
Owns an immutable, spatially uniform sampled or analytic definition and optional independent extinction table. Requires no file, catalog or registration. Native JSON contains the complete optical definition and copied metadata. |
|
AbbeModel is a prediction interface, not a material. AbbePolynomialModel,
BuchdahlDModel and BuchdahlEModel own their equations and reference-line
conventions. Fresh predictions derive coefficients from current parameters;
the wrappers expose those exact parameters and provide material persistence.
MaterialSpec and MatchPolicy describe catalog lookup choices.
MaterialRegistry owns discovery, registered YAML sources and resolution;
MaterialCatalog is its catalog-scoped browsing facade. These are not optical
evaluators. MaterialFactory converts surface specifications into material
instances: an existing material is accepted directly, and a dictionary there
continues to describe a lookup specification. Restore an inline native object
with BaseMaterial.from_dict before passing it to a surface.
Owned data and persistence#
from optiland.materials import BaseMaterial, DataMaterial
measured = DataMaterial.from_samples(
[0.4, 0.6, 0.8], [1.6, 1.55, 1.5], name="measured glass",
extinction={
"kind": "tabulated_k",
"wavelengths_um": [0.45, 0.75],
"values": [0.001, 0.002],
},
metadata={"source": "laboratory measurement"},
)
analytic = DataMaterial.from_coefficients(
"formula 2", [0, 1.03961212, 0.00600069867,
0.231792344, 0.0200179144, 1.01046945, 103.560653],
wavelength_range=(0.3, 2.5), name="analytic example",
)
restored = BaseMaterial.from_dict(measured.to_dict())
assert restored.definition == measured.definition
The definition contains a dispersion tagged as tabulated or
formula, an optional extinction tagged as tabulated_k, and reserved
thermal: null. Formula identifiers are formula 1 through formula 9,
using the existing refractiveindex.info conventions. A formula remains analytic;
it is never replaced with fitted or sampled indices. Negative finite indices
are permitted in the generic material interface. Import/export adapters enforce
their own format’s physical restrictions.
Sample arrays are sorted together and require at least two distinct positive wavelengths. All optical data must be finite; extinction must be nonnegative. Each table rejects queries beyond its own support by default. Missing extinction means zero at all finite positive wavelengths. Formula bounds are optional and enforced when supplied. Scalar evaluation returns a one-element array; array queries keep their shape and Torch wavelength gradients. Imported coefficients are constants, not trainable parameters: create a replacement definition to change them.
The frozen definition and its tuple fields cannot be edited. Input containers,
serialized containers and finite JSON metadata are copied; metadata and display
names do not affect optical evaluation. BaseMaterial.from_dict dispatches
the class and restores the registered propagation model. Existing public
material JSON formats continue to work; there are no aliases for earlier
unreleased material prototypes.
Choosing a table bounds policy#
DataMaterial accepts a keyword-only bounds="raise" or bounds="clamp"
on its constructor and both factories. MaterialFile and its catalog wrapper
Material expose the same option with "clamp" as their existing default.
The option applies to every tabulated property of the material, with each n/k
table using its own wavelength interval. It does not change the stored samples
or the support reported by spectral_range.
from optiland.materials import DataMaterial, Material, MaterialFile
measured = DataMaterial.from_samples(
[0.4, 0.8], [1.6, 1.4], bounds="clamp",
)
assert measured.n(0.9).item() == 1.4
strict_file = MaterialFile("measurements.yml", bounds="raise")
strict_catalog = Material("N-BK7", catalog="schott", bounds="raise")
Clamping holds the nearest endpoint value. It is an explicit assumption about unknown dispersion or absorption outside the supplied interval, not additional measurement data. The clamped interpolation’s wavelength derivative is zero outside the table; later thermal corrections can still depend on wavelength. Gradients with respect to live file-table endpoint values remain available. The default exception for owned data prevents silently tracing with this assumption. A strict one-sample file table accepts only its recorded wavelength; clamping makes it constant.
The policy is read-only after construction, is part of material cache state and
is written as bounds in native material JSON. Construct a replacement to
choose another policy. Old public MaterialFile/Material JSON without this
field still loads with clamping, and new saves record it explicitly. This allows
applications to opt into strict table evaluation now and retain their chosen
behavior if a later breaking release changes the file-material default.
Analytic equations are independent of table interpolation: a declared
DataMaterial formula range is always enforced, while MaterialFile keeps
its existing analytic extrapolation behavior. On from_coefficients, the
bounds option therefore affects only an optional tabulated extinction curve.
Missing extinction remains the documented zero assumption. Invalid/nonpositive
DataMaterial wavelengths still fail even with clamping enabled.
OSLO can preserve owned index samples but cannot encode this explicit clamping
choice. Its writer rejects clamped DataMaterial objects before replacing the
destination; use native JSON to retain the policy. Catalog-name exports in
external formats retain the destination program’s evaluation conventions.
Runtime and persistence callers#
Consumer |
Contract and responsibility |
|---|---|
Surface groups and paraxial/real ray tracing |
Query index at the ray wavelength. Material ownership is independent of whether rays are traced sequentially or through the nonsequential adapter. |
Interaction models and thin-film calculations |
Query n and k, then own complex-index conventions, Fresnel coefficients, layer interference and polarization. Materials do not implement coatings. |
|
Advances rays and uses extinction for attenuation, and owns propagation distance units and absorption conversion. |
|
Adapts a material to nonsequential tracing and attaches a surface BSDF; it delegates optical dispersion instead of duplicating its equations. |
Optimization material variables |
Update live Ideal/Abbe parameters or choose catalog glass. Owned imported definitions are constant data and are replaced as complete definitions. |
Native JSON |
Uses |
OSLO reader/writer |
Reader constructs sampled |
Zemax and CODE V writers |
Reject |
Plotting and information display |
Use |
Adding catalog transmission/thermal data or new file encodings requires a
separate feature with its own physical validation and persistence tests. Material
temperature/pressure keywords are not automatically connected to the global
Optic environment; that caller-level change is outside this material rework.
Extending material models#
A fitted glass model with live index, Abbe number or partial-dispersion
parameters is a BaseMaterial implementation (or a prediction strategy
behind a material wrapper). It is not an immutable imported data definition.
Reuse buchdahl_coordinate(wavelength, reference_wavelength, alpha) and
evaluate_buchdahl(index, coefficients, omega) for Buchdahl models. Pass
coefficients in increasing power order, starting with the linear term.
Different fits can have different orders and reference conventions without
sharing regression assets or becoming the same optical model.
Derive coefficients from the current parameters during evaluation; retaining
a derived tensor graph across optimizer steps or backward calls gives stale
values or invalid gradients. Reuse immutable regression data separately from
that derived state. Use backend operations and preserve parameter and wavelength
gradients. A new subclass is uncached by default; opt in only with a complete
_cache_state including every optical parameter and model choice. Caching
does not refresh coefficients stored by the subclass itself.
Serialize the model’s defining parameters in to_dict alongside
super().to_dict(). Its from_dict reconstructs those parameters;
BaseMaterial.from_dict restores propagation centrally. Do not serialize
derived coefficients as a second authority. Native loading and GUI background
workers must import the material class so its registry entry exists. GUI undo
and document replacement use the same native persistence contract.
Provide display_name for labels and spectral_range('n'/'k') for known
validity limits, validating the property name through the base contract.
Information displays use this label for unregistered material families;
explicit formatter registrations and a configured default still take precedence.
Unknown limits remain None; plotting then requires an explicit range.
Consumers must not infer catalog identity from a display label, or assume an
abbe member is numeric: BaseMaterial.abbe() is a method while the Abbe
wrappers expose a live parameter. Callers needing a spectral-line quantity
should evaluate n at the stated wavelengths. An optimization variable owns
its choice of model and must preserve all independent model parameters when
updating one of them. Import/export mappings need explicit, tested semantics;
sharing an equation does not make different fitted models interchangeable.
A future spatial material is another BaseMaterial implementation. It can
compose an existing material as its homogeneous spectral background, without
forcing its field or propagation controls into DataMaterial.definition.
Keep position/environment arguments in n/k(..., **kwargs) and require the
position needed by the optical law. In an opted-in cache, spatial context must
participate in the key even when all wavelengths are equal.
This extension point does not make tracing, paraxial analysis, wavefront launch phase, drawings or coatings position-aware. A GRIN implementation must update those callers or reject unsupported use, and define medium coordinates, boundary events, index/gradient consistency and a single owner of optical-path and attenuation integration. Custom propagation must remain explicit so homogeneous-only exporters can reject it. A spatial law attached to the default homogeneous propagator is not a supported GRIN configuration.