Public Python API#

BornSim computes scattering from finite voxel samples. Choose physical units, spatial and angular configurations explicitly. Grid.from_extent derives an isotropic voxel spacing from explicit axis extents and shape. GaussianMedium, ExponentialMedium, and WhittleMaternMedium generate random fields; RandomSphereMedium places identical nonoverlapping spheres. StructuredMedium voxelizes backgrounds and shapes and can add a seeded fluctuation field over the full structure. Solver accepts either AngularSampling or Directions for fixed volumes, or an ensemble of generated volumes. Integrated coefficients describe finite-sample cross sections divided by volume. Infinite-medium analytical formulas are test references, not public solvers.

Numerical calculation#

from bornsim import AngularSampling, Directions, Grid, Solver, Source
from bornsim.medium.random_medium import GaussianMedium
from bornsim.units import ureg

grid = Grid(
    shape=(4, 4, 4),
    spacing=40 * ureg.nanometer,
)

medium = GaussianMedium(
    background_refractive_index=1.33,
    refractive_index_std=0.01,
    correlation_length=80 * ureg.nanometer,
)

volume = medium.to_volume(
    grid=grid,
    seed=42,
)

sampling = AngularSampling(
    start=0 * ureg.degree,
    end=180 * ureg.degree,
    n_points=19,
)

source = Source(wavelength=633 * ureg.nanometer)

solver = Solver(
    source=source,
    sampling=sampling,
    order=2,
)

result = solver.solve(target=volume)

print(f'mu_s = {result.mu_s}, g = {result.g}')

directions = Directions(
    vectors=[
        [0, 0, 1],
        [1, 0, 0],
    ],
)

directional_result = solver.solve(
    target=volume,
    sampling=directions,
)

cut = solver.solve_cut(
    target=volume,
    directions=directions,
)

np.testing.assert_allclose(
    directional_result.differential.magnitude,
    cut.differential.magnitude,
)

Field construction#

from bornsim import Grid, Layer, StructuredMedium, Volume
from bornsim.medium.random_medium import WhittleMaternMedium
from bornsim.units import ureg

grid = Grid.from_extent(
    shape=(16, 16, 16),
    extent=(400, 400, 400) * ureg.nanometer,
)

layer = Layer.from_center_and_thickness(
    centre=(0, 0, -100) * ureg.nanometer,
    thickness=200 * ureg.nanometer,
    refractive_index=1.335,
)

statistics = WhittleMaternMedium(
    background_refractive_index=1.33,
    refractive_index_std=0.002,
    correlation_length=50 * ureg.nanometer,
    smoothness=1.5,
)

medium = StructuredMedium()
medium.add_background(refractive_index=1.33)
medium.add_structures(layer)
medium.add_fluctuations(medium=statistics)

volume = medium.to_volume(
    grid=grid,
    seed=42,
)

absolute_refractive_index = volume.refractive_index

imported_volume = Volume.from_refractive_index(
    refractive_index=absolute_refractive_index,
    background_refractive_index=1.33,
    grid=grid,
)

Alternatively, independently generated volumes with matching grids and backgrounds can be combined with volume_a + volume_b. The result’s refractive_index property exposes its immutable absolute voxel field. Volume.from_refractive_index also constructs a volume from an absolute unitless index array, converting it to the linearized contrast representation.

Configuration#

Incident illumination for BornSim scattering calculations.

class bornsim.source.Source(*, wavelength)[source]#

Define an unpolarized plane wave propagating along the positive z axis.

Parameters:

wavelength (Quantity) – Positive, finite vacuum wavelength. Explicit length units are required; the supplied units are preserved. A wavelength must be supplied.

wavelength#

Scalar vacuum wavelength with its supplied length units.

Type:

Quantity

Raises:

ValueError – If the wavelength is nonpositive, nonfinite, nonscalar, or has units incompatible with length.

Parameters:

wavelength (Quantity)

Notes

The transverse x and y incident polarizations have equal weights and are averaged incoherently. Arbitrary incidence directions and polarized illumination are not currently supported.

Examples

>>> from bornsim import Source
>>> from bornsim.units import ureg
...
...
>>> source = Source(wavelength=633 * ureg.nanometer)
>>> source.wavelength.check("[length]")
True

Reusable centred cubic voxel grids with unit-bearing coordinates.

class bornsim.grid.Grid(*, shape, spacing)[source]#

Define the spatial discretization once for generation and scattering.

Parameters:
  • shape (tuple of int) – Three explicitly chosen voxel counts, each from 2 to 32.

  • spacing (Quantity) – Positive, finite cubic voxel width, with its supplied length units. A spacing must be supplied.

Use from_extent() when the full physical voxel-box extent is known instead of the voxel spacing.

Notes

Voxel centres are (i - (N - 1)/2) * spacing on each axis. The box extends half a voxel beyond its outer centres. It describes the sampled domain, not an additional dielectric boundary. Background refractive index belongs to the medium or volume, rather than the spatial grid.

classmethod from_extent(*, shape, extent)[source]#

Create an isotropic voxel grid from its full physical extent.

extent gives the full voxel-box width along x, y, and z. Each axis extent must imply the same cubic voxel spacing.

Parameters:
  • shape (tuple[int, int, int])

  • extent (Quantity)

Return type:

Grid

property metadata: dict[str, object]#

Fresh JSON-compatible grid settings in SI units.

property positions: Quantity#

Unit-bearing voxel-centre coordinates, shape (*shape, 3).

property volume: Quantity#

Physical voxel-box volume in cubic metres.

Validated observation directions for numerical scattering.

class bornsim.directions.Directions(*, vectors)[source]#

Define an ordered collection of Cartesian unit observation vectors.

Parameters:

vectors (array_like or Quantity) – Explicit dimensionless vectors of shape (n, 3), with 1 to 16384 rows. Every vector must be finite and have unit length. Vectors are copied into an immutable array; they are never normalized automatically.

Notes

Coordinates use the global x, y, z axes. The incident wave propagates along positive z. Vector order is preserved in scattered amplitudes. Use vectors to access the NumPy array and from_angles() to construct vectors from explicitly unit-bearing spherical coordinates.

classmethod from_angles(*, polar_angles, azimuth_angles)[source]#

Convert paired or broadcastable spherical angles into unit vectors.

Polar angles are measured from positive z and lie in [0, pi]. Azimuth angles are measured from positive x toward positive y and are periodic. Both inputs require explicit angular units. Broadcast coordinates are flattened in row-major order, preserving polar/azimuth grid ordering.

Parameters:
  • polar_angles (Quantity)

  • azimuth_angles (Quantity)

Return type:

Directions

Full directional output sampling and independent solid-angle quadrature.

