AMS-BP 0.0.231__py3-none-any.whl → 0.2.0__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.
- AMS_BP/__init__.py +1 -1
- AMS_BP/cells/__init__.py +30 -4
- AMS_BP/cells/budding_yeast_cell.py +274 -0
- AMS_BP/cells/cell_factory.py +148 -0
- AMS_BP/configio/configmodels.py +9 -6
- AMS_BP/configio/convertconfig.py +37 -29
- AMS_BP/groundtruth_generators/__init__.py +3 -0
- AMS_BP/groundtruth_generators/nuclearporecomplexes.py +68 -0
- AMS_BP/motion/condensate_movement.py +28 -29
- AMS_BP/motion/movement/__init__.py +1 -6
- AMS_BP/motion/movement/boundary_conditions.py +12 -1
- AMS_BP/motion/track_gen.py +35 -84
- AMS_BP/optics/lasers/laser_profiles.py +27 -185
- AMS_BP/optics/lasers/scanning_patterns.py +102 -0
- AMS_BP/optics/psf/psf_engine.py +25 -8
- AMS_BP/photophysics/photon_physics.py +4 -4
- AMS_BP/photophysics/state_kinetics.py +37 -2
- AMS_BP/probabilityfuncs/probability_functions.py +33 -100
- AMS_BP/sample/sim_sampleplane.py +55 -24
- AMS_BP/sim_config.toml +13 -8
- AMS_BP/sim_microscopy.py +40 -9
- AMS_BP/utils/util_functions.py +9 -0
- {ams_bp-0.0.231.dist-info → ams_bp-0.2.0.dist-info}/METADATA +25 -4
- {ams_bp-0.0.231.dist-info → ams_bp-0.2.0.dist-info}/RECORD +27 -27
- AMS_BP/cells/base_cell.py +0 -55
- AMS_BP/cells/rectangular_cell.py +0 -82
- AMS_BP/cells/rod_cell.py +0 -98
- AMS_BP/cells/spherical_cell.py +0 -74
- AMS_BP/motion/movement/fbm_BP.py +0 -244
- {ams_bp-0.0.231.dist-info → ams_bp-0.2.0.dist-info}/WHEEL +0 -0
- {ams_bp-0.0.231.dist-info → ams_bp-0.2.0.dist-info}/entry_points.txt +0 -0
- {ams_bp-0.0.231.dist-info → ams_bp-0.2.0.dist-info}/licenses/LICENSE +0 -0
AMS_BP/cells/base_cell.py
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from dataclasses import dataclass
|
3
|
-
from typing import Tuple
|
4
|
-
import numpy as np
|
5
|
-
|
6
|
-
|
7
|
-
@dataclass
|
8
|
-
class BaseCell(ABC):
|
9
|
-
"""
|
10
|
-
Abstract base class for all cell types.
|
11
|
-
|
12
|
-
Attributes:
|
13
|
-
origin (np.ndarray): The (x, y) coordinates of the cell's origin in XY plane
|
14
|
-
"""
|
15
|
-
|
16
|
-
origin: np.ndarray
|
17
|
-
|
18
|
-
def __post_init__(self):
|
19
|
-
"""Validate inputs and convert to numpy arrays if needed."""
|
20
|
-
self.origin = np.array(self.origin, dtype=float)
|
21
|
-
if self.origin.shape != (2,):
|
22
|
-
raise ValueError("Origin must be a 2D point (x,y)")
|
23
|
-
self._validate_specific()
|
24
|
-
self._volume = self._calculate_volume()
|
25
|
-
|
26
|
-
@abstractmethod
|
27
|
-
def _validate_specific(self) -> None:
|
28
|
-
"""Validate cell-specific parameters."""
|
29
|
-
pass
|
30
|
-
|
31
|
-
@abstractmethod
|
32
|
-
def _calculate_volume(self) -> float:
|
33
|
-
"""Calculate the volume of the cell."""
|
34
|
-
pass
|
35
|
-
|
36
|
-
@property
|
37
|
-
@abstractmethod
|
38
|
-
def center(self) -> np.ndarray:
|
39
|
-
"""Get the center point of the cell."""
|
40
|
-
pass
|
41
|
-
|
42
|
-
@abstractmethod
|
43
|
-
def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
|
44
|
-
"""Get the minimum and maximum points that define the cell's bounds."""
|
45
|
-
pass
|
46
|
-
|
47
|
-
@abstractmethod
|
48
|
-
def contains_point(self, point: np.ndarray) -> bool:
|
49
|
-
"""Check if a point lies within the cell."""
|
50
|
-
pass
|
51
|
-
|
52
|
-
@property
|
53
|
-
def volume(self) -> float:
|
54
|
-
"""Get the pre-calculated volume of the cell."""
|
55
|
-
return self._volume
|
AMS_BP/cells/rectangular_cell.py
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
import numpy as np
|
3
|
-
from .base_cell import BaseCell
|
4
|
-
from typing import Tuple
|
5
|
-
|
6
|
-
|
7
|
-
@dataclass
|
8
|
-
class RectangularCell(BaseCell):
|
9
|
-
"""
|
10
|
-
Represents a rectangular cell in 3D space.
|
11
|
-
|
12
|
-
Attributes:
|
13
|
-
origin (np.ndarray): The (x, y) coordinates of the cell's origin in XY plane
|
14
|
-
dimensions (np.ndarray): The (length, width, height) of the cell
|
15
|
-
"""
|
16
|
-
|
17
|
-
dimensions: np.ndarray
|
18
|
-
|
19
|
-
def _validate_specific(self) -> None:
|
20
|
-
"""Validate rectangle-specific parameters."""
|
21
|
-
self.dimensions = np.array(self.dimensions, dtype=float)
|
22
|
-
if self.dimensions.shape != (3,):
|
23
|
-
raise ValueError("Dimensions must be a 3D vector (length, width, height)")
|
24
|
-
if np.any(self.dimensions <= 0):
|
25
|
-
raise ValueError("All dimensions must be positive")
|
26
|
-
|
27
|
-
def _calculate_volume(self) -> float:
|
28
|
-
"""Calculate the volume of the rectangular cell."""
|
29
|
-
return float(np.prod(self.dimensions))
|
30
|
-
|
31
|
-
@property
|
32
|
-
def center(self) -> np.ndarray:
|
33
|
-
"""Get the center point of the rectangular cell."""
|
34
|
-
return np.array(
|
35
|
-
[
|
36
|
-
self.origin[0] + self.dimensions[0] / 2,
|
37
|
-
self.origin[1] + self.dimensions[1] / 2,
|
38
|
-
0.0,
|
39
|
-
]
|
40
|
-
)
|
41
|
-
|
42
|
-
def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
|
43
|
-
"""
|
44
|
-
Get the minimum and maximum points that define the cell's bounds.
|
45
|
-
|
46
|
-
Returns:
|
47
|
-
Tuple containing (min_point, max_point)
|
48
|
-
"""
|
49
|
-
min_point = np.array(
|
50
|
-
[
|
51
|
-
self.origin[0],
|
52
|
-
self.origin[1],
|
53
|
-
-self.dimensions[2] / 2, # Z extends downward from 0
|
54
|
-
]
|
55
|
-
)
|
56
|
-
|
57
|
-
max_point = np.array(
|
58
|
-
[
|
59
|
-
self.origin[0] + self.dimensions[0],
|
60
|
-
self.origin[1] + self.dimensions[1],
|
61
|
-
self.dimensions[2] / 2, # Z extends upward from 0
|
62
|
-
]
|
63
|
-
)
|
64
|
-
|
65
|
-
return min_point, max_point
|
66
|
-
|
67
|
-
def contains_point(self, point: np.ndarray) -> bool:
|
68
|
-
"""
|
69
|
-
Check if a point lies within the rectangular cell.
|
70
|
-
|
71
|
-
Args:
|
72
|
-
point: A 3D point to check
|
73
|
-
|
74
|
-
Returns:
|
75
|
-
bool: True if the point is inside the cell, False otherwise
|
76
|
-
"""
|
77
|
-
point = np.array(point)
|
78
|
-
if point.shape != (3,):
|
79
|
-
raise ValueError("Point must be a 3D point")
|
80
|
-
|
81
|
-
min_point, max_point = self.get_bounds()
|
82
|
-
return np.all(point >= min_point) and np.all(point <= max_point)
|
AMS_BP/cells/rod_cell.py
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
import numpy as np
|
3
|
-
from .base_cell import BaseCell
|
4
|
-
from typing import Tuple
|
5
|
-
|
6
|
-
|
7
|
-
@dataclass
|
8
|
-
class RodCell(BaseCell):
|
9
|
-
"""
|
10
|
-
Represents a rod-like cell in 3D space.
|
11
|
-
|
12
|
-
Attributes:
|
13
|
-
origin (np.ndarray): The (x, y) coordinates of the cell's origin in XY plane
|
14
|
-
length (float): Total length of the rod (including end caps)
|
15
|
-
radius (float): Radius of both the cylindrical body and hemispheres
|
16
|
-
"""
|
17
|
-
|
18
|
-
length: float
|
19
|
-
radius: float
|
20
|
-
|
21
|
-
def _validate_specific(self) -> None:
|
22
|
-
"""Validate rod-specific parameters."""
|
23
|
-
if self.length <= 0:
|
24
|
-
raise ValueError("Length must be positive")
|
25
|
-
if self.radius <= 0:
|
26
|
-
raise ValueError("Radius must be positive")
|
27
|
-
|
28
|
-
def _calculate_volume(self) -> float:
|
29
|
-
"""Calculate the volume of the rod."""
|
30
|
-
cylinder_volume = np.pi * self.radius**2 * (self.length - 2 * self.radius)
|
31
|
-
sphere_volume = (4 / 3) * np.pi * self.radius**3
|
32
|
-
return cylinder_volume + sphere_volume
|
33
|
-
|
34
|
-
@property
|
35
|
-
def center(self) -> np.ndarray:
|
36
|
-
"""Get the center point of the rod."""
|
37
|
-
return np.array([self.origin[0] + self.length / 2, self.origin[1], 0.0])
|
38
|
-
|
39
|
-
def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
|
40
|
-
"""
|
41
|
-
Get the minimum and maximum points that define the rod's bounding box.
|
42
|
-
|
43
|
-
Returns:
|
44
|
-
Tuple containing (min_point, max_point)
|
45
|
-
"""
|
46
|
-
min_point = np.array(
|
47
|
-
[
|
48
|
-
self.origin[0],
|
49
|
-
self.origin[1] - self.radius,
|
50
|
-
-self.radius, # Z extends downward from 0
|
51
|
-
]
|
52
|
-
)
|
53
|
-
|
54
|
-
max_point = np.array(
|
55
|
-
[
|
56
|
-
self.origin[0] + self.length,
|
57
|
-
self.origin[1] + self.radius,
|
58
|
-
self.radius, # Z extends upward from 0
|
59
|
-
]
|
60
|
-
)
|
61
|
-
|
62
|
-
return min_point, max_point
|
63
|
-
|
64
|
-
def contains_point(self, point: np.ndarray) -> bool:
|
65
|
-
"""
|
66
|
-
Check if a point lies within the rod.
|
67
|
-
|
68
|
-
Args:
|
69
|
-
point: A 3D point to check
|
70
|
-
|
71
|
-
Returns:
|
72
|
-
bool: True if the point is inside the rod, False otherwise
|
73
|
-
"""
|
74
|
-
point = np.array(point)
|
75
|
-
if point.shape != (3,):
|
76
|
-
raise ValueError("Point must be a 3D point")
|
77
|
-
|
78
|
-
# Project point onto XY plane for cylinder check
|
79
|
-
x, y, z = point - np.array([self.origin[0], self.origin[1], 0])
|
80
|
-
|
81
|
-
# Check if point is within the length bounds
|
82
|
-
if x < 0 or x > self.length:
|
83
|
-
return False
|
84
|
-
|
85
|
-
# If point is within the cylindrical section
|
86
|
-
if self.radius <= x <= (self.length - self.radius):
|
87
|
-
# Check distance from central axis
|
88
|
-
return np.sqrt(y**2 + z**2) <= self.radius
|
89
|
-
|
90
|
-
# Check left hemisphere (if x < radius)
|
91
|
-
elif x < self.radius:
|
92
|
-
center = np.array([self.radius, 0, 0])
|
93
|
-
return np.linalg.norm(np.array([x, y, z]) - center) <= self.radius
|
94
|
-
|
95
|
-
# Check right hemisphere (if x > length - radius)
|
96
|
-
else:
|
97
|
-
center = np.array([self.length - self.radius, 0, 0])
|
98
|
-
return np.linalg.norm(np.array([x, y, z]) - center) <= self.radius
|
AMS_BP/cells/spherical_cell.py
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
from dataclasses import dataclass
|
2
|
-
import numpy as np
|
3
|
-
from .base_cell import BaseCell
|
4
|
-
from typing import Tuple
|
5
|
-
|
6
|
-
|
7
|
-
@dataclass
|
8
|
-
class SphericalCell(BaseCell):
|
9
|
-
"""
|
10
|
-
Represents a spherical cell in 3D space, centered around Z=0.
|
11
|
-
|
12
|
-
Attributes:
|
13
|
-
origin (np.ndarray): The (x, y) coordinates of the cell's center in XY plane
|
14
|
-
radius (float): Radius of the sphere
|
15
|
-
"""
|
16
|
-
|
17
|
-
radius: float
|
18
|
-
|
19
|
-
def _validate_specific(self) -> None:
|
20
|
-
"""Validate sphere-specific parameters."""
|
21
|
-
if self.radius <= 0:
|
22
|
-
raise ValueError("Radius must be positive")
|
23
|
-
|
24
|
-
def _calculate_volume(self) -> float:
|
25
|
-
"""Calculate the volume of the sphere."""
|
26
|
-
return (4 / 3) * np.pi * self.radius**3
|
27
|
-
|
28
|
-
@property
|
29
|
-
def center(self) -> np.ndarray:
|
30
|
-
"""Get the center point of the sphere."""
|
31
|
-
return np.array([self.origin[0], self.origin[1], 0.0])
|
32
|
-
|
33
|
-
def get_bounds(self) -> Tuple[np.ndarray, np.ndarray]:
|
34
|
-
"""
|
35
|
-
Get the minimum and maximum points that define the sphere's bounding box.
|
36
|
-
|
37
|
-
Returns:
|
38
|
-
Tuple containing (min_point, max_point)
|
39
|
-
"""
|
40
|
-
min_point = np.array(
|
41
|
-
[
|
42
|
-
self.origin[0] - self.radius,
|
43
|
-
self.origin[1] - self.radius,
|
44
|
-
-self.radius, # Z extends downward from 0
|
45
|
-
]
|
46
|
-
)
|
47
|
-
|
48
|
-
max_point = np.array(
|
49
|
-
[
|
50
|
-
self.origin[0] + self.radius,
|
51
|
-
self.origin[1] + self.radius,
|
52
|
-
self.radius, # Z extends upward from 0
|
53
|
-
]
|
54
|
-
)
|
55
|
-
|
56
|
-
return min_point, max_point
|
57
|
-
|
58
|
-
def contains_point(self, point: np.ndarray) -> bool:
|
59
|
-
"""
|
60
|
-
Check if a point lies within the sphere.
|
61
|
-
|
62
|
-
Args:
|
63
|
-
point: A 3D point to check
|
64
|
-
|
65
|
-
Returns:
|
66
|
-
bool: True if the point is inside the sphere, False otherwise
|
67
|
-
"""
|
68
|
-
point = np.array(point)
|
69
|
-
if point.shape != (3,):
|
70
|
-
raise ValueError("Point must be a 3D point")
|
71
|
-
|
72
|
-
# Calculate distance from center to point
|
73
|
-
center_3d = self.center
|
74
|
-
return np.linalg.norm(point - center_3d) <= self.radius
|
AMS_BP/motion/movement/fbm_BP.py
DELETED
@@ -1,244 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
from .boundary_conditions import _refecting_boundary, _absorbing_boundary
|
3
|
-
from ...probabilityfuncs.markov_chain import MCMC_state_selection
|
4
|
-
|
5
|
-
BOUNDARY_CONDITIONS = {
|
6
|
-
"reflecting": _refecting_boundary,
|
7
|
-
"absorbing": _absorbing_boundary,
|
8
|
-
}
|
9
|
-
|
10
|
-
|
11
|
-
class FBM_BP:
|
12
|
-
"""
|
13
|
-
Fractional Brownian Motion (FBM) simulation with a Markov process for
|
14
|
-
diffusion coefficients and Hurst exponents.
|
15
|
-
|
16
|
-
This class simulates the motion of particles using a fractional Brownian motion model
|
17
|
-
with adjustable parameters for diffusion and the Hurst exponent.
|
18
|
-
|
19
|
-
Parameters:
|
20
|
-
-----------
|
21
|
-
n : int
|
22
|
-
Number of time steps in the simulation.
|
23
|
-
dt : float
|
24
|
-
Time step duration in milliseconds.
|
25
|
-
diffusion_parameters : np.ndarray
|
26
|
-
Array of diffusion coefficients for the FBM simulation.
|
27
|
-
hurst_parameters : np.ndarray
|
28
|
-
Array of Hurst exponents for the FBM simulation.
|
29
|
-
diffusion_parameter_transition_matrix : np.ndarray
|
30
|
-
Transition matrix for diffusion coefficients.
|
31
|
-
hurst_parameter_transition_matrix : np.ndarray
|
32
|
-
Transition matrix for Hurst exponents.
|
33
|
-
state_probability_diffusion : np.ndarray
|
34
|
-
Initial probabilities of different diffusion states.
|
35
|
-
state_probability_hurst : np.ndarray
|
36
|
-
Initial probabilities of different Hurst states.
|
37
|
-
space_lim : np.ndarray
|
38
|
-
Space limits (min, max) for the FBM.
|
39
|
-
|
40
|
-
Methods:
|
41
|
-
--------
|
42
|
-
_autocovariance(k: int, hurst: float) -> float:
|
43
|
-
Computes the autocovariance function for fractional Gaussian noise (fGn).
|
44
|
-
|
45
|
-
_setup() -> None:
|
46
|
-
Sets up the simulation by precomputing the autocovariance matrix and initial states.
|
47
|
-
|
48
|
-
fbm() -> np.ndarray:
|
49
|
-
Runs the FBM simulation and returns the positions at each time step.
|
50
|
-
"""
|
51
|
-
|
52
|
-
def __init__(
|
53
|
-
self,
|
54
|
-
n: int,
|
55
|
-
dt: float,
|
56
|
-
diffusion_parameters: np.ndarray,
|
57
|
-
hurst_parameters: np.ndarray,
|
58
|
-
diffusion_parameter_transition_matrix: np.ndarray,
|
59
|
-
hurst_parameter_transition_matrix: np.ndarray,
|
60
|
-
state_probability_diffusion: np.ndarray,
|
61
|
-
state_probability_hurst: np.ndarray,
|
62
|
-
space_lim: np.ndarray,
|
63
|
-
):
|
64
|
-
self.n = int(n)
|
65
|
-
self.dt = dt # ms
|
66
|
-
self.diffusion_parameter = diffusion_parameters
|
67
|
-
self.hurst_parameter = hurst_parameters
|
68
|
-
# state probability of the diffusion parameter
|
69
|
-
self.diffusion_parameter_transition_matrix = (
|
70
|
-
diffusion_parameter_transition_matrix
|
71
|
-
)
|
72
|
-
# state probability of the hurst parameter
|
73
|
-
self.hurst_parameter_transition_matrix = hurst_parameter_transition_matrix
|
74
|
-
# probability of the initial state, this approximates the population distribution
|
75
|
-
self.state_probability_diffusion = state_probability_diffusion
|
76
|
-
# probability of the initial state, this approximates the population distribution
|
77
|
-
self.state_probability_hurst = state_probability_hurst
|
78
|
-
# space lim (min, max) for the FBM
|
79
|
-
self.space_lim = np.array(space_lim, dtype=float)
|
80
|
-
# initialize the autocovariance matrix and the diffusion parameter
|
81
|
-
self._setup()
|
82
|
-
|
83
|
-
def _autocovariance(self, k: int, hurst: float) -> float:
|
84
|
-
"""
|
85
|
-
Autocovariance function for fractional Gaussian noise (fGn).
|
86
|
-
|
87
|
-
Parameters:
|
88
|
-
-----------
|
89
|
-
k : int
|
90
|
-
Lag in time steps.
|
91
|
-
hurst : float
|
92
|
-
Hurst parameter, which controls the roughness of the trajectory.
|
93
|
-
|
94
|
-
Returns:
|
95
|
-
--------
|
96
|
-
float
|
97
|
-
The autocovariance value for the given lag.
|
98
|
-
"""
|
99
|
-
return 0.5 * (
|
100
|
-
abs(k - 1) ** (2 * hurst)
|
101
|
-
- 2 * abs(k) ** (2 * hurst)
|
102
|
-
+ abs(k + 1) ** (2 * hurst)
|
103
|
-
)
|
104
|
-
|
105
|
-
def _setup(self) -> None:
|
106
|
-
"""
|
107
|
-
Precomputes the autocovariance matrix and sets up initial diffusion and Hurst parameters.
|
108
|
-
|
109
|
-
This method initializes the state selection using Markov Chain Monte Carlo (MCMC)
|
110
|
-
and avoids recomputation of the autocovariance matrix during the simulation.
|
111
|
-
"""
|
112
|
-
self._cov = np.zeros(self.n)
|
113
|
-
self._diff_a_n = np.zeros(self.n)
|
114
|
-
self._hurst_n = np.zeros(self.n)
|
115
|
-
# catch if the diffusion or hurst parameter sets are singular
|
116
|
-
if len(self.diffusion_parameter) == 1:
|
117
|
-
self._diff_a_n = np.full(self.n, self.diffusion_parameter[0])
|
118
|
-
else:
|
119
|
-
diff_a_start = np.random.choice(
|
120
|
-
self.diffusion_parameter, p=self.state_probability_diffusion
|
121
|
-
)
|
122
|
-
self._diff_a_n[0] = diff_a_start
|
123
|
-
self._diff_a_n[1:] = MCMC_state_selection(
|
124
|
-
np.where(self.diffusion_parameter == diff_a_start)[0][0],
|
125
|
-
self.diffusion_parameter_transition_matrix,
|
126
|
-
self.diffusion_parameter,
|
127
|
-
self.n - 1,
|
128
|
-
)
|
129
|
-
|
130
|
-
if len(self.hurst_parameter) == 1:
|
131
|
-
self._hurst_n = np.full(self.n, self.hurst_parameter[0])
|
132
|
-
else:
|
133
|
-
hurst_start = np.random.choice(
|
134
|
-
self.hurst_parameter, p=self.state_probability_hurst
|
135
|
-
)
|
136
|
-
self._hurst_n[0] = hurst_start
|
137
|
-
self._hurst_n[1:] = MCMC_state_selection(
|
138
|
-
np.where(self.hurst_parameter == hurst_start)[0][0],
|
139
|
-
self.hurst_parameter_transition_matrix,
|
140
|
-
self.hurst_parameter,
|
141
|
-
self.n - 1,
|
142
|
-
)
|
143
|
-
for i in range(self.n):
|
144
|
-
self._cov[i] = self._autocovariance(i, self._hurst_n[i])
|
145
|
-
|
146
|
-
def fbm(self) -> np.ndarray:
|
147
|
-
"""
|
148
|
-
Simulates fractional Brownian motion (FBM) over `n` time steps.
|
149
|
-
|
150
|
-
If the Hurst exponent is 0.5 for all time steps, it performs a simple Gaussian
|
151
|
-
random walk. Otherwise, it uses the precomputed autocovariance matrix to generate
|
152
|
-
fractional Gaussian noise (fGn) and simulates FBM.
|
153
|
-
|
154
|
-
Returns:
|
155
|
-
--------
|
156
|
-
np.ndarray
|
157
|
-
An array representing the simulated FBM positions over time.
|
158
|
-
"""
|
159
|
-
fgn = np.zeros(self.n)
|
160
|
-
fbm_store = np.zeros(self.n)
|
161
|
-
phi = np.zeros(self.n)
|
162
|
-
psi = np.zeros(self.n)
|
163
|
-
# construct a gaussian noise vector
|
164
|
-
gn = (
|
165
|
-
np.random.normal(0, 1, self.n)
|
166
|
-
* np.sqrt(self.dt * 2 * self._diff_a_n)
|
167
|
-
* (self.dt ** (2 * self._hurst_n))
|
168
|
-
)
|
169
|
-
# catch is all hurst are 0.5 then use the gaussian noise vector corresponding to the scale defined by the diffusion parameter
|
170
|
-
if np.all(self._hurst_n == 0.5):
|
171
|
-
# each gn is then pulled from a normal distribution with mean 0 and standard deviation diff_a_n
|
172
|
-
# ignore the fbm calculations but keep the reflection
|
173
|
-
for i in range(1, self.n):
|
174
|
-
fbm_candidate = fbm_store[i - 1] + gn[i]
|
175
|
-
# check if this is outside the space limit by using the reflecting boundary condition
|
176
|
-
fbm_store[i] = _boundary_conditions(
|
177
|
-
fbm_store[i - 1], fbm_candidate, self.space_lim, "reflecting"
|
178
|
-
)
|
179
|
-
return fbm_store
|
180
|
-
|
181
|
-
fbm_store[0] = 0
|
182
|
-
fgn[0] = gn[0]
|
183
|
-
v = 1
|
184
|
-
phi[0] = 0
|
185
|
-
|
186
|
-
for i in range(1, self.n):
|
187
|
-
phi[i - 1] = self._cov[i]
|
188
|
-
for j in range(i - 1):
|
189
|
-
psi[j] = phi[j]
|
190
|
-
phi[i - 1] -= psi[j] * self._cov[i - j - 1]
|
191
|
-
phi[i - 1] /= v
|
192
|
-
for j in range(i - 1):
|
193
|
-
phi[j] = psi[j] - phi[i - 1] * psi[i - j - 2]
|
194
|
-
v *= 1 - phi[i - 1] * phi[i - 1]
|
195
|
-
for j in range(i):
|
196
|
-
fgn[i] += phi[j] * fgn[i - j - 1]
|
197
|
-
fgn[i] += np.sqrt(np.abs(v)) * gn[i]
|
198
|
-
# add to the fbm
|
199
|
-
fbm_candidate = fbm_store[i - 1] + fgn[i]
|
200
|
-
|
201
|
-
# check if this is outside the space limit by using the reflecting boundary condition
|
202
|
-
fbm_store[i] = _boundary_conditions(
|
203
|
-
fbm_store[i - 1], fbm_candidate, self.space_lim, "reflecting"
|
204
|
-
)
|
205
|
-
if fbm_store[i] != fbm_candidate:
|
206
|
-
# update the fgn based on the new difference
|
207
|
-
fgn[i] = fbm_store[i] - fbm_store[i - 1]
|
208
|
-
return fbm_store
|
209
|
-
|
210
|
-
|
211
|
-
def _boundary_conditions(
|
212
|
-
fbm_store_last: float,
|
213
|
-
fbm_candidate: float,
|
214
|
-
space_lim: np.ndarray,
|
215
|
-
condition_type: str,
|
216
|
-
) -> float:
|
217
|
-
"""
|
218
|
-
Apply boundary conditions to the FBM simulation.
|
219
|
-
|
220
|
-
Parameters:
|
221
|
-
-----------
|
222
|
-
fbm_store_last : float
|
223
|
-
The last value of the fractional Brownian motion (FBM) trajectory.
|
224
|
-
fbm_candidate : float
|
225
|
-
The candidate value for the next step in the FBM trajectory.
|
226
|
-
space_lim : np.ndarray
|
227
|
-
A 2-element array representing the minimum and maximum space limits.
|
228
|
-
condition_type : str
|
229
|
-
The type of boundary condition to apply, either "reflecting" or "absorbing".
|
230
|
-
|
231
|
-
Returns:
|
232
|
-
--------
|
233
|
-
float
|
234
|
-
The new value for the FBM trajectory, adjusted by the specified boundary condition.
|
235
|
-
"""
|
236
|
-
# check if the condition type is valid
|
237
|
-
if condition_type not in BOUNDARY_CONDITIONS:
|
238
|
-
raise ValueError(
|
239
|
-
"Invalid condition type: "
|
240
|
-
+ condition_type
|
241
|
-
+ "! Must be one of: "
|
242
|
-
+ str(BOUNDARY_CONDITIONS.keys())
|
243
|
-
)
|
244
|
-
return BOUNDARY_CONDITIONS[condition_type](fbm_store_last, fbm_candidate, space_lim)
|
File without changes
|
File without changes
|
File without changes
|