BornSim 0.2.6__tar.gz
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-0.2.6/.coveragerc +17 -0
- bornsim-0.2.6/.pre-commit-config.yaml +18 -0
- bornsim-0.2.6/.zenodo.json +18 -0
- bornsim-0.2.6/BornSim.egg-info/PKG-INFO +529 -0
- bornsim-0.2.6/BornSim.egg-info/SOURCES.txt +108 -0
- bornsim-0.2.6/BornSim.egg-info/dependency_links.txt +1 -0
- bornsim-0.2.6/BornSim.egg-info/requires.txt +24 -0
- bornsim-0.2.6/BornSim.egg-info/top_level.txt +1 -0
- bornsim-0.2.6/CITATION.cff +17 -0
- bornsim-0.2.6/CONTRIBUTING.md +23 -0
- bornsim-0.2.6/LICENSE +21 -0
- bornsim-0.2.6/MANIFEST.in +11 -0
- bornsim-0.2.6/PKG-INFO +529 -0
- bornsim-0.2.6/README.rst +488 -0
- bornsim-0.2.6/bornsim/__init__.py +69 -0
- bornsim-0.2.6/bornsim/_archives.py +172 -0
- bornsim-0.2.6/bornsim/_result_plotting.py +454 -0
- bornsim-0.2.6/bornsim/_result_validation.py +89 -0
- bornsim-0.2.6/bornsim/_validation.py +10 -0
- bornsim-0.2.6/bornsim/_version.py +1 -0
- bornsim-0.2.6/bornsim/_volume_plotting.py +395 -0
- bornsim-0.2.6/bornsim/angular_data.py +338 -0
- bornsim-0.2.6/bornsim/api.py +26 -0
- bornsim-0.2.6/bornsim/directions.py +99 -0
- bornsim-0.2.6/bornsim/ensemble.py +253 -0
- bornsim-0.2.6/bornsim/ensemble_sampling.py +75 -0
- bornsim-0.2.6/bornsim/geometry.py +716 -0
- bornsim-0.2.6/bornsim/green.py +161 -0
- bornsim-0.2.6/bornsim/grid.py +98 -0
- bornsim-0.2.6/bornsim/material.py +46 -0
- bornsim-0.2.6/bornsim/media.py +359 -0
- bornsim-0.2.6/bornsim/model.py +276 -0
- bornsim-0.2.6/bornsim/results.py +717 -0
- bornsim-0.2.6/bornsim/rotation.py +68 -0
- bornsim-0.2.6/bornsim/sampling.py +231 -0
- bornsim-0.2.6/bornsim/series.py +300 -0
- bornsim-0.2.6/bornsim/solver.py +561 -0
- bornsim-0.2.6/bornsim/source.py +57 -0
- bornsim-0.2.6/bornsim/units.py +81 -0
- bornsim-0.2.6/bornsim/volume.py +319 -0
- bornsim-0.2.6/conda.recipe/meta.yaml +36 -0
- bornsim-0.2.6/docs/examples/README.rst +22 -0
- bornsim-0.2.6/docs/examples/born_orders/README.rst +5 -0
- bornsim-0.2.6/docs/examples/born_orders/born_interference.py +120 -0
- bornsim-0.2.6/docs/examples/born_orders/compare_orders.py +80 -0
- bornsim-0.2.6/docs/examples/random_media/README.rst +5 -0
- bornsim-0.2.6/docs/examples/random_media/analytical_scattering.py +28 -0
- bornsim-0.2.6/docs/examples/random_media/covariance_models.py +90 -0
- bornsim-0.2.6/docs/examples/random_media/phase_function.py +81 -0
- bornsim-0.2.6/docs/examples/random_media/random_medium.py +154 -0
- bornsim-0.2.6/docs/examples/random_media/theory_random_fields.py +195 -0
- bornsim-0.2.6/docs/examples/random_media/wavelength_dependence.py +85 -0
- bornsim-0.2.6/docs/examples/results/README.rst +5 -0
- bornsim-0.2.6/docs/examples/results/result_plots.py +118 -0
- bornsim-0.2.6/docs/examples/results/save_load_results.py +104 -0
- bornsim-0.2.6/docs/examples/structured_media/README.rst +5 -0
- bornsim-0.2.6/docs/examples/structured_media/dielectric_sphere.py +139 -0
- bornsim-0.2.6/docs/examples/structured_media/structured_media.py +152 -0
- bornsim-0.2.6/docs/examples/structured_media/theory_directional_interference.py +146 -0
- bornsim-0.2.6/docs/examples/validation/README.rst +5 -0
- bornsim-0.2.6/docs/examples/validation/angular_convergence.py +140 -0
- bornsim-0.2.6/docs/examples/validation/ensemble_sampling.py +128 -0
- bornsim-0.2.6/docs/examples/validation/finite_size_comparison.py +114 -0
- bornsim-0.2.6/docs/examples/validation/grid_refinement.py +102 -0
- bornsim-0.2.6/docs/images/branding-prompts.rst +21 -0
- bornsim-0.2.6/docs/source/_static/favicon.png +0 -0
- bornsim-0.2.6/docs/source/_static/logo.png +0 -0
- bornsim-0.2.6/docs/source/_static/phase_function.svg +2429 -0
- bornsim-0.2.6/docs/source/api.rst +442 -0
- bornsim-0.2.6/docs/source/conf.py +154 -0
- bornsim-0.2.6/docs/source/development.rst +89 -0
- bornsim-0.2.6/docs/source/examples.rst +221 -0
- bornsim-0.2.6/docs/source/gallery_config.py +53 -0
- bornsim-0.2.6/docs/source/getting_started.rst +99 -0
- bornsim-0.2.6/docs/source/guide.rst +28 -0
- bornsim-0.2.6/docs/source/index.rst +50 -0
- bornsim-0.2.6/docs/source/medium_visualization.rst +358 -0
- bornsim-0.2.6/docs/source/overview.rst +2 -0
- bornsim-0.2.6/docs/source/resources.rst +41 -0
- bornsim-0.2.6/docs/source/theory.rst +566 -0
- bornsim-0.2.6/makefile +71 -0
- bornsim-0.2.6/pyproject.toml +52 -0
- bornsim-0.2.6/pytest.ini +19 -0
- bornsim-0.2.6/setup.cfg +4 -0
- bornsim-0.2.6/tests/analytical/test_api.py +363 -0
- bornsim-0.2.6/tests/analytical/test_model.py +169 -0
- bornsim-0.2.6/tests/analytical/test_result.py +242 -0
- bornsim-0.2.6/tests/analytical/test_units.py +289 -0
- bornsim-0.2.6/tests/conftest.py +15 -0
- bornsim-0.2.6/tests/numerical/test_api.py +215 -0
- bornsim-0.2.6/tests/numerical/test_api_keywords.py +120 -0
- bornsim-0.2.6/tests/numerical/test_api_ownership.py +263 -0
- bornsim-0.2.6/tests/numerical/test_configuration.py +443 -0
- bornsim-0.2.6/tests/numerical/test_directional_api.py +301 -0
- bornsim-0.2.6/tests/numerical/test_directions.py +130 -0
- bornsim-0.2.6/tests/numerical/test_engine.py +164 -0
- bornsim-0.2.6/tests/numerical/test_explicit_physics.py +108 -0
- bornsim-0.2.6/tests/numerical/test_geometry.py +478 -0
- bornsim-0.2.6/tests/numerical/test_media.py +371 -0
- bornsim-0.2.6/tests/numerical/test_medium_composition.py +238 -0
- bornsim-0.2.6/tests/numerical/test_medium_metadata.py +123 -0
- bornsim-0.2.6/tests/numerical/test_result.py +462 -0
- bornsim-0.2.6/tests/numerical/test_series.py +591 -0
- bornsim-0.2.6/tests/numerical/test_units.py +622 -0
- bornsim-0.2.6/tests/numerical/test_volume_plotting.py +444 -0
- bornsim-0.2.6/tests/packaging/test_release_tools.py +158 -0
- bornsim-0.2.6/tools/check_release.py +115 -0
- bornsim-0.2.6/tools/check_wheel.py +73 -0
- bornsim-0.2.6/tools/next_release_version.py +81 -0
- bornsim-0.2.6/tools/release_tag.py +201 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.6.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-merge-conflict
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: mixed-line-ending
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
exclude: ^conda.recipe/meta.yaml$
|
|
10
|
+
- id: check-toml
|
|
11
|
+
- id: detect-private-key
|
|
12
|
+
- id: check-json
|
|
13
|
+
- id: check-case-conflict
|
|
14
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
15
|
+
rev: v0.11.2
|
|
16
|
+
hooks:
|
|
17
|
+
- id: ruff
|
|
18
|
+
- id: ruff-format
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "BornSim",
|
|
3
|
+
"upload_type": "software",
|
|
4
|
+
"description": "Vector Born-series scattering from continuous random dielectric media.",
|
|
5
|
+
"creators": [
|
|
6
|
+
{
|
|
7
|
+
"name": "Poinsinet de Sivry-Houle, Martin"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"license": "mit",
|
|
11
|
+
"version": "0.2.6",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"Born approximation",
|
|
14
|
+
"light scattering",
|
|
15
|
+
"random media"
|
|
16
|
+
],
|
|
17
|
+
"publication_date": "2026-10-09"
|
|
18
|
+
}
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: BornSim
|
|
3
|
+
Version: 0.2.6
|
|
4
|
+
Summary: Vector Born-series scattering from continuous random dielectric media
|
|
5
|
+
Author-email: Martin Poinsinet de Sivry-Houle <martin.poinsinet.de.sivry@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: born approximation,light scattering,random media,tissue optics
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/x-rst
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy<3,>=1.24
|
|
21
|
+
Requires-Dist: TypedUnit==0.0.11
|
|
22
|
+
Requires-Dist: matplotlib<4,>=3.7
|
|
23
|
+
Requires-Dist: plotly<7,>=6
|
|
24
|
+
Provides-Extra: testing
|
|
25
|
+
Requires-Dist: pytest<10,>=8; extra == "testing"
|
|
26
|
+
Requires-Dist: pytest-cov<8,>=5; extra == "testing"
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: BornSim[testing]; extra == "test"
|
|
29
|
+
Provides-Extra: documentation
|
|
30
|
+
Requires-Dist: sphinx<10,>=7; extra == "documentation"
|
|
31
|
+
Requires-Dist: pydata-sphinx-theme<0.20,>=0.16; extra == "documentation"
|
|
32
|
+
Requires-Dist: sphinx-gallery<1,>=0.19; extra == "documentation"
|
|
33
|
+
Requires-Dist: sphinx-design<1,>=0.6; extra == "documentation"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: ruff<1,>=0.11; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy<2,>=1.11; extra == "dev"
|
|
37
|
+
Requires-Dist: build<2,>=1.2; extra == "dev"
|
|
38
|
+
Requires-Dist: twine<7,>=6; extra == "dev"
|
|
39
|
+
Requires-Dist: pre-commit<5,>=4; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
.. image:: docs/source/_static/logo.png
|
|
43
|
+
:alt: BornSim logo
|
|
44
|
+
:width: 420
|
|
45
|
+
|
|
46
|
+
.. documentation-content-start
|
|
47
|
+
|
|
48
|
+
.. list-table::
|
|
49
|
+
:widths: 35 65
|
|
50
|
+
:header-rows: 1
|
|
51
|
+
|
|
52
|
+
* - Badge
|
|
53
|
+
- Status
|
|
54
|
+
* - Python versions
|
|
55
|
+
- |python|
|
|
56
|
+
* - Documentation
|
|
57
|
+
- |docs|
|
|
58
|
+
* - Continuous integration
|
|
59
|
+
- |tests|
|
|
60
|
+
* - Static quality checks
|
|
61
|
+
- |quality|
|
|
62
|
+
* - Test coverage
|
|
63
|
+
- |coverage|
|
|
64
|
+
* - Latest release tag
|
|
65
|
+
- |release|
|
|
66
|
+
* - Package publication
|
|
67
|
+
- |publication|
|
|
68
|
+
* - License
|
|
69
|
+
- |license|
|
|
70
|
+
|
|
71
|
+
BornSim
|
|
72
|
+
========
|
|
73
|
+
|
|
74
|
+
A Python package for vector Born-series scattering from continuous, isotropic refractive-index fluctuations, such as idealized tissue models.
|
|
75
|
+
|
|
76
|
+
Install
|
|
77
|
+
-------
|
|
78
|
+
|
|
79
|
+
Requires Python 3.11 or newer.
|
|
80
|
+
|
|
81
|
+
.. code-block:: console
|
|
82
|
+
|
|
83
|
+
python -m venv .venv
|
|
84
|
+
.venv/bin/python -m pip install -e .
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
BornSim constructor, function, and method arguments are keyword-only, except
|
|
88
|
+
``add_structures(*structures)``, which takes positional shape objects.
|
|
89
|
+
Calls with multiple arguments use one argument per line and a trailing comma.
|
|
90
|
+
|
|
91
|
+
The Python API accepts TypedUnit quantities; dimensional inputs require explicit units. The wavelength is the **vacuum** wavelength. Outputs are angular differential scattering, μs, anisotropy g, and μs′, with scattering coefficients in inverse metres.
|
|
92
|
+
|
|
93
|
+
.. code-block:: python
|
|
94
|
+
|
|
95
|
+
import matplotlib.pyplot as plt
|
|
96
|
+
from bornsim import EnsembleSampling, Grid, RandomMedium, Source, Solver
|
|
97
|
+
from bornsim.units import ureg
|
|
98
|
+
|
|
99
|
+
grid = Grid(
|
|
100
|
+
shape=(8, 8, 8),
|
|
101
|
+
spacing=50 * ureg.nanometer,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
medium = RandomMedium(
|
|
105
|
+
background_refractive_index=1.33,
|
|
106
|
+
refractive_index_std=0.01,
|
|
107
|
+
correlation_length=100 * ureg.nanometer,
|
|
108
|
+
correlation="gaussian",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
solver = Solver(
|
|
112
|
+
source=Source(wavelength=633 * ureg.nanometer),
|
|
113
|
+
order=3,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
result = solver.ensemble(
|
|
117
|
+
medium=medium,
|
|
118
|
+
grid=grid,
|
|
119
|
+
ensemble_sampling=EnsembleSampling(
|
|
120
|
+
realizations=4,
|
|
121
|
+
seed=42,
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
print(
|
|
126
|
+
f"mu_s = {result.mu_s.to('1 / millimeter')}, "
|
|
127
|
+
f"g = {result.g}, "
|
|
128
|
+
f"mu_s_prime = {result.mu_s_prime.to('1 / millimeter')}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
result.plot()
|
|
132
|
+
|
|
133
|
+
plt.show()
|
|
134
|
+
|
|
135
|
+
``Source`` describes the supported unpolarized plane wave propagating along +z.
|
|
136
|
+
``RandomMedium`` defines numerical random-field statistics.
|
|
137
|
+
``Grid`` defines spatial shape and spacing once, and is shared by
|
|
138
|
+
``medium.to_volume(grid=grid)`` and ``solver.ensemble(grid=grid, ...)``.
|
|
139
|
+
Every ``Volume`` retains its grid. ``AngularSampling`` groups output polar
|
|
140
|
+
angles, polar integration nodes and azimuth resolution; pass it as
|
|
141
|
+
``Solver(sampling=sampling, ...)`` to share settings across calculations.
|
|
142
|
+
|
|
143
|
+
``Solver.solve(target=volume)`` computes full directional scattering and
|
|
144
|
+
solid-angle integrals by default, providing normalized 3D phase functions
|
|
145
|
+
for one fixed sample. Use ``solve_cut`` with explicit ``angles`` for an x-z
|
|
146
|
+
angular cut, or explicit unit ``directions`` for arbitrary observations. Neither cut
|
|
147
|
+
supplies integrated coefficients. Angle quantities may use degrees or radians.
|
|
148
|
+
``Solver.ensemble(medium=medium, grid=grid, ensemble_sampling=...)`` averages
|
|
149
|
+
independent seeded random volumes and returns standard errors. It accepts
|
|
150
|
+
random media and structures with a random background. Deterministic
|
|
151
|
+
structures generate a volume and use ``solve`` directly.
|
|
152
|
+
|
|
153
|
+
These calculations return a ``Result`` whose ``differential`` array has
|
|
154
|
+
shape (order, polar angle, azimuth) for full calculations. Its ``plot()`` returns a Matplotlib figure, with
|
|
155
|
+
one-standard-error bars for ensembles; ``plot(terms=True)`` shows isolated
|
|
156
|
+
numerical terms without interference or error bars. Call ``plt.show()`` to display the figures or
|
|
157
|
+
``figure.savefig("scattering.svg")`` to export a figure. Full-volume and
|
|
158
|
+
ensemble coefficients describe finite samples and remain distinct from
|
|
159
|
+
infinite-medium analytical ones.
|
|
160
|
+
Undefined anisotropy is ``NaN`` in this object API. Existing functions and
|
|
161
|
+
their return formats remain available.
|
|
162
|
+
|
|
163
|
+
``result.phase_function`` normalizes every sampled direction using its full
|
|
164
|
+
solid-angle integral. Full ``differential`` data have shape
|
|
165
|
+
``(order, polar angle, azimuth)``. Curves select one sampled meridian;
|
|
166
|
+
``result.azimuth_average()`` explicitly requests averaged intensities.
|
|
167
|
+
``result.plot_phase_function(view="3d")`` preserves directional asymmetry.
|
|
168
|
+
For notebook rotation, install the notebook extra and use
|
|
169
|
+
``%matplotlib widget``. ``result.amplitudes`` retains complex amplitudes with
|
|
170
|
+
the same observation axes followed by polarization and Cartesian axes.
|
|
171
|
+
Use ``solver.solve_cut`` for unnormalized angular cuts, and
|
|
172
|
+
``result.plot_cross_section()`` for finite-sample area per steradian without
|
|
173
|
+
passing the original Volume. Saved results retain the physical sample volume.
|
|
174
|
+
|
|
175
|
+
``result.plot_field_norms()`` shows numerical field terms per realization.
|
|
176
|
+
See ``docs/examples/results/result_plots.py`` for direct plotting examples in the
|
|
177
|
+
Sphinx Gallery documentation.
|
|
178
|
+
|
|
179
|
+
Results validate array shapes and physical ranges at construction.
|
|
180
|
+
``result.provenance`` records calculation settings, versions, seeds, grid
|
|
181
|
+
and quadrature information. ``result.save(path="scattering.npz")`` and
|
|
182
|
+
``Result.load(path="scattering.npz")`` preserve units, complex amplitudes,
|
|
183
|
+
uncertainty, and metadata without pickle. Input voxel fields are not stored;
|
|
184
|
+
retain manually supplied fields separately. See the numerical validation
|
|
185
|
+
gallery for independent refinement and sampling checks.
|
|
186
|
+
|
|
187
|
+
Units
|
|
188
|
+
-----
|
|
189
|
+
|
|
190
|
+
``bornsim.units`` exposes TypedUnit's shared ``ureg``, ``Quantity``, ``Length``,
|
|
191
|
+
``Angle``, ``Dimensionless``, and ``RefractiveIndex``, using the same registry
|
|
192
|
+
as PyMieSim. Physical inputs accept compatible units and reject incompatible
|
|
193
|
+
dimensions. Lengths and angles require explicit units. Refractive indices,
|
|
194
|
+
their standard deviations and fluctuation arrays require plain numbers without
|
|
195
|
+
units, including when passed to Material or shape constructors.
|
|
196
|
+
Physical configuration has no arbitrary defaults. Supply wavelengths,
|
|
197
|
+
refractive indices, fluctuation statistics, covariance choice, grid shape and
|
|
198
|
+
voxel spacing explicitly. ``StructuredMedium()`` starts as an empty builder;
|
|
199
|
+
call ``add_background(...)`` before generating a volume. Numerical controls
|
|
200
|
+
retain defaults, and zero centres and identity shape rotations define the
|
|
201
|
+
neutral coordinate conventions.
|
|
202
|
+
|
|
203
|
+
Physical settings and coordinates retain quantities in the supplied units.
|
|
204
|
+
Validation checks units and scalar shape without converting them. FFT and
|
|
205
|
+
Fourier kernels extract explicit SI magnitudes where numeric arrays are needed.
|
|
206
|
+
|
|
207
|
+
``Source.wavelength`` and every numerical array in ``Result`` are quantities.
|
|
208
|
+
Angles, amplitudes, scattering coefficients and uncertainties retain physical
|
|
209
|
+
units. Saved archives use radians, metres and inverse metres explicitly. Anisotropy, direction vectors, and relative
|
|
210
|
+
field norms are dimensionless. Use ``.to("unit")`` to convert and
|
|
211
|
+
``.magnitude`` to retrieve an array. Plots convert to degrees and SI
|
|
212
|
+
scattering units explicitly, including uncertainty bars.
|
|
213
|
+
|
|
214
|
+
Analytical functions return unit-bearing scattering coefficients.
|
|
215
|
+
``random_volume`` retains unit-bearing voxel spacing and coordinates.
|
|
216
|
+
The low-level Born and ensemble kernels document their numeric SI outputs.
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
Physical model
|
|
220
|
+
--------------
|
|
221
|
+
|
|
222
|
+
Real-valued refractive index fluctuations are linearized as δε ≈ 2 n₀ δn. For an unpolarized incident wave, the differential scattering coefficient is k₀⁴ Φε(q) (1 + cos²θ)/(32π²), where q = 2 n₀ k₀ sin(θ/2), k₀ = 2π/λvac and Φε is the three-dimensional Fourier transform of the dielectric covariance without a Fourier normalization prefactor.
|
|
223
|
+
|
|
224
|
+
The refractive index covariance is σn² exp(−r²/(2ℓ²)) for the Gaussian model and σn² exp(−r/ℓ) for the exponential model. These definitions matter when comparing correlation lengths between publications. Gauss–Legendre quadrature integrates over solid angle. Increase ``quadrature_order`` to check convergence for strongly forward-peaked scattering.
|
|
225
|
+
|
|
226
|
+
The analytical solver is a first-order, single-scattering model. The numerical solver includes repeated interactions through the chosen Born order. No slab-transmission observable or particle-packing generator is provided;
|
|
227
|
+
fixed particle configurations can be composed and propagated numerically. A dense medium can still have weak fluctuations; density alone does not establish Born validity. Contrast, correlation length, wavelength, and propagation distance matter. Decreasing Born terms do not certify convergence.
|
|
228
|
+
|
|
229
|
+
Scientific references:
|
|
230
|
+
|
|
231
|
+
- `Nonscalar elastic light scattering from continuous media in the Born approximation <https://pmc.ncbi.nlm.nih.gov/articles/PMC3839346/>`_.
|
|
232
|
+
- `Accuracy of the Born approximation in calculating the scattering coefficient of biological continuous random media <https://opg.optica.org/ol/abstract.cfm?uri=ol-34-17-2679>`_.
|
|
233
|
+
|
|
234
|
+
Check
|
|
235
|
+
-----
|
|
236
|
+
|
|
237
|
+
.. code-block:: console
|
|
238
|
+
|
|
239
|
+
.venv/bin/python -m pytest tests
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
Tests cover the analytic short-correlation limit, contrast scaling, independent angular integration, and input validation.
|
|
243
|
+
|
|
244
|
+
Numerical media and geometry
|
|
245
|
+
----------------------------
|
|
246
|
+
|
|
247
|
+
``RandomMedium`` generates numerical Gaussian random fields with Gaussian,
|
|
248
|
+
exponential, or Whittle–Matérn spatial covariance. Matérn ``smoothness`` uses
|
|
249
|
+
the convention ``x = sqrt(2 * nu) * r / correlation_length``; ``nu = 0.5``
|
|
250
|
+
reproduces exponential covariance. ``Medium`` is an abstract base class;
|
|
251
|
+
instantiate ``RandomMedium`` or ``StructuredMedium`` and call ``to_volume()``.
|
|
252
|
+
The existing analytical statistics class is named ``AnalyticalMedium``.
|
|
253
|
+
Old ``Medium(...)`` calls must be replaced; choose ``correlation="gaussian"``
|
|
254
|
+
for the analytical Gaussian model. Choose the covariance family explicitly. Matérn models also require smoothness.
|
|
255
|
+
|
|
256
|
+
``StructuredMedium`` voxelizes ordered ``Layer``, ``Sphere``, ``Ellipsoid``,
|
|
257
|
+
``Box``, and ``Cylinder`` regions. Regions specify absolute indices; later
|
|
258
|
+
regions replace earlier ones in overlaps. Layers are finite slabs clipped
|
|
259
|
+
to the box, with a uniform background outside the sample.
|
|
260
|
+
|
|
261
|
+
.. code-block:: python
|
|
262
|
+
|
|
263
|
+
from bornsim.units import ureg
|
|
264
|
+
|
|
265
|
+
from bornsim import Grid, RandomMedium, Layer, Sphere, StructuredMedium
|
|
266
|
+
from bornsim.media import random_volume
|
|
267
|
+
from bornsim import Solver, Source
|
|
268
|
+
|
|
269
|
+
random_sample = random_volume(
|
|
270
|
+
medium=RandomMedium(
|
|
271
|
+
correlation="matern",
|
|
272
|
+
smoothness=1.5,
|
|
273
|
+
background_refractive_index=1.33,
|
|
274
|
+
refractive_index_std=0.01,
|
|
275
|
+
correlation_length=100e-9 * ureg.meter,
|
|
276
|
+
),
|
|
277
|
+
grid=Grid(
|
|
278
|
+
shape=(12, 12, 12),
|
|
279
|
+
spacing=3e-08 * ureg.meter,
|
|
280
|
+
),
|
|
281
|
+
seed=42,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
structure = StructuredMedium()
|
|
285
|
+
|
|
286
|
+
structure.add_background(refractive_index=1.33)
|
|
287
|
+
|
|
288
|
+
layer = Layer(
|
|
289
|
+
lower=-1.8e-07 * ureg.meter,
|
|
290
|
+
upper=0 * ureg.meter,
|
|
291
|
+
refractive_index=1.34,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
sphere = Sphere(
|
|
295
|
+
radius=7e-08 * ureg.meter,
|
|
296
|
+
refractive_index=1.345,
|
|
297
|
+
centre=(0, 0, 4e-08) * ureg.meter,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
structure.add_structures(
|
|
301
|
+
layer,
|
|
302
|
+
sphere,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
structured_sample = structure.to_volume(
|
|
306
|
+
grid=Grid(
|
|
307
|
+
shape=(12, 12, 12),
|
|
308
|
+
spacing=3e-08 * ureg.meter,
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
solver = Solver(
|
|
313
|
+
source=Source(
|
|
314
|
+
wavelength=633e-9 * ureg.meter,
|
|
315
|
+
),
|
|
316
|
+
order=3,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
result = solver.solve(target=structured_sample)
|
|
320
|
+
|
|
321
|
+
``Solver`` delegates numerical propagation to ``BornSeries``, which reuses
|
|
322
|
+
its Green operator across compatible volumes. Ensemble calculations build
|
|
323
|
+
one engine for all realizations. Voxel fields, random generation, Green
|
|
324
|
+
propagation, Born iteration, and ensemble statistics live in separate modules.
|
|
325
|
+
|
|
326
|
+
The numerical method uses a vector volume integral on cubic voxels, rather
|
|
327
|
+
than finite elements. The documentation's numerical theory section derives
|
|
328
|
+
field synthesis, geometry masks, Born orders, the Green self cell, and
|
|
329
|
+
scattering normalization.
|
|
330
|
+
|
|
331
|
+
Three-dimensional media
|
|
332
|
+
-----------------------
|
|
333
|
+
|
|
334
|
+
``Volume.plot_3d()`` defaults to an interactive Plotly volume.
|
|
335
|
+
``result.plot_phase_function(view="3d")`` defaults to a Plotly surface.
|
|
336
|
+
Select ``backend="matplotlib"`` explicitly for static 3D figures, orthogonal
|
|
337
|
+
slices, or sampled voxel geometry such as spheres.
|
|
338
|
+
|
|
339
|
+
.. code-block:: python
|
|
340
|
+
|
|
341
|
+
from bornsim.units import ureg
|
|
342
|
+
|
|
343
|
+
from bornsim import Grid, RandomMedium
|
|
344
|
+
|
|
345
|
+
medium = RandomMedium(
|
|
346
|
+
background_refractive_index=1.33,
|
|
347
|
+
refractive_index_std=0.01,
|
|
348
|
+
correlation_length=100e-9 * ureg.meter,
|
|
349
|
+
correlation="matern",
|
|
350
|
+
smoothness=1.5,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
volume = medium.to_volume(
|
|
354
|
+
grid=Grid(
|
|
355
|
+
shape=(16, 16, 16),
|
|
356
|
+
spacing=2.5e-08 * ureg.meter,
|
|
357
|
+
),
|
|
358
|
+
seed=42,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
figure = volume.plot_3d(
|
|
362
|
+
field="refractive_index",
|
|
363
|
+
opacity_scale="increasing",
|
|
364
|
+
length_unit="nanometer",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
figure.show()
|
|
368
|
+
|
|
369
|
+
Matplotlib figures rotate with an interactive backend and can be exported
|
|
370
|
+
with ``figure.savefig()``. The documentation gallery embeds interactive 3D volumes and phase surfaces.
|
|
371
|
+
Plotly is included with BornSim and is the default for 3D views. Call
|
|
372
|
+
``figure.show()`` for browser interaction or ``figure.write_html()`` to export
|
|
373
|
+
an interactive view. Angular and polar result plots use Matplotlib.
|
|
374
|
+
See the medium visualization documentation for units and rendering conventions.
|
|
375
|
+
|
|
376
|
+
Numerical Born series
|
|
377
|
+
---------------------
|
|
378
|
+
|
|
379
|
+
Generate a seeded random volume and evaluate cumulative Born orders, or average independent realizations with one-standard-error estimates. Isolated term intensities exclude interference and must not be summed to obtain cumulative intensity.
|
|
380
|
+
|
|
381
|
+
.. code-block:: python
|
|
382
|
+
|
|
383
|
+
from bornsim.units import ureg
|
|
384
|
+
|
|
385
|
+
import numpy as np
|
|
386
|
+
from bornsim import Directions, EnsembleSampling, Grid, RandomMedium
|
|
387
|
+
from bornsim.series import BornSeries
|
|
388
|
+
from bornsim.media import random_volume
|
|
389
|
+
from bornsim.ensemble import ensemble_scattering
|
|
390
|
+
|
|
391
|
+
medium = RandomMedium(
|
|
392
|
+
refractive_index_std=0.01,
|
|
393
|
+
correlation_length=1e-07 * ureg.meter,
|
|
394
|
+
correlation="gaussian",
|
|
395
|
+
background_refractive_index=1.33,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
volume = random_volume(
|
|
399
|
+
medium=medium,
|
|
400
|
+
grid=Grid(
|
|
401
|
+
shape=(12, 12, 12),
|
|
402
|
+
spacing=5e-08 * ureg.meter,
|
|
403
|
+
),
|
|
404
|
+
seed=42,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
directions = Directions(vectors=[[0.0, 0.0, 1.0], [1.0, 0.0, 0.0]])
|
|
408
|
+
|
|
409
|
+
# directions.vectors is the immutable Cartesian array.
|
|
410
|
+
|
|
411
|
+
engine = BornSeries(
|
|
412
|
+
grid=volume.grid,
|
|
413
|
+
background_refractive_index=volume.background_refractive_index,
|
|
414
|
+
wavelength=6.33e-07 * ureg.meter,
|
|
415
|
+
directions=directions,
|
|
416
|
+
order=3,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
result = engine.solve(volume=volume)
|
|
420
|
+
|
|
421
|
+
# result.amplitudes[j] is the (j+1)-th term, in metres.
|
|
422
|
+
# result.differential[j] includes amplitude interference through order j+1.
|
|
423
|
+
ensemble = ensemble_scattering(
|
|
424
|
+
medium=medium,
|
|
425
|
+
wavelength=6.33e-07 * ureg.meter,
|
|
426
|
+
order=3,
|
|
427
|
+
ensemble_sampling=EnsembleSampling(
|
|
428
|
+
realizations=4,
|
|
429
|
+
seed=42,
|
|
430
|
+
),
|
|
431
|
+
shape=(12, 12, 12),
|
|
432
|
+
spacing=50e-9 * ureg.meter,
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
The API supports orders 1–12. Work limits bound synchronous calculations. Random fields have a **Gaussian probability distribution**, with Gaussian, exponential or Whittle–Matérn spatial covariance. Those are distinct choices. A spectral generator samples a doubled periodic box and crops it; ensemble point variance is normalized, but individual samples retain their random means and variances. Finite resolution truncates the spectrum, particularly for exponential covariance, and finite synthesis boxes approximate the continuum statistics.
|
|
436
|
+
|
|
437
|
+
The solver uses the outgoing dyadic background Green tensor, incident propagation along +z and an incoherent average over x/y incident polarizations. Zero-padded FFT convolution evaluates the finite-volume interactions without periodic propagation. The singular self cell uses an equal-volume sphere with the longitudinal contact term; off-diagonal cells use midpoint quadrature. This is an approximate voxel discretization, not a validated full-wave tissue simulator.
|
|
438
|
+
|
|
439
|
+
All orders use the same **linearized dielectric contrast** δε = 2 n₀ δn as the analytical model. They are perturbation orders in this dielectric contrast; the omitted δn² constitutive term is not restored by increasing Born order. Generated nonpositive dielectric values are rejected.
|
|
440
|
+
|
|
441
|
+
A first-order scattered amplitude is calculated from the incident field; subsequent amplitudes use successive applications of the Green operator. Amplitudes are summed before squaring. Ensemble averaging then averages intensities, never random amplitudes. The numerical coefficients are finite-sample cross sections divided by volume, and include the total scattered intensity; they are not automatically intrinsic infinite-medium transport coefficients. The mean anisotropy is calculated from averaged angular moments.
|
|
442
|
+
|
|
443
|
+
Numerical integration uses ``polar_samples`` Gauss nodes (default 32, range 16–128) and an azimuth count set by ``azimuth_samples`` (default 8). Plot angles are separately configurable. Check angular convergence for electrically large samples; increase ``polar_samples`` and ``azimuth_samples`` before using such samples quantitatively. Check grid refinement, sample size, synthesis-box effects and realization count as well. A single realization has unknown standard error (``NaN``). Growing field terms trigger a diagnostic; decreasing terms do not certify convergence. Modified/convergent Born series are not implemented.
|
|
444
|
+
|
|
445
|
+
Higher-order references:
|
|
446
|
+
|
|
447
|
+
- `Electromagnetic Born-series formulation and amplitude scaling <https://doi.org/10.1093/ptep/ptae008>`_.
|
|
448
|
+
- `Discrete-dipole and volume-integral discretization background <https://collaborate.princeton.edu/en/publications/discrete-dipole-approximation-for-scattering-calculations/>`_.
|
|
449
|
+
- `Modified convergent Born series, a distinct future solver <https://arxiv.org/abs/1601.05997>`_.
|
|
450
|
+
|
|
451
|
+
Regression checks compare open-boundary FFT interactions against an independently assembled dyadic matrix, compare orders 1–3 against a direct linear solve, verify per-order contrast scaling and interference, test first-order grid refinement, check random-field statistics, and verify ensemble means and standard errors.
|
|
452
|
+
|
|
453
|
+
Repository layout
|
|
454
|
+
-----------------
|
|
455
|
+
|
|
456
|
+
.. code-block:: text
|
|
457
|
+
|
|
458
|
+
bornsim/ Public API, numerical solvers, Matplotlib plots
|
|
459
|
+
tests/analytical/ First-order analytical regression tests
|
|
460
|
+
tests/numerical/ Independent Born-series and random-field checks
|
|
461
|
+
tests/packaging/ Metadata and release-tool tests
|
|
462
|
+
docs/source/ Sphinx documentation
|
|
463
|
+
docs/examples/ Runnable examples
|
|
464
|
+
development/ Exploratory work excluded from distributions
|
|
465
|
+
tools/ Release and wheel-verification commands
|
|
466
|
+
conda.recipe/ Conda package recipe
|
|
467
|
+
.github/workflows/ Quality, tests, documentation and publication checks
|
|
468
|
+
|
|
469
|
+
Development
|
|
470
|
+
-----------
|
|
471
|
+
|
|
472
|
+
.. code-block:: console
|
|
473
|
+
|
|
474
|
+
make editable PYTHON=.venv/bin/python
|
|
475
|
+
make check PYTHON=.venv/bin/python
|
|
476
|
+
make docs PYTHON=.venv/bin/python
|
|
477
|
+
make package-check PYTHON=.venv/bin/python
|
|
478
|
+
make release-check PYTHON=.venv/bin/python
|
|
479
|
+
|
|
480
|
+
See ``CONTRIBUTING.md`` for development and release instructions. The package
|
|
481
|
+
uses the MIT license in ``LICENSE``; software citation metadata is provided in
|
|
482
|
+
``CITATION.cff``. The repository currently has no configured GitHub remote.
|
|
483
|
+
|
|
484
|
+
API configuration and data ownership
|
|
485
|
+
------------------------------------
|
|
486
|
+
|
|
487
|
+
Use Grid, AngularSampling and EnsembleSampling to define spatial, angular and
|
|
488
|
+
realization settings. Legacy individual configuration keywords are deprecated.
|
|
489
|
+
EnsembleSampling accepts consecutive seeds or an explicit list of distinct
|
|
490
|
+
seeds. Material objects share a real absolute refractive index across shapes.
|
|
491
|
+
Results and their arrays are read-only; StructuredMedium remains mutable.
|
|
492
|
+
``result.angular`` owns the directional data and is reused on every access.
|
|
493
|
+
``result.meridian(azimuth=90 * ureg.degree)`` selects sampled directions using
|
|
494
|
+
physical angles. Exact selection is the default; nearest selection must be
|
|
495
|
+
requested explicitly and never interpolates or averages intensities.
|
|
496
|
+
|
|
497
|
+
Theory and simulated figures
|
|
498
|
+
----------------------------
|
|
499
|
+
|
|
500
|
+
The documentation's theory section derives the vector volume integral, Born
|
|
501
|
+
orders, coherent amplitudes, phase normalization and ensemble uncertainty.
|
|
502
|
+
Simulated figures connect these equations to random-field covariance,
|
|
503
|
+
translation invariance, two-particle interference and voxel refinement.
|
|
504
|
+
The figures link to runnable examples and state their numerical limitations.
|
|
505
|
+
|
|
506
|
+
.. |python| image:: https://img.shields.io/badge/Python-3.11%2B-3776AB.svg
|
|
507
|
+
:alt: Python 3.11 or newer
|
|
508
|
+
:target: https://www.python.org/
|
|
509
|
+
.. |docs| image:: https://github.com/MartinPdeS/BornSim/actions/workflows/deploy_documentation.yml/badge.svg
|
|
510
|
+
:alt: Documentation build status
|
|
511
|
+
:target: https://martinpdes.github.io/BornSim/docs/latest/
|
|
512
|
+
.. |tests| image:: https://github.com/MartinPdeS/BornSim/actions/workflows/tests.yml/badge.svg
|
|
513
|
+
:alt: Test status
|
|
514
|
+
:target: https://github.com/MartinPdeS/BornSim/actions/workflows/tests.yml
|
|
515
|
+
.. |quality| image:: https://github.com/MartinPdeS/BornSim/actions/workflows/quality.yml/badge.svg
|
|
516
|
+
:alt: Static quality check status
|
|
517
|
+
:target: https://github.com/MartinPdeS/BornSim/actions/workflows/quality.yml
|
|
518
|
+
.. |coverage| image:: https://raw.githubusercontent.com/MartinPdeS/BornSim/python-coverage-comment-action-data/badge.svg
|
|
519
|
+
:alt: Test coverage
|
|
520
|
+
:target: https://github.com/MartinPdeS/BornSim/actions/workflows/deploy_coverage.yml
|
|
521
|
+
.. |release| image:: https://img.shields.io/github/v/tag/MartinPdeS/BornSim.svg
|
|
522
|
+
:alt: Latest release tag
|
|
523
|
+
:target: https://github.com/MartinPdeS/BornSim/tags
|
|
524
|
+
.. |publication| image:: https://github.com/MartinPdeS/BornSim/actions/workflows/deploy_release.yml/badge.svg
|
|
525
|
+
:alt: Package publication status
|
|
526
|
+
:target: https://github.com/MartinPdeS/BornSim/actions/workflows/deploy_release.yml
|
|
527
|
+
.. |license| image:: https://img.shields.io/github/license/MartinPdeS/BornSim.svg
|
|
528
|
+
:alt: MIT license
|
|
529
|
+
:target: https://github.com/MartinPdeS/BornSim/blob/master/LICENSE
|