class bornsim.sampling.AngularSampling(*, angles=None, start=None, end=None, n_points=None, polar_samples=32, azimuth_samples=8)[source]#

Configure directional plots and their solid-angle normalization.

Parameters:
  • angles (Quantity, optional) – One-dimensional output polar angles theta, from 0 to pi, with 1 to 181 entries. Angular values require explicit units. Cannot be combined with start, end or n_points. Omit to generate an evenly spaced range.

  • start (Quantity, optional) – Inclusive range endpoints between 0 and pi. Angular values require explicit units. Defaults are 0 and pi; descending ranges are supported.

  • end (Quantity, optional) – Inclusive range endpoints between 0 and pi. Angular values require explicit units. Defaults are 0 and pi; descending ranges are supported.

  • n_points (int, optional) – Number of evenly spaced output angles, from 1 to 181. Default is 121. A single point samples start.

  • polar_samples (int, optional) – Gauss-Legendre integration nodes in cos(theta), from 16 to 128. Default is 32. These are independent of the output angles.

  • azimuth_samples (int, optional) – Uniform azimuth directions in [0, 2*pi), from 4 to 32. Default is 8. Used for both directional output and solid-angle integration.

Notes

Intensities are retained at every output (theta, phi). One-dimensional curves select sampled meridians; azimuth averages require an explicit azimuth_average() call. 3D phase surfaces use directional data. Integration uses dOmega = dphi * d(cos(theta)). Each Born order’s phase density is its intensity divided by its integrated coefficient. Refine both quadratures independently of output plot resolution.

property azimuths: Quantity#

Return uniform output and integration azimuths.

Returns:

Azimuth directions spanning one full turn, in radians.

Return type:

Quantity

check_work(*, grid, order, realizations=1)[source]#

Reject synchronous workloads above 100 million voxel-direction-orders.

Parameters:
  • grid (Grid) – Spatial grid used to estimate the number of voxel-direction pairs.

  • order (int) – Highest cumulative Born order in the requested calculation.

  • realizations (int, optional) – Number of independent volumes; defaults to 1.

Returns:

Returns normally when the estimated workload is within the limit.

Return type:

None

Raises:

ValueError – If the estimate exceeds 100 million voxel-direction-orders.

property directions: Directions#

Return unit directions for output and integration grids.

Returns:

Directions with output polar angles first, followed by the Gauss-Legendre integration nodes, for each azimuth.

Return type:

Directions

property metadata: dict[str, list[int | float] | int]#

Return fresh SI sampling settings for reproducible result archives.

Returns:

Output angles in radians and the polar and azimuthal sample counts.

Return type:

dict[str, list[int | float] | int]

summarize(*, result)[source]#

Reduce numeric Born data while retaining all directional intensities.

Parameters:

result (BornResult) – Numeric Born amplitudes, cumulative differential data, and isolated-term differential data for one volume.

Returns:

Numeric arrays with these keys: mean and terms have shape (order, output angle); directional_terms and directional_differential have shape (order, output angle, azimuth); directional_amplitudes adds incident polarization and vector-component axes of sizes two and three; integrals has shape (order, 3), for total, first-moment, and reduced coefficients.

Return type:

dict[str, numpy.ndarray]

Notes

Output amplitudes retain (order, theta, phi, incident polarization, vector component). Integrated coefficients are finite-sample cross sections divided by voxel-box volume, not intrinsic material values.

Reproducible independent-realization sampling configurations.

class bornsim.ensemble_sampling.EnsembleSampling(*, realizations=None, seed=None, seeds=None)[source]#

Choose consecutive seeds or an explicit ordered set of distinct seeds.

realizations defaults to four and seed to zero when seeds is omitted. Explicit seeds cannot be combined with realizations or seed. Seeds range from zero to 2**32-1; there are 1 to 32 independent realizations. Duplicate seeds are rejected because they do not provide independent uncertainty.

Parameters:
  • realizations (int)

  • seed (int)

  • seeds (tuple[int, ...])

property metadata#

Return exact ordered seeds for reproducibility.

Media and geometry#

Numerical random-medium statistics, independent of analytical solutions.

class bornsim.medium.base.Medium[source]#

Abstract interface for media that can be sampled into a finite volume.

Instantiate bornsim.GaussianMedium or bornsim.StructuredMedium, not this base class. Concrete media specify a uniform background_refractive_index and implement to_volume() using cubic voxels, SI spacing, and centred coordinates. The resulting field stores delta_refractive_index = n(r) - n0; propagation retains epsilon_r = n0**2 + 2*n0*delta_refractive_index.

add_background(*, refractive_index=None, medium=None, material=None)[source]#

Configure a composable medium’s background in place.

StructuredMedium accepts exactly one of a positive uniform refractive_index or a RandomFieldMedium supplied as medium. Statistical media describe homogeneous distributions; use StructuredMedium to compose material.

add_structures(*structures)[source]#

Append positional material shapes to a composable medium in place.

StructuredMedium supports Layer, Sphere, Ellipsoid, Box, and Cylinder. Later structures replace earlier material wherever their masks overlap.

property is_random: bool#

Whether independent generation seeds represent random realizations.

property metadata: dict[str, object]#

Return a fresh JSON-compatible description with numeric SI values.

Concrete media extend this dictionary with their own statistics or geometry. Generation seeds and grids belong to individual volumes.

abstractmethod to_volume(*, grid, seed=0)[source]#

Voxelize or sample this medium on an explicit Grid.

Parameters:
  • grid (Grid)

  • seed (int)

Return type:

Volume

Seeded collections of identical, nonoverlapping spheres.

class bornsim.medium.random_spheres.RandomSphereMedium(*, background_refractive_index, sphere_refractive_index, radius, sphere_count, max_placement_attempts=10000)[source]#

Place a fixed number of identical spheres by sequential rejection sampling.

Centres are proposed uniformly inside the grid box, inset by the radius so entire spheres remain inside. Proposals overlapping an accepted sphere are rejected. This defines a sequential placement distribution, rather than an equilibrium hard-sphere distribution. Dense configurations may fail to fit within max_placement_attempts; failure raises instead of returning fewer spheres. Voxelization samples material at voxel centres, so an unresolved sphere can occupy no voxels.

Both refractive indices are required plain numbers. Radius requires scalar length units. Each realization records its medium and generation seed.

Parameters:
  • background_refractive_index (float)

  • sphere_refractive_index (float)

  • radius (Quantity)

  • sphere_count (int)

  • max_placement_attempts (int)

property is_random: bool#

Whether independent generation seeds represent random realizations.

property metadata: dict[str, object]#

Return a fresh JSON-compatible description with numeric SI values.

Concrete media extend this dictionary with their own statistics or geometry. Generation seeds and grids belong to individual volumes.

