blobmodel 1.2.1__tar.gz → 2.0.0__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.
- blobmodel-2.0.0/.github/workflows/workflow.yml +46 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/.readthedocs.yml +6 -4
- {blobmodel-1.2.1 → blobmodel-2.0.0}/PKG-INFO +19 -18
- {blobmodel-1.2.1 → blobmodel-2.0.0}/README.md +9 -11
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel/__init__.py +8 -2
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel/blob_shape.py +76 -22
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel/blobs.py +129 -70
- blobmodel-2.0.0/blobmodel/distributions.py +70 -0
- blobmodel-2.0.0/blobmodel/geometry.py +191 -0
- blobmodel-2.0.0/blobmodel/model.py +560 -0
- blobmodel-2.0.0/blobmodel/plotting.py +204 -0
- blobmodel-2.0.0/blobmodel/stochasticality.py +467 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel.egg-info/PKG-INFO +19 -18
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel.egg-info/SOURCES.txt +7 -3
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel.egg-info/requires.txt +5 -0
- blobmodel-2.0.0/docs/blob_factory.rst +143 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/blob_shapes.rst +13 -4
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/blob_tilt.rst +4 -4
- blobmodel-2.0.0/docs/changing_t_drain_plot.py +45 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/contributor_guide.rst +5 -2
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/create_logo.py +7 -13
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/drainage_time.rst +2 -1
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/getting_started.rst +11 -6
- blobmodel-2.0.0/docs/model_geometry.rst +52 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/one_dim.rst +2 -1
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/plot_pulses.py +2 -2
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/1d_animation.py +4 -10
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/2_sided_exp_pulse.py +5 -14
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/2d_animation.py +5 -11
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/analyse_output.py +3 -8
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/blob_tilting.py +25 -34
- blobmodel-2.0.0/examples/changing_t_drain.py +43 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/compare_to_analytical_sol.py +9 -13
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/custom_blobfactory.py +19 -16
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/point_process.py +5 -15
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/show_labels.py +3 -10
- {blobmodel-1.2.1 → blobmodel-2.0.0}/examples/single_blob.py +3 -9
- {blobmodel-1.2.1 → blobmodel-2.0.0}/pyproject.toml +10 -3
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_analytical.py +11 -12
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_blob.py +142 -10
- blobmodel-2.0.0/tests/test_blob_factories.py +164 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_changing_t_drain.py +10 -12
- blobmodel-2.0.0/tests/test_docs.py +332 -0
- blobmodel-2.0.0/tests/test_docs_scripts.py +29 -0
- blobmodel-2.0.0/tests/test_geometry.py +250 -0
- blobmodel-2.0.0/tests/test_imaging_layout.py +80 -0
- blobmodel-2.0.0/tests/test_input_validation.py +239 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_labels.py +23 -11
- blobmodel-2.0.0/tests/test_one_dim.py +94 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_pulse_shape.py +52 -3
- blobmodel-2.0.0/tests/test_seed.py +123 -0
- blobmodel-2.0.0/tests/test_show_model.py +129 -0
- blobmodel-2.0.0/tests/test_speed_up.py +349 -0
- blobmodel-2.0.0/tests/test_stochasticality.py +154 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_str.py +5 -11
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_vx_or_vy=0.py +14 -23
- blobmodel-1.2.1/.github/workflows/workflow.yml +0 -73
- blobmodel-1.2.1/blobmodel/distributions.py +0 -77
- blobmodel-1.2.1/blobmodel/geometry.py +0 -80
- blobmodel-1.2.1/blobmodel/model.py +0 -369
- blobmodel-1.2.1/blobmodel/plotting.py +0 -162
- blobmodel-1.2.1/blobmodel/stochasticality.py +0 -252
- blobmodel-1.2.1/docs/blob_factory.rst +0 -76
- blobmodel-1.2.1/docs/changing_t_drain_plot.py +0 -50
- blobmodel-1.2.1/docs/model_geometry.rst +0 -30
- blobmodel-1.2.1/docs/requirements.txt +0 -136
- blobmodel-1.2.1/examples/changing_t_drain.py +0 -54
- blobmodel-1.2.1/requirements.txt +0 -136
- blobmodel-1.2.1/tests/test_docs.py +0 -266
- blobmodel-1.2.1/tests/test_model.py +0 -81
- blobmodel-1.2.1/tests/test_one_dim.py +0 -71
- blobmodel-1.2.1/tests/test_show_model.py +0 -52
- blobmodel-1.2.1/tests/test_stochasticality.py +0 -42
- {blobmodel-1.2.1 → blobmodel-2.0.0}/.coveragerc +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/.github/workflows/draft-pdf.yml +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/.gitignore +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/LICENSE +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel.egg-info/dependency_links.txt +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/blobmodel.egg-info/top_level.txt +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/1d_animation.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/2-sided_pulse_shape.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/2d_animation.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/alignment_false.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/alignment_true.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/blob_labels.rst +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/blobmodel.rst +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/change_t_drain_plot.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/conf.py +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/index.rst +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/install.rst +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/labels_labels.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/labels_n.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/logo.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/modules.rst +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/pulse_shapes.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/visualize_model.rst +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/xarray_example.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/docs/y-periodicity.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/paper/paper.bib +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/paper/paper.md +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/readme_files/1d_blobs.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/readme_files/2d_blobs.gif +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/readme_files/logo.png +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/readme_files/tmp5kfvrjp5.svg +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/readme_files/tmpp0mi4n8p.svg +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/setup.cfg +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/.gitignore +0 -0
- {blobmodel-1.2.1 → blobmodel-2.0.0}/tests/test_drain.py +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
python-version: ['3.10', '3.11']
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
cache: 'pip'
|
|
21
|
+
cache-dependency-path: pyproject.toml
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install .[dev]
|
|
26
|
+
|
|
27
|
+
- name: Check formatting with black
|
|
28
|
+
if: matrix.python-version == '3.10'
|
|
29
|
+
run: |
|
|
30
|
+
black --version
|
|
31
|
+
black --check .
|
|
32
|
+
|
|
33
|
+
- name: Type checking with mypy
|
|
34
|
+
if: matrix.python-version == '3.10'
|
|
35
|
+
run: |
|
|
36
|
+
mypy --ignore-missing-imports blobmodel
|
|
37
|
+
|
|
38
|
+
- name: Test with pytest
|
|
39
|
+
run: |
|
|
40
|
+
pytest --cov --cov-report=xml
|
|
41
|
+
|
|
42
|
+
- name: Upload coverage to Codecov
|
|
43
|
+
if: matrix.python-version == '3.10'
|
|
44
|
+
uses: codecov/codecov-action@v4
|
|
45
|
+
with:
|
|
46
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -24,9 +24,11 @@ sphinx:
|
|
|
24
24
|
# - pdf
|
|
25
25
|
# - epub
|
|
26
26
|
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
|
27
|
+
# Install the package itself (autodoc imports it) together with the
|
|
28
|
+
# docs extra declared in pyproject.toml
|
|
30
29
|
python:
|
|
31
30
|
install:
|
|
32
|
-
-
|
|
31
|
+
- method: pip
|
|
32
|
+
path: .
|
|
33
|
+
extra_requirements:
|
|
34
|
+
- docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blobmodel
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Two dimensional model of propagating blobs
|
|
5
5
|
Author-email: gregordecristoforo <gregor.decristoforo@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -25,7 +25,7 @@ License: MIT License
|
|
|
25
25
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
26
|
SOFTWARE.
|
|
27
27
|
|
|
28
|
-
Requires-Python: >=3
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Requires-Dist: numpy
|
|
@@ -34,11 +34,14 @@ Requires-Dist: tqdm
|
|
|
34
34
|
Requires-Dist: nptyping
|
|
35
35
|
Requires-Dist: matplotlib
|
|
36
36
|
Requires-Dist: netcdf4
|
|
37
|
-
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist:
|
|
40
|
-
Requires-Dist:
|
|
41
|
-
Requires-Dist:
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
40
|
+
Requires-Dist: black; extra == "dev"
|
|
41
|
+
Requires-Dist: mypy; extra == "dev"
|
|
42
|
+
Provides-Extra: docs
|
|
43
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
44
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
42
45
|
Dynamic: license-file
|
|
43
46
|
|
|
44
47
|
# blobmodel <img align="left" src="https://github.com/uit-cosmo/blobmodel/blob/main/readme_files/logo.png" width="120px" >
|
|
@@ -97,9 +100,11 @@ python -m pip install -e .
|
|
|
97
100
|
Create a grid on which the blobs are discretized using the `Model` class. The `make_realization()` method computes the output as an xarray dataset which can also be written out as a `netcdf` file if the argument `file_name` is specified. A simple example is shown below:
|
|
98
101
|
|
|
99
102
|
```Python
|
|
100
|
-
from blobmodel import Model, show_model
|
|
103
|
+
from blobmodel import Geometry, Model, show_model
|
|
101
104
|
|
|
102
|
-
bm = Model(
|
|
105
|
+
bm = Model(
|
|
106
|
+
geometry=Geometry(Nx=200, Ny=100, Lx=10, Ly=10, dt=0.1, T=20), num_blobs=100
|
|
107
|
+
)
|
|
103
108
|
|
|
104
109
|
ds = bm.make_realization(file_name="example.nc")
|
|
105
110
|
```
|
|
@@ -110,21 +115,17 @@ show_model(ds)
|
|
|
110
115
|
You can specify the blob parameters with a BlobFactory class. The DefaultBlobFactory class has some of the most common distribution functions implemented. An example would look like this:
|
|
111
116
|
|
|
112
117
|
```Python
|
|
113
|
-
from blobmodel import DefaultBlobFactory, DistributionEnum, Model
|
|
118
|
+
from blobmodel import DefaultBlobFactory, DistributionEnum, Geometry, Model
|
|
114
119
|
|
|
115
120
|
# use DefaultBlobFactory to define distribution functions of random variables
|
|
116
|
-
my_blob_factory = DefaultBlobFactory(
|
|
121
|
+
my_blob_factory = DefaultBlobFactory(t_drain=100).set_sampler(
|
|
122
|
+
"amplitude", DistributionEnum.normal, free_parameter=5
|
|
123
|
+
)
|
|
117
124
|
|
|
118
125
|
# pass on my_blob_factory when creating the Model
|
|
119
126
|
bm = Model(
|
|
120
|
-
Nx=100,
|
|
121
|
-
Ny=100,
|
|
122
|
-
Lx=10,
|
|
123
|
-
Ly=10,
|
|
124
|
-
dt=0.1,
|
|
125
|
-
T=20,
|
|
127
|
+
geometry=Geometry(Nx=100, Ny=100, Lx=10, Ly=10, dt=0.1, T=20),
|
|
126
128
|
blob_factory=my_blob_factory,
|
|
127
|
-
t_drain=100,
|
|
128
129
|
num_blobs=100,
|
|
129
130
|
)
|
|
130
131
|
```
|
|
@@ -54,9 +54,11 @@ python -m pip install -e .
|
|
|
54
54
|
Create a grid on which the blobs are discretized using the `Model` class. The `make_realization()` method computes the output as an xarray dataset which can also be written out as a `netcdf` file if the argument `file_name` is specified. A simple example is shown below:
|
|
55
55
|
|
|
56
56
|
```Python
|
|
57
|
-
from blobmodel import Model, show_model
|
|
57
|
+
from blobmodel import Geometry, Model, show_model
|
|
58
58
|
|
|
59
|
-
bm = Model(
|
|
59
|
+
bm = Model(
|
|
60
|
+
geometry=Geometry(Nx=200, Ny=100, Lx=10, Ly=10, dt=0.1, T=20), num_blobs=100
|
|
61
|
+
)
|
|
60
62
|
|
|
61
63
|
ds = bm.make_realization(file_name="example.nc")
|
|
62
64
|
```
|
|
@@ -67,21 +69,17 @@ show_model(ds)
|
|
|
67
69
|
You can specify the blob parameters with a BlobFactory class. The DefaultBlobFactory class has some of the most common distribution functions implemented. An example would look like this:
|
|
68
70
|
|
|
69
71
|
```Python
|
|
70
|
-
from blobmodel import DefaultBlobFactory, DistributionEnum, Model
|
|
72
|
+
from blobmodel import DefaultBlobFactory, DistributionEnum, Geometry, Model
|
|
71
73
|
|
|
72
74
|
# use DefaultBlobFactory to define distribution functions of random variables
|
|
73
|
-
my_blob_factory = DefaultBlobFactory(
|
|
75
|
+
my_blob_factory = DefaultBlobFactory(t_drain=100).set_sampler(
|
|
76
|
+
"amplitude", DistributionEnum.normal, free_parameter=5
|
|
77
|
+
)
|
|
74
78
|
|
|
75
79
|
# pass on my_blob_factory when creating the Model
|
|
76
80
|
bm = Model(
|
|
77
|
-
Nx=100,
|
|
78
|
-
Ny=100,
|
|
79
|
-
Lx=10,
|
|
80
|
-
Ly=10,
|
|
81
|
-
dt=0.1,
|
|
82
|
-
T=20,
|
|
81
|
+
geometry=Geometry(Nx=100, Ny=100, Lx=10, Ly=10, dt=0.1, T=20),
|
|
83
82
|
blob_factory=my_blob_factory,
|
|
84
|
-
t_drain=100,
|
|
85
83
|
num_blobs=100,
|
|
86
84
|
)
|
|
87
85
|
```
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
from .model import Model
|
|
1
|
+
from .model import Model, to_imaging_dataset
|
|
2
2
|
from .blobs import Blob
|
|
3
3
|
from .plotting import show_model
|
|
4
|
-
from .stochasticality import
|
|
4
|
+
from .stochasticality import (
|
|
5
|
+
BlobFactory,
|
|
6
|
+
BlobListFactory,
|
|
7
|
+
CallableBlobFactory,
|
|
8
|
+
DefaultBlobFactory,
|
|
9
|
+
ParameterSampler,
|
|
10
|
+
)
|
|
5
11
|
from .geometry import Geometry
|
|
6
12
|
from .blob_shape import AbstractBlobShape, BlobShapeImpl, BlobShapeEnum
|
|
7
13
|
from .distributions import DistributionEnum
|
|
@@ -78,6 +78,19 @@ def _get_lorentz_shape(theta: np.ndarray, **kwargs) -> np.ndarray:
|
|
|
78
78
|
def _get_double_exponential_shape(theta: np.ndarray, **kwargs) -> np.ndarray:
|
|
79
79
|
"""Compute the double-exponential pulse shape.
|
|
80
80
|
|
|
81
|
+
The shape is ``exp(-theta / lam)`` for ``theta >= 0`` and
|
|
82
|
+
``exp(theta / (1 - lam))`` for ``theta < 0``, i.e. ``lam`` is the
|
|
83
|
+
e-folding fraction of the leading (``theta >= 0``) side of the pulse.
|
|
84
|
+
For a blob propagating with ``v_x > 0`` observed at a fixed position,
|
|
85
|
+
``lam`` is therefore the temporal *rise* fraction of the measured pulse,
|
|
86
|
+
matching the asymmetry-parameter convention of the FPP literature.
|
|
87
|
+
|
|
88
|
+
.. versionchanged:: 2.0.0
|
|
89
|
+
The convention was flipped: ``lam`` previously weighted the trailing
|
|
90
|
+
(``theta < 0``) side, so ``lam`` was the temporal *fall* fraction.
|
|
91
|
+
Code written against older versions that passed ``1 - lam`` to
|
|
92
|
+
compensate should now pass ``lam`` directly.
|
|
93
|
+
|
|
81
94
|
Parameters
|
|
82
95
|
----------
|
|
83
96
|
theta : np.ndarray
|
|
@@ -85,18 +98,29 @@ def _get_double_exponential_shape(theta: np.ndarray, **kwargs) -> np.ndarray:
|
|
|
85
98
|
kwargs
|
|
86
99
|
Additional keyword arguments.
|
|
87
100
|
lam : float
|
|
88
|
-
Asymmetry parameter controlling the shape.
|
|
101
|
+
Asymmetry parameter controlling the shape, in the interval [0, 1].
|
|
102
|
+
The limits are the one-sided shapes: lam = 0 is nonzero only for
|
|
103
|
+
theta < 0 (a pure temporal decay for v_x > 0), lam = 1 is nonzero
|
|
104
|
+
only for theta >= 0 (a pure temporal rise for v_x > 0).
|
|
89
105
|
|
|
90
106
|
Returns
|
|
91
107
|
-------
|
|
92
108
|
np.ndarray
|
|
93
109
|
Array representing the double-exponential pulse shape.
|
|
110
|
+
|
|
111
|
+
Raises
|
|
112
|
+
------
|
|
113
|
+
ValueError
|
|
114
|
+
If lam lies outside the interval [0, 1].
|
|
94
115
|
"""
|
|
95
116
|
lam = kwargs["lam"]
|
|
96
|
-
|
|
117
|
+
if not 0.0 <= lam <= 1.0:
|
|
118
|
+
raise ValueError(f"lam must be in the interval [0, 1], got lam = {lam}.")
|
|
97
119
|
kern = np.zeros(shape=np.shape(theta))
|
|
98
|
-
|
|
99
|
-
|
|
120
|
+
if lam < 1.0:
|
|
121
|
+
kern[theta < 0] = np.exp(theta[theta < 0] / (1 - lam))
|
|
122
|
+
if lam > 0.0:
|
|
123
|
+
kern[theta >= 0] = np.exp(-theta[theta >= 0] / lam)
|
|
100
124
|
return kern
|
|
101
125
|
|
|
102
126
|
|
|
@@ -171,43 +195,73 @@ def _get_dipole_shape(theta: np.ndarray, **kwargs) -> np.ndarray:
|
|
|
171
195
|
|
|
172
196
|
|
|
173
197
|
class BlobShapeImpl(AbstractBlobShape):
|
|
174
|
-
"""Implementation of the
|
|
198
|
+
"""Implementation of the AbstractBlobShape class."""
|
|
175
199
|
|
|
176
200
|
def __init__(
|
|
177
201
|
self,
|
|
178
|
-
pulse_shape_p=BlobShapeEnum.gaussian,
|
|
179
|
-
pulse_shape_s=BlobShapeEnum.gaussian,
|
|
202
|
+
pulse_shape_p: BlobShapeEnum = BlobShapeEnum.gaussian,
|
|
203
|
+
pulse_shape_s: BlobShapeEnum = BlobShapeEnum.gaussian,
|
|
180
204
|
):
|
|
181
205
|
"""Initialize the BlobShapeImpl object.
|
|
182
206
|
|
|
183
|
-
|
|
207
|
+
Parameters
|
|
184
208
|
----------
|
|
185
|
-
pulse_shape_p :
|
|
186
|
-
Type of pulse shape in the principal direction,
|
|
187
|
-
|
|
188
|
-
|
|
209
|
+
pulse_shape_p : BlobShapeEnum, optional
|
|
210
|
+
Type of pulse shape in the principal direction,
|
|
211
|
+
by default BlobShapeEnum.gaussian.
|
|
212
|
+
pulse_shape_s : BlobShapeEnum, optional
|
|
213
|
+
Type of pulse shape in the secondary direction,
|
|
214
|
+
by default BlobShapeEnum.gaussian.
|
|
215
|
+
|
|
216
|
+
Raises
|
|
217
|
+
------
|
|
218
|
+
NotImplementedError
|
|
219
|
+
If a pulse shape is not a member of BlobShapeEnum with an
|
|
220
|
+
implemented shape function.
|
|
189
221
|
"""
|
|
190
222
|
if (
|
|
191
|
-
pulse_shape_p not in BlobShapeImpl.__GENERATORS
|
|
192
|
-
or pulse_shape_s not in BlobShapeImpl.__GENERATORS
|
|
223
|
+
pulse_shape_p not in BlobShapeImpl.__GENERATORS
|
|
224
|
+
or pulse_shape_s not in BlobShapeImpl.__GENERATORS
|
|
193
225
|
):
|
|
194
226
|
raise NotImplementedError(
|
|
195
227
|
f"{self.__class__.__name__}.blob_shape not implemented"
|
|
196
228
|
)
|
|
197
|
-
self.
|
|
198
|
-
self.
|
|
229
|
+
self._shape_p = BlobShapeImpl.__GENERATORS[pulse_shape_p]
|
|
230
|
+
self._shape_s = BlobShapeImpl.__GENERATORS[pulse_shape_s]
|
|
199
231
|
|
|
200
232
|
def get_blob_shape_p(self, theta: np.ndarray, **kwargs) -> np.ndarray:
|
|
233
|
+
"""Compute the pulse shape in the principal direction.
|
|
234
|
+
|
|
235
|
+
Parameters
|
|
236
|
+
----------
|
|
237
|
+
theta : np.ndarray
|
|
238
|
+
Array of theta values.
|
|
239
|
+
kwargs
|
|
240
|
+
Additional keyword arguments passed to the shape function.
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
np.ndarray
|
|
245
|
+
Array representing the pulse shape in the principal direction.
|
|
201
246
|
"""
|
|
202
|
-
|
|
203
|
-
"""
|
|
204
|
-
raise NotImplementedError
|
|
247
|
+
return self._shape_p(theta, **kwargs)
|
|
205
248
|
|
|
206
249
|
def get_blob_shape_s(self, theta: np.ndarray, **kwargs) -> np.ndarray:
|
|
250
|
+
"""Compute the pulse shape in the secondary direction.
|
|
251
|
+
|
|
252
|
+
Parameters
|
|
253
|
+
----------
|
|
254
|
+
theta : np.ndarray
|
|
255
|
+
Array of theta values.
|
|
256
|
+
kwargs
|
|
257
|
+
Additional keyword arguments passed to the shape function.
|
|
258
|
+
|
|
259
|
+
Returns
|
|
260
|
+
-------
|
|
261
|
+
np.ndarray
|
|
262
|
+
Array representing the pulse shape in the secondary direction.
|
|
207
263
|
"""
|
|
208
|
-
|
|
209
|
-
"""
|
|
210
|
-
raise NotImplementedError
|
|
264
|
+
return self._shape_s(theta, **kwargs)
|
|
211
265
|
|
|
212
266
|
__GENERATORS = {
|
|
213
267
|
BlobShapeEnum.exp: _get_exponential_shape,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"""This module defines a Blob class and related functions for discretizing and manipulating blobs."""
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from typing import Union, Any
|
|
3
|
+
from typing import Union, Any, Optional
|
|
5
4
|
from nptyping import NDArray
|
|
6
5
|
import numpy as np
|
|
7
|
-
from .blob_shape import AbstractBlobShape
|
|
6
|
+
from .blob_shape import AbstractBlobShape, BlobShapeImpl
|
|
8
7
|
import cmath
|
|
9
8
|
|
|
10
9
|
|
|
@@ -14,7 +13,7 @@ class Blob:
|
|
|
14
13
|
the function `discretize_blob`. The contribution of a single blob to a grid defined by `x`, `y` and `t` is given by:
|
|
15
14
|
|
|
16
15
|
.. math::
|
|
17
|
-
a e^{-(t-t_k)/\\tau_\\shortparallel}\\varphi\\left( \\frac{x-v(t-t_k)}{\ell_x}, \\frac{(y-y_k)-w(t-t_k)}{\ell_y} \\right)
|
|
16
|
+
a e^{-(t-t_k)/\\tau_\\shortparallel}\\varphi\\left( \\frac{(x-x_k)-v(t-t_k)}{\ell_x}, \\frac{(y-y_k)-w(t-t_k)}{\ell_y} \\right)
|
|
18
17
|
|
|
19
18
|
Where:
|
|
20
19
|
- :math:`a` is the blob amplitude, `amplitude`.
|
|
@@ -22,76 +21,105 @@ class Blob:
|
|
|
22
21
|
- :math:`\ell_y` is the blob width in the secondary direction, `width_s`.
|
|
23
22
|
- :math:`v` is the horizontal blob velocity.
|
|
24
23
|
- :math:`w` is the vertical blob velocity.
|
|
25
|
-
- :math:`
|
|
24
|
+
- :math:`x_k` and :math:`y_k` are the blob position at time :math:`t_k`,
|
|
25
|
+
`pos_x0` and `pos_y0`.
|
|
26
|
+
- :math:`t_k` is the time at which the blob is seeded at
|
|
27
|
+
:math:`(x_k, y_k)`, `t_init`.
|
|
26
28
|
- :math:`\\tau_\\shortparallel` is the drainage time, `t_drain`.
|
|
27
29
|
- :math:`\\varphi` is the blob pulse shape, `blob_shape`.
|
|
28
30
|
|
|
29
|
-
Additionally, a tilt angle can be provided through `theta
|
|
31
|
+
Additionally, a tilt angle can be provided through `theta`, in which case
|
|
32
|
+
the two arguments of :math:`\\varphi` are rotated by `theta` around the
|
|
33
|
+
blob center.
|
|
30
34
|
"""
|
|
31
35
|
|
|
32
36
|
def __init__(
|
|
33
37
|
self,
|
|
34
|
-
blob_id: int,
|
|
35
|
-
blob_shape: AbstractBlobShape,
|
|
36
|
-
amplitude: float,
|
|
37
|
-
width_p: float,
|
|
38
|
-
width_s: float,
|
|
39
|
-
v_x: float,
|
|
40
|
-
v_y: float,
|
|
41
|
-
pos_x0: float,
|
|
42
|
-
pos_y0: float,
|
|
43
|
-
t_init: float,
|
|
44
|
-
t_drain: Union[float, NDArray],
|
|
38
|
+
blob_id: int = 0,
|
|
39
|
+
blob_shape: Optional[AbstractBlobShape] = None,
|
|
40
|
+
amplitude: float = 1.0,
|
|
41
|
+
width_p: float = 1.0,
|
|
42
|
+
width_s: float = 1.0,
|
|
43
|
+
v_x: float = 1.0,
|
|
44
|
+
v_y: float = 0.0,
|
|
45
|
+
pos_x0: float = 0.0,
|
|
46
|
+
pos_y0: float = 0.0,
|
|
47
|
+
t_init: float = 0.0,
|
|
48
|
+
t_drain: Union[float, NDArray] = np.inf,
|
|
45
49
|
shape_parameters_p: Union[dict, None] = None,
|
|
46
50
|
shape_parameters_s: Union[dict, None] = None,
|
|
47
51
|
blob_alignment: bool = False,
|
|
48
|
-
theta: float =
|
|
52
|
+
theta: Optional[float] = None,
|
|
49
53
|
) -> None:
|
|
50
54
|
"""
|
|
51
55
|
Initialize a single blob.
|
|
52
56
|
|
|
53
57
|
Parameters
|
|
54
58
|
----------
|
|
55
|
-
blob_id : int
|
|
56
|
-
Identifier for the blob.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
59
|
+
blob_id : int, optional
|
|
60
|
+
Identifier for the blob (default 0). Purely metadata: blob labels
|
|
61
|
+
in ``Model`` (``labels="individual"``) are assigned from each
|
|
62
|
+
blob's position in the factory output, not from ``blob_id``.
|
|
63
|
+
blob_shape : AbstractBlobShape, optional
|
|
64
|
+
Shape of the blob. Default None, which means a Gaussian shape in
|
|
65
|
+
both directions, ``BlobShapeImpl()``.
|
|
66
|
+
amplitude : float, optional
|
|
67
|
+
Amplitude of the blob. Default 1.
|
|
68
|
+
width_p : float, optional
|
|
69
|
+
Width of the blob in the propagation direction. Default 1.
|
|
70
|
+
width_s : float, optional
|
|
71
|
+
Width of the blob in the perpendicular direction. Default 1.
|
|
72
|
+
v_x : float, optional
|
|
73
|
+
Velocity of the blob in the x-direction. Default 1.
|
|
74
|
+
v_y : float, optional
|
|
75
|
+
Velocity of the blob in the y-direction. Default 0.
|
|
76
|
+
pos_x0 : float, optional
|
|
77
|
+
Initial position of the blob in the x-direction. Default 0.
|
|
78
|
+
pos_y0 : float, optional
|
|
79
|
+
Initial position of the blob in the y-direction. Default 0.
|
|
80
|
+
t_init : float, optional
|
|
81
|
+
Initial time of the blob. Default 0.
|
|
82
|
+
t_drain : Union[float, NDArray], optional
|
|
83
|
+
Time scale for the blob to drain. Either a scalar or a 1D
|
|
84
|
+
array-like of length Nx (one value per grid point in the
|
|
85
|
+
x-direction); array-likes are stored as float numpy arrays.
|
|
86
|
+
Default ``np.inf`` = no draining.
|
|
77
87
|
shape_parameters_p : dict
|
|
78
88
|
Additional shape parameters for the propagation direction.
|
|
79
89
|
shape_parameters_s : dict
|
|
80
90
|
Additional shape parameters for the perpendicular direction.
|
|
81
91
|
blob_alignment : bool, optional
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
Only used when ``theta is None``. If True, the blob shape is rotated to
|
|
93
|
+
align with the blob propagation direction; if False, the blob shape is
|
|
94
|
+
axis-aligned (no rotation). Default False.
|
|
95
|
+
theta : float or None, optional
|
|
96
|
+
Blob rotation angle with respect to the x-axis (not the velocity
|
|
97
|
+
vector). If not None, this angle is used directly and ``blob_alignment``
|
|
98
|
+
is ignored. If None (the default), the angle is determined by
|
|
99
|
+
``blob_alignment``: the velocity phase when it is True, or 0 when False.
|
|
100
|
+
|
|
101
|
+
Raises
|
|
102
|
+
------
|
|
103
|
+
TypeError
|
|
104
|
+
If ``blob_shape`` is not an ``AbstractBlobShape`` instance.
|
|
105
|
+
ValueError
|
|
106
|
+
If ``width_p`` or ``width_s`` is not positive, or if ``t_drain``
|
|
107
|
+
is not positive (every element, when it is an array).
|
|
90
108
|
|
|
91
109
|
"""
|
|
92
|
-
|
|
110
|
+
if blob_shape is None:
|
|
111
|
+
blob_shape = BlobShapeImpl()
|
|
112
|
+
if not isinstance(blob_shape, AbstractBlobShape):
|
|
113
|
+
raise TypeError(
|
|
114
|
+
f"blob_shape must be an AbstractBlobShape, got {type(blob_shape).__name__}."
|
|
115
|
+
)
|
|
116
|
+
if width_p <= 0 or width_s <= 0:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"Blob widths must be positive, got width_p = {width_p} and width_s = {width_s}."
|
|
119
|
+
)
|
|
120
|
+
if np.any(np.asarray(t_drain) <= 0):
|
|
121
|
+
raise ValueError(f"t_drain must be positive, got t_drain = {t_drain}.")
|
|
93
122
|
|
|
94
|
-
self.int = int
|
|
95
123
|
self.blob_id = blob_id
|
|
96
124
|
self.blob_shape = blob_shape
|
|
97
125
|
self.amplitude = amplitude
|
|
@@ -102,7 +130,14 @@ class Blob:
|
|
|
102
130
|
self.pos_x0 = pos_x0
|
|
103
131
|
self.pos_y0 = pos_y0
|
|
104
132
|
self.t_init = t_init
|
|
105
|
-
|
|
133
|
+
# Normalize to float scalar or float array so downstream code can
|
|
134
|
+
# rely on np.ndim and numpy indexing (lists and numpy integer
|
|
135
|
+
# scalars would otherwise fail deep inside _drain).
|
|
136
|
+
self.t_drain: Union[float, np.ndarray] = (
|
|
137
|
+
float(t_drain)
|
|
138
|
+
if np.ndim(t_drain) == 0
|
|
139
|
+
else np.asarray(t_drain, dtype=np.float64)
|
|
140
|
+
)
|
|
106
141
|
self.shape_parameters_p = (
|
|
107
142
|
{} if shape_parameters_p is None else shape_parameters_p
|
|
108
143
|
)
|
|
@@ -110,9 +145,19 @@ class Blob:
|
|
|
110
145
|
{} if shape_parameters_s is None else shape_parameters_s
|
|
111
146
|
)
|
|
112
147
|
self.blob_alignment = blob_alignment
|
|
113
|
-
|
|
114
|
-
|
|
148
|
+
if theta is not None:
|
|
149
|
+
self._theta = theta
|
|
150
|
+
elif blob_alignment:
|
|
115
151
|
self._theta = cmath.phase(self.v_x + self.v_y * 1j)
|
|
152
|
+
else:
|
|
153
|
+
self._theta = 0
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def theta(self) -> float:
|
|
157
|
+
"""float: Tilt angle used when discretizing the blob, measured from
|
|
158
|
+
the x-axis (read-only). Resolved at construction from the explicit
|
|
159
|
+
``theta`` argument or, when that is None, from ``blob_alignment``."""
|
|
160
|
+
return self._theta
|
|
116
161
|
|
|
117
162
|
def discretize_blob(
|
|
118
163
|
self,
|
|
@@ -122,6 +167,7 @@ class Blob:
|
|
|
122
167
|
Ly: float,
|
|
123
168
|
periodic_y: bool = False,
|
|
124
169
|
one_dimensional: bool = False,
|
|
170
|
+
y0: float = 0,
|
|
125
171
|
) -> NDArray:
|
|
126
172
|
"""
|
|
127
173
|
Discretize blob on grid. If one_dimensional the secondary pulse shape is ignored.
|
|
@@ -129,17 +175,28 @@ class Blob:
|
|
|
129
175
|
Parameters
|
|
130
176
|
----------
|
|
131
177
|
x : NDArray
|
|
132
|
-
Grid coordinates in the x-direction
|
|
178
|
+
Grid coordinates in the x-direction, as an array broadcastable to
|
|
179
|
+
shape (Ny, Nx, Nt): either a full meshgrid array or a 1D
|
|
180
|
+
coordinate array reshaped as ``x[np.newaxis, :, np.newaxis]``.
|
|
133
181
|
y : NDArray
|
|
134
|
-
Grid coordinates in the y-direction
|
|
182
|
+
Grid coordinates in the y-direction, as an array broadcastable to
|
|
183
|
+
shape (Ny, Nx, Nt): either a full meshgrid array or a 1D
|
|
184
|
+
coordinate array reshaped as ``y[:, np.newaxis, np.newaxis]``.
|
|
135
185
|
t : NDArray
|
|
136
|
-
Time coordinates
|
|
186
|
+
Time coordinates, as an array broadcastable to shape (Ny, Nx, Nt):
|
|
187
|
+
either a full meshgrid array or a 1D coordinate array reshaped as
|
|
188
|
+
``t[np.newaxis, np.newaxis, :]``. A scalar t (single time point)
|
|
189
|
+
is also accepted.
|
|
137
190
|
Ly : float
|
|
138
191
|
Length of domain in the y-direction.
|
|
139
192
|
periodic_y : bool, optional
|
|
140
193
|
Flag indicating periodicity in the y-direction (default: False).
|
|
141
194
|
one_dimensional : bool, optional
|
|
142
195
|
Flag indicating a one-dimensional blob (default: False).
|
|
196
|
+
y0 : float, optional
|
|
197
|
+
Origin of the domain in the y-direction (default: 0). Only used
|
|
198
|
+
when ``periodic_y`` is True, where the blob position is wrapped
|
|
199
|
+
into the domain ``[y0, y0 + Ly)``.
|
|
143
200
|
|
|
144
201
|
Notes
|
|
145
202
|
-----
|
|
@@ -149,25 +206,27 @@ class Blob:
|
|
|
149
206
|
Returns
|
|
150
207
|
-------
|
|
151
208
|
discretized_blob : NDArray
|
|
152
|
-
Discretized blob on a 3D array with dimensions (
|
|
209
|
+
Discretized blob on a 3D array with dimensions (y, x, t),
|
|
210
|
+
i.e. shape (Ny, Nx, Nt).
|
|
153
211
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
212
|
+
Raises
|
|
213
|
+
------
|
|
214
|
+
ValueError
|
|
215
|
+
If ``one_dimensional`` is True and ``Ly`` is not 0.
|
|
157
216
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
217
|
+
"""
|
|
218
|
+
if one_dimensional and Ly != 0:
|
|
219
|
+
raise ValueError(f"One dimensional blobs require Ly == 0, got Ly = {Ly}.")
|
|
162
220
|
|
|
163
221
|
if not periodic_y or one_dimensional:
|
|
164
222
|
return self._single_blob(
|
|
165
223
|
x, y, t, Ly, periodic_y, one_dimensional=one_dimensional
|
|
166
224
|
)
|
|
167
225
|
|
|
168
|
-
time = t if
|
|
169
|
-
vertical_prop = self.v_y * time + self.pos_y0
|
|
170
|
-
|
|
226
|
+
time = t if np.ndim(t) == 0 else t[0][0]
|
|
227
|
+
vertical_prop = self.v_y * (time - self.t_init) + self.pos_y0
|
|
228
|
+
# Wrap the blob position into the domain [y0, y0 + Ly).
|
|
229
|
+
number_of_y_propagations = (vertical_prop - y0) // Ly
|
|
171
230
|
|
|
172
231
|
# Sum of a centered blob is two "ghost blobs" at vertical positions +-Ly.
|
|
173
232
|
return (
|
|
@@ -277,9 +336,9 @@ class Blob:
|
|
|
277
336
|
Drain factor.
|
|
278
337
|
|
|
279
338
|
"""
|
|
280
|
-
if isinstance(self.t_drain,
|
|
281
|
-
return np.exp(-(t - self.t_init) /
|
|
282
|
-
return np.exp(-(t - self.t_init) / self.t_drain
|
|
339
|
+
if isinstance(self.t_drain, np.ndarray):
|
|
340
|
+
return np.exp(-(t - self.t_init) / self.t_drain[np.newaxis, :, np.newaxis])
|
|
341
|
+
return np.exp(-(t - self.t_init) / self.t_drain)
|
|
283
342
|
|
|
284
343
|
def _blob_trajectory_x(self, t: Union[int, NDArray]) -> Any:
|
|
285
344
|
"""
|