BornSim 0.2.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bornsim/__init__.py +69 -0
- bornsim/_archives.py +172 -0
- bornsim/_result_plotting.py +454 -0
- bornsim/_result_validation.py +89 -0
- bornsim/_validation.py +10 -0
- bornsim/_version.py +1 -0
- bornsim/_volume_plotting.py +395 -0
- bornsim/angular_data.py +338 -0
- bornsim/api.py +26 -0
- bornsim/directions.py +99 -0
- bornsim/ensemble.py +253 -0
- bornsim/ensemble_sampling.py +75 -0
- bornsim/geometry.py +716 -0
- bornsim/green.py +161 -0
- bornsim/grid.py +98 -0
- bornsim/material.py +46 -0
- bornsim/media.py +359 -0
- bornsim/model.py +276 -0
- bornsim/results.py +717 -0
- bornsim/rotation.py +68 -0
- bornsim/sampling.py +231 -0
- bornsim/series.py +300 -0
- bornsim/solver.py +561 -0
- bornsim/source.py +57 -0
- bornsim/units.py +81 -0
- bornsim/volume.py +319 -0
- bornsim-0.2.6.dist-info/METADATA +529 -0
- bornsim-0.2.6.dist-info/RECORD +31 -0
- bornsim-0.2.6.dist-info/WHEEL +5 -0
- bornsim-0.2.6.dist-info/licenses/LICENSE +21 -0
- bornsim-0.2.6.dist-info/top_level.txt +1 -0
bornsim/volume.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"""Finite refractive-index fields on centred cubic voxel grids."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
import numpy as np
|
|
5
|
+
from ._validation import _integer
|
|
6
|
+
from .grid import Grid
|
|
7
|
+
from .media import RandomMedium
|
|
8
|
+
from .units import _refractive_index_values, Quantity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, kw_only=True)
|
|
12
|
+
class Volume:
|
|
13
|
+
"""Represent a finite refractive-index fluctuation field on cubic voxels.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
delta_refractive_index : array_like
|
|
18
|
+
Finite three-dimensional refractive index fluctuations relative to the background,
|
|
19
|
+
with 2 to 32 cells per axis. Quantities are rejected, including dimensionless ones.
|
|
20
|
+
grid : Grid, optional
|
|
21
|
+
Shared spatial configuration. Its shape must match delta_refractive_index.
|
|
22
|
+
Supply either grid or spacing.
|
|
23
|
+
spacing : Quantity, optional
|
|
24
|
+
Positive, finite cubic voxel width. Explicit length units are required.
|
|
25
|
+
Legacy alternative to supplying grid; shape is inferred from the array.
|
|
26
|
+
background_refractive_index : float
|
|
27
|
+
Positive, finite background refractive index. Plain numbers are required; quantities are rejected. A background must be supplied.
|
|
28
|
+
medium : RandomMedium, optional
|
|
29
|
+
Generation statistics recorded by ``random_volume``. None for a
|
|
30
|
+
manually supplied field. Must be supplied together with ``seed``.
|
|
31
|
+
seed : int, optional
|
|
32
|
+
Random seed recorded by ``random_volume``. None for a manual field.
|
|
33
|
+
|
|
34
|
+
Attributes
|
|
35
|
+
----------
|
|
36
|
+
delta_refractive_index : numpy.ndarray
|
|
37
|
+
Copied, read-only, dimensionless fluctuation array of shape (nx, ny, nz).
|
|
38
|
+
grid : Grid
|
|
39
|
+
Spatial configuration shared with generation and scattering.
|
|
40
|
+
spacing : Quantity
|
|
41
|
+
Cubic voxel width with its supplied units, also available as grid.spacing.
|
|
42
|
+
background_refractive_index : float
|
|
43
|
+
Dimensionless background refractive index.
|
|
44
|
+
medium : RandomMedium or None
|
|
45
|
+
Recorded generation statistics, when available.
|
|
46
|
+
seed : int or None
|
|
47
|
+
Recorded generation seed, when available.
|
|
48
|
+
|
|
49
|
+
Raises
|
|
50
|
+
------
|
|
51
|
+
ValueError
|
|
52
|
+
If the field or grid is invalid, units are incompatible, or linearized
|
|
53
|
+
relative permittivity is nonpositive in any voxel.
|
|
54
|
+
TypeError
|
|
55
|
+
If recorded generation statistics are not a RandomMedium.
|
|
56
|
+
|
|
57
|
+
See Also
|
|
58
|
+
--------
|
|
59
|
+
bornsim.media.random_volume : Generate a seeded sample from medium statistics.
|
|
60
|
+
bornsim.series.BornSeries.solve : Compute coherent scattering from the sample.
|
|
61
|
+
|
|
62
|
+
Notes
|
|
63
|
+
-----
|
|
64
|
+
Relative permittivity is ``background_refractive_index**2 + 2*background_refractive_index*delta_refractive_index``.
|
|
65
|
+
The quadratic refractive-index fluctuation term is omitted at every Born order.
|
|
66
|
+
Voxel centres are measured relative to the sample centre.
|
|
67
|
+
|
|
68
|
+
Examples
|
|
69
|
+
--------
|
|
70
|
+
>>> import numpy as np
|
|
71
|
+
>>> from bornsim import Volume
|
|
72
|
+
>>> from bornsim.units import ureg
|
|
73
|
+
...
|
|
74
|
+
...
|
|
75
|
+
>>> volume = Volume(
|
|
76
|
+
... delta_refractive_index=np.zeros((2, 2, 2)),
|
|
77
|
+
... spacing=50 * ureg.nanometer,
|
|
78
|
+
... background_refractive_index=1.33,
|
|
79
|
+
... )
|
|
80
|
+
>>> volume.positions.shape
|
|
81
|
+
(2, 2, 2, 3)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
delta_refractive_index: np.ndarray
|
|
85
|
+
background_refractive_index: float
|
|
86
|
+
spacing: Quantity = None
|
|
87
|
+
grid: Grid | None = None
|
|
88
|
+
medium: RandomMedium | None = None
|
|
89
|
+
seed: int | None = None
|
|
90
|
+
|
|
91
|
+
def __post_init__(self) -> None:
|
|
92
|
+
data = np.array(
|
|
93
|
+
_refractive_index_values(
|
|
94
|
+
value=self.delta_refractive_index,
|
|
95
|
+
name="delta_refractive_index",
|
|
96
|
+
),
|
|
97
|
+
copy=True,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if data.ndim != 3 or any(n < 2 or n > 32 for n in data.shape) or not np.all(np.isfinite(data)):
|
|
101
|
+
raise ValueError("delta_refractive_index must be a finite 3D array with 2–32 cells per axis.")
|
|
102
|
+
|
|
103
|
+
if self.grid is None:
|
|
104
|
+
if self.spacing is None:
|
|
105
|
+
raise ValueError("Supply grid or spacing for a Volume.")
|
|
106
|
+
|
|
107
|
+
grid = Grid(
|
|
108
|
+
shape=data.shape,
|
|
109
|
+
spacing=self.spacing,
|
|
110
|
+
)
|
|
111
|
+
else:
|
|
112
|
+
grid = Grid._resolve(
|
|
113
|
+
grid=self.grid,
|
|
114
|
+
spacing=self.spacing,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if grid.shape != data.shape:
|
|
118
|
+
raise ValueError("grid shape must match delta_refractive_index.")
|
|
119
|
+
|
|
120
|
+
object.__setattr__(self, "grid", grid)
|
|
121
|
+
|
|
122
|
+
object.__setattr__(self, "spacing", grid.spacing)
|
|
123
|
+
|
|
124
|
+
object.__setattr__(
|
|
125
|
+
self,
|
|
126
|
+
"background_refractive_index",
|
|
127
|
+
_refractive_index_values(
|
|
128
|
+
value=self.background_refractive_index,
|
|
129
|
+
name="background_refractive_index",
|
|
130
|
+
scalar=True,
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if not np.isfinite(self.background_refractive_index) or self.background_refractive_index <= 0:
|
|
135
|
+
raise ValueError("background_refractive_index must be finite and positive.")
|
|
136
|
+
|
|
137
|
+
if np.any(self.background_refractive_index**2 + 2 * self.background_refractive_index * data <= 0):
|
|
138
|
+
raise ValueError("Linearized relative permittivity must stay positive.")
|
|
139
|
+
|
|
140
|
+
if (self.medium is None) != (self.seed is None):
|
|
141
|
+
raise ValueError("medium and seed must be supplied together.")
|
|
142
|
+
|
|
143
|
+
if self.medium is not None:
|
|
144
|
+
if not isinstance(self.medium, RandomMedium):
|
|
145
|
+
raise TypeError("medium must be a RandomMedium.")
|
|
146
|
+
|
|
147
|
+
if self.medium.background_refractive_index != self.background_refractive_index:
|
|
148
|
+
raise ValueError("medium background_refractive_index must match the volume.")
|
|
149
|
+
|
|
150
|
+
object.__setattr__(
|
|
151
|
+
self,
|
|
152
|
+
"seed",
|
|
153
|
+
_integer(
|
|
154
|
+
value=self.seed,
|
|
155
|
+
name="seed",
|
|
156
|
+
low=0,
|
|
157
|
+
high=2**32 - 1,
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
data.setflags(write=False)
|
|
162
|
+
|
|
163
|
+
object.__setattr__(self, "delta_refractive_index", data)
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def positions(self) -> Quantity:
|
|
167
|
+
"""Return voxel-centre coordinates relative to the sample centre.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
positions : Quantity
|
|
172
|
+
Unit-bearing coordinates, shape (nx, ny, nz, 3), with the final
|
|
173
|
+
axis ordered as x, y, z.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
return getattr(self, "grid").positions
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def volume(self) -> Quantity:
|
|
180
|
+
"""Return the total physical volume of all voxels.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
volume : Quantity
|
|
185
|
+
Unit-bearing sample volume, equal to ``delta_refractive_index.size*spacing**3``.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
return getattr(self, "grid").volume
|
|
189
|
+
|
|
190
|
+
def plot_slice(self, *, normal="z", index=None, field="refractive_index", length_unit="nanometer", title=None):
|
|
191
|
+
"""Build a Matplotlib figure of one voxel plane.
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
normal : {'x', 'y', 'z'}, optional
|
|
196
|
+
Axis perpendicular to the plane; default 'z'.
|
|
197
|
+
index : int, optional
|
|
198
|
+
Voxel index along the normal. Defaults to n//2, the positive
|
|
199
|
+
central plane for an even grid. The title gives its actual position.
|
|
200
|
+
field : {'refractive_index', 'delta_refractive_index', 'permittivity'}, optional
|
|
201
|
+
Display n0 + delta_refractive_index, fluctuations, or linearized relative
|
|
202
|
+
permittivity n0**2 + 2*n0*delta_refractive_index. Default 'refractive_index'.
|
|
203
|
+
length_unit : str, optional
|
|
204
|
+
Spatial display unit; default 'nanometer'. Stored SI data is unchanged.
|
|
205
|
+
title : str, optional
|
|
206
|
+
Override the title describing the field and plane position.
|
|
207
|
+
|
|
208
|
+
Returns
|
|
209
|
+
-------
|
|
210
|
+
figure : matplotlib.figure.Figure
|
|
211
|
+
Equal-aspect image with voxel-edge extents and a labelled colorbar.
|
|
212
|
+
Call pyplot.show() to display or figure.savefig() to export.
|
|
213
|
+
Colors use the full volume's range to compare different planes;
|
|
214
|
+
fluctuation colors are symmetric about zero.
|
|
215
|
+
|
|
216
|
+
Raises
|
|
217
|
+
------
|
|
218
|
+
ValueError
|
|
219
|
+
If the plane, index, field, or length unit is invalid.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
from ._volume_plotting import _VolumePlotter
|
|
223
|
+
|
|
224
|
+
return _VolumePlotter(
|
|
225
|
+
volume=self,
|
|
226
|
+
).plot_slice(
|
|
227
|
+
normal=normal,
|
|
228
|
+
index=index,
|
|
229
|
+
field=field,
|
|
230
|
+
length_unit=length_unit,
|
|
231
|
+
title=title,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def plot_3d(
|
|
235
|
+
self,
|
|
236
|
+
*,
|
|
237
|
+
backend="plotly",
|
|
238
|
+
mode=None,
|
|
239
|
+
field="delta_refractive_index",
|
|
240
|
+
length_unit="nanometer",
|
|
241
|
+
surface_count=8,
|
|
242
|
+
opacity=None,
|
|
243
|
+
opacity_scale="uniform",
|
|
244
|
+
slice_indices=None,
|
|
245
|
+
):
|
|
246
|
+
"""Build a three-dimensional figure of the finite voxel medium.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
backend : {'matplotlib', 'plotly'}, optional
|
|
251
|
+
Default is Plotly for browser interaction. Select 'matplotlib'
|
|
252
|
+
explicitly for a Matplotlib figure.
|
|
253
|
+
mode : str, optional
|
|
254
|
+
Matplotlib supports 'slices' (default) and 'voxels'. Voxels display
|
|
255
|
+
cells with nonzero refractive index contrast, or the full box for a zero field.
|
|
256
|
+
Plotly supports 'volume' (default), 'isosurface', and 'slices'.
|
|
257
|
+
Uniform Plotly fields fall back to slices.
|
|
258
|
+
field : {'delta_refractive_index', 'refractive_index', 'permittivity'}, optional
|
|
259
|
+
Scalar field to display, default refractive index fluctuation. 'refractive_index' displays
|
|
260
|
+
n0 + delta_refractive_index; 'permittivity' displays the solver's linearized
|
|
261
|
+
relative permittivity n0**2 + 2*n0*delta_refractive_index, not (n0+delta_refractive_index)**2.
|
|
262
|
+
length_unit : str, optional
|
|
263
|
+
Spatial display unit, default 'nanometer'. Stored SI data is unchanged.
|
|
264
|
+
surface_count : int, optional
|
|
265
|
+
Number of contours, from 1 to 32; default 8. More surfaces increase
|
|
266
|
+
browser rendering cost. Used only by Plotly volume and isosurface modes.
|
|
267
|
+
opacity : float, optional
|
|
268
|
+
Surface opacity in (0, 1]. Default is 1 for Matplotlib and 0.15
|
|
269
|
+
for Plotly. Slices are opaque in both backends.
|
|
270
|
+
opacity_scale : {'uniform', 'increasing'}, optional
|
|
271
|
+
Default 'uniform' gives all contours the same opacity. 'increasing'
|
|
272
|
+
is available only for Plotly volume mode and scales opacity from
|
|
273
|
+
zero at the lowest displayed contour to ``opacity`` at the highest.
|
|
274
|
+
Use field='refractive_index' to emphasize high refractive index, rather than
|
|
275
|
+
the magnitude of positive and negative fluctuations.
|
|
276
|
+
slice_indices : tuple of int, optional
|
|
277
|
+
One voxel index for each x, y, z plane, used only for slices.
|
|
278
|
+
Defaults to the middle voxel along each axis.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
figure : matplotlib.figure.Figure or plotly.graph_objects.Figure
|
|
283
|
+
Matplotlib: call pyplot.show() to display or figure.savefig() to
|
|
284
|
+
export. Rotation needs an interactive Matplotlib backend; gallery
|
|
285
|
+
images are static. Plotly: call figure.show() or figure.write_html().
|
|
286
|
+
Constructing a figure opens no window or browser.
|
|
287
|
+
|
|
288
|
+
Raises
|
|
289
|
+
------
|
|
290
|
+
ValueError
|
|
291
|
+
If a mode, scalar field, display unit, or rendering setting is invalid.
|
|
292
|
+
|
|
293
|
+
Notes
|
|
294
|
+
-----
|
|
295
|
+
Matplotlib voxels preserve staircase interfaces; slices display every
|
|
296
|
+
selected cell with voxel-edge extents. Dense transparent scenes can have
|
|
297
|
+
depth-ordering limitations in Matplotlib. Plotly contours interpolate
|
|
298
|
+
between voxel centres and are visualization aids,
|
|
299
|
+
not exact curved interfaces or a replacement for grid refinement.
|
|
300
|
+
Slices show sampled values. Aspect ratio follows physical dimensions.
|
|
301
|
+
All voxels are retained, with at most 32**3 samples under Volume's grid
|
|
302
|
+
limit; browser performance also depends on surface count and graphics
|
|
303
|
+
support. This plots the input material, not an electromagnetic field.
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
from ._volume_plotting import _VolumePlotter
|
|
307
|
+
|
|
308
|
+
return _VolumePlotter(
|
|
309
|
+
volume=self,
|
|
310
|
+
).plot_3d(
|
|
311
|
+
backend=backend,
|
|
312
|
+
mode=mode,
|
|
313
|
+
field=field,
|
|
314
|
+
length_unit=length_unit,
|
|
315
|
+
surface_count=surface_count,
|
|
316
|
+
opacity=opacity,
|
|
317
|
+
opacity_scale=opacity_scale,
|
|
318
|
+
slice_indices=slice_indices,
|
|
319
|
+
)
|