sphere_centers(*, grid, seed=0)[source]#

Return reproducible sphere centres with shape (sphere_count, 3).

Parameters:
  • grid (Grid)

  • seed (int)

Return type:

Quantity

to_volume(*, grid, seed=0)[source]#

Sample the sphere material at voxel centres on an explicit grid.

Parameters:
  • grid (Grid)

  • seed (int)

Return type:

Volume

Seeded random fields with explicitly chosen spatial covariance.

class bornsim.medium.random_medium.ExponentialMedium(*, background_refractive_index, refractive_index_std, correlation_length)[source]#

Exponential spatial covariance: C(r) = refractive_index_std² exp(-r/ell).

Parameters:
  • background_refractive_index (float)

  • refractive_index_std (float)

  • correlation_length (Quantity)

class bornsim.medium.random_medium.GaussianMedium(*, background_refractive_index, refractive_index_std, correlation_length)[source]#

Gaussian spatial covariance: C(r) = refractive_index_std² exp(-r²/(2 ell²)).

Parameters:
  • background_refractive_index (float)

  • refractive_index_std (float)

  • correlation_length (Quantity)

class bornsim.medium.random_medium.RandomFieldMedium(*, background_refractive_index, refractive_index_std, correlation_length)[source]#

Abstract spectral synthesis shared by covariance-specific random fields.

Fields have Gaussian one-point statistics. Synthesis preserves expected variance and the DC mode, using a doubled box followed by cropping. Individual realizations are never recentered or rescaled.

Parameters:
  • background_refractive_index (float)

  • refractive_index_std (float)

  • correlation_length (Quantity)

abstract property correlation: str#

Spatial covariance family recorded in provenance.

property is_random: bool#

Statistical media support independent seeded realizations.

property metadata: dict[str, object]#

Return independent SI statistics for result provenance.

Lengths use the _m suffix. This describes ensemble statistics rather than a particular seed or a sample’s measured mean and variance.

spectral_weight(*, q_squared)[source]#

Return dimensionless spectrum shape at squared wavenumbers in m^-2.

The zero-wavenumber weight is one. This is not the normalized continuum power spectral density; the synthesis code supplies discrete variance normalization. Inputs must be finite and nonnegative.

Parameters:

q_squared (Quantity)

Return type:

ndarray

to_volume(*, grid, seed=0)[source]#

Generate a seeded random refractive-index Volume on an explicit Grid.

The field has a Gaussian one-point probability distribution for every spatial covariance model. Spectral synthesis uses a doubled periodic box and crops its first half. Expected point variance is normalized; individual samples are never recentered or rescaled. Grid resolution and synthesis-box size can alter sampled covariance. The seed defaults to zero and remains explicit in the generated volume’s provenance.

Parameters:
  • grid (Grid)

  • seed (int)

Return type:

Volume

class bornsim.medium.random_medium.WhittleMaternMedium(*, background_refractive_index, refractive_index_std, correlation_length, smoothness)[source]#

Whittle–Matérn covariance with explicitly chosen positive smoothness.

The spectrum is proportional to (1 + (q ell)²/(2 smoothness))**(-smoothness-3/2). Smoothness 0.5 reproduces ExponentialMedium with the same correlation length.

Parameters:
  • background_refractive_index (float)

  • refractive_index_std (float)

  • correlation_length (Quantity)

  • smoothness (float)

Voxel-centre geometry for finite structured dielectric samples.

Lengths use SI metres or compatible quantities. Shapes set absolute refractive index, not dielectric contrast; Volume retains the solver’s linearized constitutive law. Interfaces are sampled at cell centres without subvoxel averaging.

class bornsim.geometry.Box(*, centre=<factory>, refractive_index=None, material=None, size, rotation=((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)))[source]#

Set absolute refractive index where abs(local) <= size/2 in every axis.

Size contains three positive full side lengths along the local axes. All lengths use SI or compatible quantities; the boundary is closed.

Parameters:
  • centre (Quantity)

  • refractive_index (float | None)

  • material (Material | None)

  • size (Quantity)

  • rotation (Rotation | tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | ndarray[tuple[Any, ...], dtype[float64]] | Sequence[Sequence[float]] | Quantity)

mask(*, positions)[source]#

Return membership at unit-bearing voxel positions, shape (…, 3).

Parameters:

positions (Quantity)

Return type:

ndarray

class bornsim.geometry.Cylinder(*, centre=<factory>, refractive_index=None, material=None, radius, height, axis=2, rotation=((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)))[source]#

A finite circular cylinder with absolute refractive index and closed boundary.

Membership requires abs(local[axis]) <= height/2 and squared transverse distance <= radius**2. Radius and full height are positive SI lengths or quantities; axis is 0, 1, or 2, default z. Centre has three lengths.

Parameters:
  • centre (Quantity)

  • refractive_index (float | None)

  • material (Material | None)

  • radius (Quantity)

  • height (Quantity)

  • axis (int)

  • rotation (Rotation | tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | ndarray[tuple[Any, ...], dtype[float64]] | Sequence[Sequence[float]] | Quantity)

mask(*, positions)[source]#

Return membership at unit-bearing voxel positions, shape (…, 3).

Parameters:

positions (Quantity)

Return type:

ndarray

class bornsim.geometry.Ellipsoid(*, centre=<factory>, refractive_index=None, material=None, radii, rotation=((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)))[source]#

Set absolute refractive index where sum((local/radii)**2) <= 1.

Three positive semi-axis radii align with local x, y, z. All lengths use SI or compatible quantities. rotation maps local semi-axes into global coordinates.

Parameters:
  • centre (Quantity)

  • refractive_index (float | None)

  • material (Material | None)

  • radii (Quantity)

  • rotation (Rotation | tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | ndarray[tuple[Any, ...], dtype[float64]] | Sequence[Sequence[float]] | Quantity)

mask(*, positions)[source]#

Return membership at unit-bearing voxel positions, shape (…, 3).

Parameters:

positions (Quantity)

Return type:

ndarray

class bornsim.geometry.Layer(*, centre=<factory>, refractive_index=None, material=None, lower, upper, axis=2, rotation=((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)))[source]#

A slab with absolute refractive index inside lower <= local[axis] < upper.

Bounds are SI lengths in the slab local frame, offset by centre and oriented by rotation. The default axis is z (2); x and y are 0 and 1. The half-open interval gives adjacent slabs an unambiguous interface. The slab spans the voxel box in transverse axes; it represents a finite sample, not an infinite planar background.

Use from_center_and_thickness() to specify a slab by its centre and full thickness instead of its local lower and upper bounds.

