Note
Go to the end to download the full example code.
Layers and scatterers with numerical random fluctuations#
Compose finite slabs and shaped inclusions using absolute refractive indices. Later regions replace earlier ones where they overlap. Add a seeded Whittle–Matérn fluctuation field over the complete structure, then solve numerically. The background outside the voxel box stays homogeneous. This is a finite sample with edges, not an infinite planar stack calculation.
All Born orders retain the linearized contrast 2*n0*delta_refractive_index. The
smoothness convention is x=sqrt(2*nu)*r/ell; probability statistics remain
Gaussian. The sampled structured volume records its fluctuation statistics and
seed as provenance.
from bornsim.medium.random_medium import WhittleMaternMedium
import sys
import matplotlib.pyplot as plt
import numpy as np
from bornsim import Directions, Grid, Layer, Solver, Source, Sphere, StructuredMedium
from bornsim.units import ureg
grid = Grid.from_extent(
shape=(16, 16, 16),
extent=(400, 400, 400) * ureg.nanometer,
)
structure = StructuredMedium()
structure.add_background(refractive_index=1.33)
layer = Layer.from_center_and_thickness(
centre=(0, 0, -100) * ureg.nanometer,
thickness=200 * ureg.nanometer,
refractive_index=1.335,
)
layer_2 = Layer.from_center_and_thickness(
centre=(0, 0, 100) * ureg.nanometer,
thickness=200 * ureg.nanometer,
refractive_index=1.34,
)
sphere = Sphere(
radius=7e-08 * ureg.meter,
refractive_index=1.345,
centre=(0, 0, 5e-08) * ureg.meter,
)
structure.add_structures(
layer,
layer_2,
sphere,
)
structured = structure.to_volume(
grid=grid,
)
statistics = WhittleMaternMedium(
refractive_index_std=0.002,
correlation_length=5e-08 * ureg.meter,
smoothness=1.5,
background_refractive_index=1.33,
)
structure_with_fluctuations = StructuredMedium()
structure_with_fluctuations.add_background(refractive_index=1.33)
structure_with_fluctuations.add_structures(
layer,
layer_2,
sphere,
)
structure_with_fluctuations.add_fluctuations(medium=statistics)
combined = structure_with_fluctuations.to_volume(
grid=grid,
seed=42,
)
Inspect the structured medium in 3D#
Inspect the deterministic layers and sphere before adding fluctuations. Drag to rotate and scroll to zoom in the embedded browser volume.
medium_figure = structured.plot_3d(
mode="volume",
field="refractive_index",
opacity_scale="increasing",
)
if "--no-browser" not in sys.argv:
medium_figure.show(renderer="browser")
Inspect the combined medium in 3D#
This interactive volume shows the sampled refractive index used by the solver, including the same seeded Matérn fluctuations as the scattering curves. Regions with higher refractive index are more opaque; opacity is a display setting.
medium_figure = combined.plot_3d(
mode="volume",
field="refractive_index",
opacity_scale="increasing",
)
if "--no-browser" not in sys.argv:
medium_figure.show(renderer="browser")
Scattering curves and a diagnostic cross-section#
The slice checks exact voxel values after exploring the full volumes above.
figure, axes = plt.subplots(1, 2, figsize=(10, 4), layout="constrained")
image = axes[0].imshow(
combined.refractive_index[:, grid.shape[1] // 2, :].T,
origin="lower",
extent=(-200, 200, -200, 200),
cmap="viridis",
)
axes[0].set(xlabel="x (nm)", ylabel="z (nm)", title="Layers, sphere and Matérn fluctuations")
figure.colorbar(image, ax=axes[0], label="Refractive index")
source_configuration_1 = Source(wavelength=633 * ureg.nanometer)
solver = Solver(
source=source_configuration_1,
order=3,
)
angles = np.linspace(0, 180, 91) * ureg.degree
directions = Directions.from_angles(
polar_angles=angles,
azimuth_angles=0 * ureg.degree,
)
result = solver.solve(
target=combined,
sampling=directions,
)
cross_section = result.differential_cross_section.to("nanometer**2 / steradian")
for order, curve in enumerate(cross_section.magnitude, start=1):
axes[1].plot(
angles.to("degree").magnitude,
curve,
label=f"Through order {order}",
)
axes[1].set(xlabel="Scattering angle (degrees)", ylabel="dσ/dΩ (nm² sr⁻¹)")
axes[1].legend(frameon=False)
plt.show()

Total running time of the script: (0 minutes 0.461 seconds)