Parameters:
  • centre (Quantity)

  • refractive_index (float | None)

  • material (Material | None)

  • lower (Quantity)

  • upper (Quantity)

  • axis (int)

  • rotation (Rotation | tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | ndarray[tuple[Any, ...], dtype[float64]] | Sequence[Sequence[float]] | Quantity)

classmethod from_center_and_thickness(*, thickness, centre, refractive_index=None, material=None, axis=2, rotation=((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)))[source]#

Create a slab from its centre and full thickness along axis.

Parameters:
  • thickness (Quantity)

  • centre (Quantity)

  • refractive_index (float | None)

  • material (Material | None)

  • axis (int)

  • rotation (Rotation | tuple[tuple[float, float, float], tuple[float, float, float], tuple[float, float, float]] | ndarray[tuple[Any, ...], dtype[float64]] | Sequence[Sequence[float]] | Quantity)

Return type:

Layer

mask(*, positions)[source]#

Return membership at unit-bearing voxel positions, shape (…, 3).

Parameters:

positions (Quantity)

Return type:

ndarray

class bornsim.geometry.Sphere(*, centre=<factory>, refractive_index=None, material=None, radius)[source]#

Set absolute refractive index where sum((r-centre)**2) <= radius**2.

Radius is positive; centre is a three-component SI length or quantity. The closed surface is sampled at voxel centres, giving a staircase boundary.

Parameters:
  • centre (Quantity)

  • refractive_index (float | None)

  • material (Material | None)

  • radius (Quantity)

mask(*, positions)[source]#

Return membership at unit-bearing voxel positions, shape (…, 3).

Parameters:

positions (Quantity)

Return type:

ndarray

class bornsim.geometry.StructuredMedium(*, regions=(), background_refractive_index=None, overlap='replace', warn_on_clipping=False)[source]#

Compose ordered material regions in a uniform background.

Parameters:
  • regions (sequence of Layer, Sphere, Ellipsoid, Box, or Cylinder) – Regions set absolute refractive index. Overlap precedence follows the overlap policy; indices are not added. Default is empty.

  • background_refractive_index (float) – Explicit positive uniform background refractive index. It fills uncovered voxels and extends outside the finite voxel box.

  • overlap ({"replace", "preserve", "error"}) – Later regions win, earlier regions win, or reject shared voxel centres.

  • warn_on_clipping (bool) – Warn when geometry extends beyond the voxel box; default False.

  • fluctuations (RandomFieldMedium, configured with add_fluctuations()) – Optional additive field applied over the background and every region.

Notes

Configure this mutable builder with add_background, add_structures, and optionally add_fluctuations. An empty builder has no background; voxelization requires an explicit one. Medium remains abstract; random statistics and geometry shapes remain frozen. Calls update the builder and return None. Previously generated volumes do not change. Constructor regions are still accepted for existing callers. Voxelization returns delta_refractive_index(r) = n(r) - background_refractive_index. A configured additive fluctuation field is added after the absolute-index regions are composed. The numerical solver uses epsilon_r = n0**2 + 2*n0*delta_refractive_index, omitting delta_refractive_index**2 even at higher Born orders. Strong refractive index differences can invalidate this constitutive approximation or Born iteration. Regions crossing the box are clipped. Layers therefore have finite lateral extent; this is not a transfer-matrix or layered-background Green solver.

add_background(*, refractive_index=None, medium=None, material=None)[source]#

Set the background in place, retaining all existing structures.

Supply exactly one of refractive_index (a positive uniform refractive index) or medium (RandomFieldMedium statistics). A random background is sampled with to_volume(seed=…) and fills only voxels outside structures. The exterior scattering background remains uniform at background_refractive_index. Replacing a background changes the reference n0, not structures’ absolute refractive indices. Validation precedes any state change. Returns None. Random statistics are copied when added.

add_fluctuations(*, medium)[source]#

Set an additive random field applied over the background and all structures.

The statistical medium must share the configured uniform background. This replaces any previously configured additive fluctuation field. Random backgrounds and additive fluctuations are mutually exclusive. Returns None. Generated volumes record the medium and seed as provenance.

add_structures(*structures)[source]#

Append material regions in argument order; return None.

Accepts any number of positional Layer, Sphere, Ellipsoid, Box, or Cylinder objects. Later arguments replace earlier material in overlaps and replace a random background inside their masks. Separately configured additive fluctuations remain present over every region. All arguments are validated before editing the builder; an empty call is a no-op. Existing voxel samples are independent snapshots.

property is_random#

Random backgrounds or additive fluctuations supply independent realizations.

property metadata#

Return independent SI geometry descriptions in application order.

Region type, absolute refractive index, axis where applicable, and dimensions are recorded. Length keys have the _m suffix, and vectors are JSON lists. The list order preserves replacement precedence in overlapping regions. Configured additive fluctuation statistics are recorded under fluctuations. Generated volumes with fluctuations retain a snapshot of this metadata and their seed.

to_volume(*, grid, seed=0)[source]#

Voxelize this medium on an explicit Grid.

An explicit background is required. Ordered shapes set absolute refractive indices; overlap follows replace, preserve, or error. A random background uses the supplied seed and is replaced by structures inside their masks. Additive fluctuations use the same seed and are applied over all regions.

Parameters:
  • grid (Grid)

  • seed (int)

Return type:

Volume

Immutable, reusable homogeneous optical materials.

class bornsim.material.Material(*, refractive_index)[source]#

A nondispersive real refractive index shared by any number of shapes.

refractive_index is an absolute positive dimensionless refractive index, not a contrast. Supply a plain number; quantities are rejected. Absorption and wavelength dispersion are not implemented.

Parameters:

refractive_index (float)

property metadata#

Fresh JSON-compatible material description.

Rotation inputs accept a bornsim.rotation.Rotation, a nested matrix sequence, a NumPy array, or a dimensionless quantity. Shapes normalize accepted inputs to immutable bornsim.rotation.RotationMatrix tuples; bornsim.rotation.RotationInput describes the accepted forms.

Proper three-dimensional rotations for immutable material shapes.

class bornsim.rotation.Rotation(*, axis, angle)[source]#

Define an active right-handed rotation about a dimensionless axis.

angle accepts angular quantities; angular values require explicit units. The axis is normalized. matrix maps local column coordinates into global coordinates. It follows the right-hand rule, and successive world rotations compose as R_new @ R_existing. Shape membership uses local row coordinates (positions - centre) @ R. Shapes accept this object, a proper orthonormal 3-by-3 matrix, or a dimensionless matrix quantity; they store the normalized matrix as an immutable tuple of tuples. No voxel resampling is performed.

Parameters:
  • axis (tuple[float, float, float])

  • angle (Quantity)

property matrix: ndarray[tuple[Any, ...], dtype[float64]]#

Return Rodrigues’ active rotation matrix, shape (3, 3).

Finite refractive-index fields on centred cubic voxel grids.

class bornsim.volume.Volume(*, delta_refractive_index, background_refractive_index, grid, medium=None, seed=None)[source]#

Store a finite voxel field and its explicit spatial Grid.

The copied, read-only delta_refractive_index array has shape grid.shape. refractive_index returns the read-only absolute field background_refractive_index + delta_refractive_index. Addition combines contrasts from volumes only when their grids and backgrounds match. Background refractive index is a plain number; grid spacing and positions carry physical units. Relative permittivity is linearized as background_refractive_index**2 + 2*background_refractive_index*delta_refractive_index. The quadratic fluctuation term is omitted at every Born order. Medium.to_volume records generation statistics and seed. medium_metadata returns the metadata snapshot captured when the volume was created. Manually constructed volumes have only their explicitly supplied provenance.

Use from_refractive_index() when the input array contains absolute refractive indices rather than fluctuations from the background.

Parameters:
  • delta_refractive_index (ndarray)

  • background_refractive_index (float)

  • grid (Grid)

  • medium (Medium | None)

  • seed (int | None)

classmethod from_refractive_index(*, refractive_index, background_refractive_index, grid, medium=None, seed=None)[source]#

Construct a volume from an absolute refractive-index field.

Parameters:
  • refractive_index (array_like) – Plain numerical absolute refractive indices, with shape grid.shape. Values must not carry units.

  • background_refractive_index (float) – Positive, unitless reference refractive index.

  • grid (Grid) – Explicit spatial grid matching the field shape.

  • medium (Medium, optional) – Medium metadata associated with generation. Must be supplied together with seed.

  • seed (int, optional) – Generation seed, supplied together with medium.

Returns:

Immutable volume storing the field as refractive_index - background_refractive_index.

Return type:

Volume

Raises:
  • ValueError – If the field, background, grid shape, or resulting linearized permittivity is invalid.

  • TypeError – If medium is not a Medium instance.

property medium_metadata: dict[str, object] | None#

Return a copy of the generation metadata captured at construction.

Returns:

A fresh copy of the source medium’s metadata, or None when the volume has no associated medium.

Return type:

dict[str, object] or None

plot_3d(*, backend='plotly', mode=None, field='delta_refractive_index', length_unit='nanometer', surface_count=8, opacity=None, opacity_scale='uniform')[source]#

Build a three-dimensional figure of the finite voxel medium.

Parameters:
  • backend ({'matplotlib', 'plotly'}, optional) – Default is Plotly for browser interaction. Select ‘matplotlib’ explicitly for a Matplotlib figure.

  • mode (str, optional) – Matplotlib supports ‘voxels’ (default). Plotly supports ‘volume’ (default) and ‘isosurface’. Uniform Plotly fields are rendered as volume data; an isosurface cannot be generated from a uniform field.

  • field ({'delta_refractive_index', 'refractive_index', 'permittivity'}, optional) – Scalar field to display, default refractive index fluctuation. ‘refractive_index’ displays n0 + delta_refractive_index; ‘permittivity’ displays the solver’s linearized relative permittivity n0**2 + 2*n0*delta_refractive_index, not (n0+delta_refractive_index)**2.

  • length_unit (str, optional) – Spatial display unit, default ‘nanometer’. Stored SI data is unchanged.

  • surface_count (int, optional) – Number of contours, from 1 to 32; default 8. More surfaces increase browser rendering cost. Used only by Plotly volume and isosurface modes.

  • opacity (float, optional) – Surface opacity in (0, 1]. Default is 1 for Matplotlib and 0.15 for Plotly.

  • opacity_scale ({'uniform', 'increasing'}, optional) – Default ‘uniform’ gives all contours the same opacity. ‘increasing’ is available only for Plotly volume mode and scales opacity from zero at the lowest displayed contour to opacity at the highest. Use field=’refractive_index’ to emphasize high refractive index, rather than the magnitude of positive and negative fluctuations.

Returns:

figure – Matplotlib: call pyplot.show() to display or figure.savefig() to export. Rotation needs an interactive Matplotlib backend; gallery images are static. Plotly: call figure.show() or figure.write_html(). Constructing a figure opens no window or browser.

Return type:

matplotlib.figure.Figure or plotly.graph_objects.Figure

Raises:

ValueError – If a mode, scalar field, display unit, or rendering setting is invalid.

Notes

Matplotlib voxels preserve staircase interfaces. Dense transparent scenes can have depth-ordering limitations in Matplotlib. Plotly contours interpolate between voxel centres and are visualization aids, not exact curved interfaces or a replacement for grid refinement. Aspect ratio follows physical dimensions. All voxels are retained, with at most 32**3 samples under Volume’s grid limit; browser performance also depends on surface count and graphics support. This plots the input material, not an electromagnetic field.

plot_slice(*, normal='z', index=None, field='refractive_index', length_unit='nanometer', title=None)[source]#

Build a Matplotlib figure of one voxel plane.

Parameters:
  • normal ({'x', 'y', 'z'}, optional) – Axis perpendicular to the plane; default ‘z’.

  • index (int, optional) – Voxel index along the normal. Defaults to n//2, the positive central plane for an even grid. The title gives its actual position.

  • field ({'refractive_index', 'delta_refractive_index', 'permittivity'}, optional) – Display n0 + delta_refractive_index, fluctuations, or linearized relative permittivity n0**2 + 2*n0*delta_refractive_index. Default ‘refractive_index’.

  • length_unit (str, optional) – Spatial display unit; default ‘nanometer’. Stored SI data is unchanged.

  • title (str, optional) – Override the title describing the field and plane position.

Returns:

figure – Equal-aspect image with voxel-edge extents and a labelled colorbar. Call pyplot.show() to display or figure.savefig() to export. Colors use the full volume’s range to compare different planes; fluctuation colors are symmetric about zero.

Return type:

matplotlib.figure.Figure

Raises:

ValueError – If the plane, index, field, or length unit is invalid.

property positions: Quantity#

Return voxel-centre coordinates relative to the sample centre.

Returns:

positions – Unit-bearing coordinates, shape (nx, ny, nz, 3), with the final axis ordered as x, y, z.

Return type:

Quantity

property refractive_index: ndarray#

Return the read-only absolute refractive-index field.

Returns:

Absolute refractive indices, equal to the background plus delta_refractive_index.

Return type:

numpy.ndarray

property volume: Quantity#

Return the total physical volume of all voxels.

Returns:

volume – Unit-bearing sample volume, equal to delta_refractive_index.size*spacing**3.

Return type:

Quantity

Solver and results#

Solver configuration for finite-volume and ensemble Born calculations.

class bornsim.solver.Solver(*, source, sampling=<factory>, order=3)[source]#

Configure numerical Born scattering from explicit finite samples.

Source chooses the wavelength. AngularSampling supplies output directions and integration quadrature; order sets the highest cumulative Born order. All orders retain the linearized dielectric contrast and the equal-volume Green self cell. Decreasing successive terms do not certify convergence.

Parameters:
ensemble(*, medium, grid, sampling=None, ensemble_sampling=None)[source]#

Average numerical scattering over independently generated finite samples.

Grid selects the voxel domain. AngularSampling selects output directions and integration quadrature; the solver’s sampling is used when omitted. EnsembleSampling selects ordered independent seeds and defaults to four consecutive seeds starting at zero. Deterministic structures use solve.

Intensities are averaged after coherent summation within each realization. Standard errors quantify realization uncertainty, not discretization or finite-size error. One realization has unknown (NaN) uncertainty. Integrated coefficients are finite-sample cross sections divided by voxel-box volume.

Parameters:
Return type:

Result

solve(*, target, sampling=None)[source]#

Compute scattering from a volume using angular or Cartesian sampling.

Pass AngularSampling for solid-angle integration, or Directions for an unnormalized set of observation directions. The target is an explicit voxelized Volume; random statistics use ensemble instead.

Parameters:
Return type:

Result

solve_cut(*, target, angles=None, directions=None)[source]#

Compute an unnormalized angular cut through a fixed Volume.

Supply polar angles on the x-z meridian or a Directions configuration, but not both. This is a convenience wrapper for solve(target=..., sampling=Directions(...)). Explicit angular units are required. Default angles span 0 to pi with 121 samples. Cut amplitudes and intensities retain every requested direction. A cut alone cannot determine a solid-angle integral; integrated coefficients and normalized phase functions are unavailable.

Parameters:
Return type:

Result

Unitful scattering results, validated archives, and Matplotlib plots.

class bornsim.results.BornResult(*, directions, amplitudes, differential, term_differential, field_norms, warnings)[source]#

Store numeric SI results for one finite-volume Born calculation.

Parameters:
  • directions (numpy.ndarray) – Dimensionless observation vectors, shape (observation, 3).

  • amplitudes (numpy.ndarray) – Complex isolated amplitudes in metres, shape (order, observation, incident polarization, vector component).

  • differential (numpy.ndarray) – Cumulative scattering in m^-1 sr^-1, shape (order, observation).

  • term_differential (numpy.ndarray) – Isolated-term scattering in m^-1 sr^-1, same shape as differential.

  • field_norms (numpy.ndarray) – Dimensionless relative field norms, shape (order,).

  • warnings (tuple of str) – Numerical-resolution and Born-term diagnostics.

directions, amplitudes, differential, term_differential, field_norms

Arrays described above, without attached units.

Type:

numpy.ndarray

warnings#

Numerical diagnostics.

Type:

tuple of str

See also

bornsim.series.BornSeries.solve

Produce this numeric result.

bornsim.results.Result

Unitful result with plotting support.

Notes

Row zero denotes first order. Cumulative intensities include interference between amplitudes; isolated intensities must not be summed to reconstruct them. Integrated coefficients are not inferred from observation samples.

class bornsim.results.Result(*, source, kind, angular=None, differential=None, sample_volume=None, azimuth_averaged=False, azimuth_stderr=None, angles=None, directions=None, azimuths=None, mu_s=None, g=None, mu_s_prime=None, amplitudes=None, term_differential=None, stderr=None, field_norms=None, warnings=(), realizations=None, provenance=None)[source]#

Store unitful scattering, diagnostics and reproducible settings.

Full solves retain differential and term_differential with shape (order, polar angle, azimuth). Cuts and explicitly averaged results use (order, observation). amplitudes always adds polarization and Cartesian axes of sizes two and three to the differential shape. Ensembles retain intensities and standard errors, without coherent amplitudes.

angular groups read-only coordinates, intensities, amplitudes and phase densities. azimuth_average() explicitly averages intensities and retains the covariance-correct standard error of per-realization averaged curves. Plots of full results select one sampled meridian by default.

Angles use radians, amplitudes metres, differential data m^-1 sr^-1, integrated coefficients m^-1, and sample_volume m^3. Dimensional arrays require explicit units. Every full phase density uses its order’s solid-angle integral. Cuts have no inferred integrated coefficients or phase normalization.

Numerical coefficients are finite-sample cross sections divided by the entire voxel-box volume. differential_cross_section restores area per steradian using stored sample_volume.

Source and kind are required. Supply angular or individual differential data, never both. Results are immutable; provenance returns a fresh copy. Other fields describe sampling, isolated terms, integrated moments, uncertainty, diagnostics and provenance. NaN errors represent one realization; NaN anisotropy represents zero scattering. Arrays and JSON-compatible provenance are copied and validated. Use differential and amplitudes for both cuts and full solves.

Save and load use schema 2 archives; unsupported schemas are rejected. Numerical cumulative intensities retain coherent amplitude interference. Isolated intensities must not be summed to reconstruct cumulative results.

Parameters:
  • source (Source)

  • kind (str)

  • angular (AngularData)

  • g (Quantity | None)

  • mu_s_prime (Quantity | None)

  • field_norms (Quantity | None)

  • warnings (tuple)

  • realizations (int | None)

property amplitudes#

Read-only angular data owned by angular.

property angles#

Read-only angular data owned by angular.

azimuth_average()[source]#

Return explicit averaged intensities with covariance-correct errors.

property azimuth_averaged#

Read-only angular data owned by angular.

property azimuth_stderr#

Read-only angular data owned by angular.

property azimuths#

Read-only angular data owned by angular.

property differential#

Read-only angular data owned by angular.

property differential_cross_section#

Finite-sample d-sigma/d-Omega in square metres per steradian.

property directions#

Explicit cut coordinates; full-grid vectors are angular.directions.

classmethod load(*, path)[source]#

Load and validate a BornSim result archive without using pickle.

Parameters:

path (str or pathlib.Path) – Archive created by Result.save.

Returns:

result – Restored quantities, complex amplitudes, diagnostics, and original provenance. Loading does not change the recorded package version.

Return type:

Result

Raises:
  • ValueError – If the schema is unsupported, metadata or arrays are missing or inconsistent, or result validation fails.

  • OSError – If the archive cannot be read.

meridian(*, azimuth, method='exact')[source]#

Select a physical azimuth from full data without interpolation.

property mu_s#

Read-only angular data owned by angular.

property phase_function#

Return directional phase density per steradian.

Full solves retain (order, polar angle, azimuth). Explicitly averaged results retain (order, polar angle). Cuts have no normalization. Every order uses its own full solid-angle scattering coefficient.

plot(*, terms=False, log_y=False, title=None, azimuth=0)[source]#

Build a Matplotlib figure of differential scattering curves.

Parameters:
  • terms (bool, optional) – Plot isolated numerical terms instead of cumulative curves. Default is False. Isolated terms omit interference and ensemble error bars.

  • log_y (bool, optional) – Use a logarithmic scattering axis. Default is False.

  • azimuth (int, optional) – Index of the sampled meridian; default zero. Use azimuth_average() explicitly to plot averaged intensities.

  • title (str, optional) – Override the default title.

Returns:

figure – Figure with angles in degrees and scattering in m^-1 sr^-1. Explicit direction samples use input indices and markers. Ensemble cumulative curves show one-standard-error bars where sampling error is known.

Return type:

matplotlib.figure.Figure

Raises:

ValueError – If terms=True and isolated-term curves are unavailable.

Notes

The figure is registered with pyplot and returned without displaying it. Call matplotlib.pyplot.show() to display open figures or figure.savefig(path) to export a PNG, SVG, or PDF. Zero values cannot be displayed on a log axis. A one-realization ensemble has unknown sampling error, so no error bars are shown. This method plots differential scattering, not a normalized phase function.

plot_cross_section(*, volume=None, area_unit='nanometer**2', terms=False, log_y=False, title=None, azimuth=0)[source]#

Build differential cross-section curves for a finite sample.

Parameters:
  • volume (Volume, optional) – Optional sample. Normally the recorded sample_volume supplies the conversion without retaining the input voxel field.

  • area_unit (str, optional) – Display area unit; default ‘nanometer**2’.

  • terms (bool, optional) – Show isolated terms, which omit interference and sampling error bars, instead of cumulative coherent curves. Default False.

  • log_y (bool, optional) – Use a logarithmic scattering axis. Default False.

  • azimuth (int, optional) – Index of the sampled meridian; default zero. Use azimuth_average() explicitly to plot averaged intensities.

  • title (str, optional) – Override the default title.

Returns:

figure – Angle or direction curves, with ensemble standard errors scaled by the same sample volume. Returned without displaying it.

Return type:

matplotlib.figure.Figure

Raises:
  • TypeError – If volume is not a Volume.

  • ValueError – If recorded grid dimensions differ, area_unit is invalid, or isolated curves are unavailable.

Notes

Uses dσ/dΩ = V * differential. These are finite-sample cross sections, not intrinsic infinite-medium transport coefficients. An optional supplied volume must agree with the recorded physical volume and grid. Stored scattering data and its SI units remain unchanged.

plot_field_norms(*, log_y=True)[source]#

Plot relative Born field-term norms for each available realization.

Parameters:

log_y (bool, optional) – Use a logarithmic vertical axis. Default is True. Zero norms cannot be displayed on a logarithmic axis.

Returns:

figure – One trace per realization versus one-based field-term order. Norms are dimensionless relative to the incident field.

Return type:

matplotlib.figure.Figure

Raises:

ValueError – If no numerical field norms are available.

Notes

The figure preserves per-realization diagnostics. Decreasing field terms do not certify Born convergence or remove discretization error.

plot_phase_function(*, view='angular', order=None, log_y=False, azimuth=0, backend=None)[source]#

Plot normalized phase functions as angular curves, polar cuts, or a surface.

Parameters:
  • view ({'angular', 'polar', '3d'}, optional) – Angular curves select a sampled meridian by default. Polar cuts use its opposite azimuth for the other half of the plane. The 3D surface uses p(theta, phi), with radius and color in sr^-1, retaining directional asymmetry. Analytical distributions are independent of phi.

  • order (int, optional) – One-based cumulative order. Angular and polar views show all orders by default; the 3D view shows only the highest order.

  • log_y (bool, optional) – Logarithmic vertical scale for angular curves. Default is False; only supported with view='angular'.

  • backend ({'plotly', 'matplotlib'}, optional) – The 3D view defaults to Plotly. Angular and polar views use Matplotlib. Select ‘matplotlib’ explicitly for a static 3D figure.

Returns:

figure – Returned without displaying it. Call figure.show() for Plotly, or matplotlib.pyplot.show() for Matplotlib. The 3D coordinates are probability-density radii, not spatial positions.

Return type:

matplotlib.figure.Figure or plotly.graph_objects.Figure

Raises:

ValueError – If phase normalization is unavailable, the view or order is invalid, directional data are unavailable for a numerical 3D view, log scaling is requested for another view, or polar/3D data do not span the full [0, pi] range with at least three angles.

Notes

Numerical 3D surfaces retain every sampled azimuth; no azimuth averaging is applied. For one realization this displays the fixed sample’s directional distribution; multiple realizations display the ensemble intensity average. Older archives lacking directional data must be recomputed for a directional 3D view. Angular and polar views select sampled meridians; averaging requires azimuth_average(). A flat phase function produces a spherical surface; forward scattering extends towards +z. Samples are sorted by angle for rendering. Error bars for normalized ratios are omitted because the required covariance is not stored.

property provenance#

Return a fresh metadata copy; edits cannot change this result.

property sample_volume#

Read-only angular data owned by angular.

save(*, path)[source]#

Save data, units, warnings, and provenance in a versioned NPZ archive.

Parameters:

path (str or pathlib.Path) – Destination file, typically ending in .npz. The exact path is used without appending an extension. An existing file is replaced.

Returns:

path – Destination of the compressed NumPy archive.

Return type:

pathlib.Path

Raises:
  • ValueError – If result fields were modified into an invalid state.

  • OSError – If the destination cannot be written.

Notes

Arrays are stored in SI units, including complex Born amplitudes. Metadata is JSON, and loading never enables pickle. The archive stores result data rather than the original voxel field; retain that field separately for manual volumes. A field SHA-256 identifies the input. Generated volumes also record their medium and seed. Exact seeded reproduction depends on the recorded implementation versions.

property stderr#

Read-only angular data owned by angular.

property term_differential#

Read-only angular data owned by angular.

Consistent unitful directional scattering data for solves and cuts.

class bornsim.angular_data.AngularData(*, differential, directions=None, amplitudes=None, angles=None, azimuths=None, term_differential=None, stderr=None, azimuth_stderr=None, mu_s=None, sample_volume=None, kind='volume', azimuth_averaged=False, meridian_azimuth=None)[source]#

Group coordinates, amplitudes, intensities and normalization.

Full angular data have shape (order, polar angle, azimuth). Cuts and explicitly averaged curves have shape (order, observation). Directions have the same observation axes and a final Cartesian axis of size three. Amplitudes add incident-polarization and vector axes of sizes two and three. Ensemble amplitudes are absent: intensities, not amplitudes, are averaged across realizations. Arrays are copied, unitful and read-only.

Parameters:
  • differential (Quantity)

  • directions (Quantity | None)

  • amplitudes (Quantity | None)

  • angles (Quantity | None)

  • azimuths (Quantity | None)

  • term_differential (Quantity | None)

  • stderr (Quantity | None)

  • azimuth_stderr (Quantity | None)

  • mu_s (Quantity | None)

  • sample_volume (Quantity | None)

  • kind (str)

  • azimuth_averaged (bool)

  • meridian_azimuth (Quantity | None)

azimuth_average()[source]#

Average intensities explicitly; never average coherent amplitudes.

property differential_cross_section#

Finite-sample differential cross section in square metres per sr.

meridian(*, azimuth, method='exact')[source]#

Select a sampled meridian using an angle, preserving normalization.

Azimuth requires explicit angular units and is periodic modulo 2*pi. exact requires a sampled azimuth within 1e-10 radians; nearest explicitly selects the closest sample. No interpolation or azimuth averaging is performed. meridian_azimuth records the actual selected angle.

property phase_function#

Density per steradian, normalized by the full solid-angle integral.

plot(*, terms=False, log_y=False, title=None, azimuth=0)[source]#

Plot one sampled meridian, or an explicitly averaged curve.

plot_phase_function(*, view='angular', order=None, log_y=False, azimuth=0, backend=None)[source]#

Plot normalized directional densities with the same Result interface.

Numerical engine#

Reusable finite-volume vector Born-series engine.

Time convention exp(-i omega t). Relative dielectric contrast is linearized as 2 n0 delta_n at every order. Amplitudes interfere coherently before the unpolarized intensity is evaluated.

class bornsim.series.BornSeries(*, grid, background_refractive_index, wavelength, directions, order=3)[source]#

Own the fixed geometry and propagation operator for numerical Born orders.

Parameters:
  • grid (Grid) – Shared spatial configuration, also used by the input Volume. Required for generation and scattering.

  • background_refractive_index (float) – Positive uniform background refractive index, dimensionless.

  • wavelength (Quantity) – Positive vacuum wavelength; explicit length units are required.

  • directions (Directions) – Validated, immutable Cartesian unit observation vectors.

  • order (int, optional) – Highest cumulative Born order, from 1 to 12; default 3.

operator#

One open-boundary FFT Green operator reused for every compatible volume.

Type:

bornsim.green.GreenOperator

Notes

The cached incident plane wave travels along +z with x and y polarizations. For each call, E[0] = E_inc and E[m] = K(chi*E[m-1]), where chi = 2*n0*delta_refractive_index and K integrates the background dyadic Green tensor multiplied by k0**2. Order-m far-field amplitude is computed from chi*E[m-1]. Cumulative intensity is |sum_m f[m]|**2, averaged over the two polarizations and divided by the entire voxel-box volume.

Geometry, incident field, and the Green tensor are reused; contrast, propagated fields, amplitudes, and diagnostics are local to each solve. Compatible volumes must have exactly the configured grid and background. Configuration is frozen to prevent stale propagation settings. Decreasing field terms do not establish universal convergence; the dielectric linearization is unchanged at higher orders.

solve(*, volume)[source]#

Compute coherent scattering from one compatible finite refractive index volume.

Parameters:

volume (Volume) – Fixed fluctuation field with positive linearized permittivity, matching this engine’s grid spacing, dimensions and background.

Returns:

result – Fresh numeric SI amplitudes, cumulative and isolated intensities, relative field norms, and diagnostic messages.

Return type:

bornsim.results.BornResult

Raises:
  • TypeError – If volume is not a Volume.

  • ValueError – If grid or background differs from the engine configuration, or iteration produces nonfinite fields.

Notes

Each solve starts with the incident field. Amplitudes are summed before squaring, preserving interference. Intensities are finite-sample cross sections divided by the entire voxel-box volume, not intrinsic infinite-medium transport coefficients. The dielectric contrast is 2*n0*delta_refractive_index at every order. Growing field terms generate a diagnostic; decreasing terms do not certify convergence.

Examples

>>> from bornsim.units import ureg
>>> from bornsim import Directions, Grid, GaussianMedium
>>> from bornsim.series import BornSeries
...
...
>>> medium = GaussianMedium(
...     background_refractive_index=1.33,
...     refractive_index_std=0.01,
...     correlation_length=100e-9 * ureg.meter,
... )

… >>> grid = Grid( … shape=(2, 2, 2), … spacing=50 * ureg.nanometer, … ) >>> volume = medium.to_volume( … grid=grid, … seed=42, … ) >>> directions = Directions(vectors=[[0.0, 0.0, 1.0]]) >>> engine = BornSeries( … grid=grid, … background_refractive_index=volume.background_refractive_index, … wavelength=633 * ureg.nanometer, … directions=directions, … order=2, … ) >>> result = engine.solve(volume=volume) >>> result.amplitudes.shape (2, 1, 2, 3)

Numerical ensemble quadrature and realization sampling uncertainty.

bornsim.ensemble.ensemble_scattering(*, medium, wavelength, grid, sampling=None, order=3, ensemble_sampling=None)[source]#

Run numerical Born ensembles on an explicit Grid.

A random Medium generates one Volume per ordered seed in EnsembleSampling. AngularSampling defines output observations and independent integration quadrature. Complex amplitudes interfere within each realization; intensities are averaged across realizations. The returned dictionary contains numeric SI directional intensities, isolated-term intensities, integrated moments, standard errors, per-realization field norms, and diagnostic warnings. Angles and azimuths retain quantities from the sampling configuration.

Coefficients are finite-sample cross sections divided by voxel-box volume. Standard errors describe realization sampling; one realization produces NaN. Check spatial, angular, finite-size, and ensemble convergence separately.

Parameters:
Return type:

dict[str, Any]

Units#

Shared TypedUnit registry and explicit input validation.

Validation preserves the supplied units. Dimensional inputs require quantities; dimensionless inputs may be bare numbers. Numerical kernels, plotting and serialization explicitly select units when they need magnitudes.

bornsim.units.validate_units(value, *, unit, name, scalar=False)[source]#

Check units and shape without converting or stripping the quantity.

Parameters:
  • value (object)

  • unit (str)

  • name (str)

  • scalar (bool)

Return type:

None

Archives#

Results save and load schema-2 NPZ archives with explicit SI units and no pickle. Unsupported schemas are rejected. Arrays and provenance are copied when results are constructed. Result.angular holds immutable directional data; .vectors on Directions exposes the observation configuration’s immutable Cartesian array.