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/__init__.py
CHANGED
AMS_BP/cells/__init__.py
CHANGED
@@ -1,5 +1,31 @@
|
|
1
|
-
from .
|
2
|
-
from .
|
3
|
-
from .
|
1
|
+
from boundedfbm.cells.base_cell import BaseCell
|
2
|
+
from boundedfbm.cells.ovoid_cell import OvoidCell, make_OvoidCell
|
3
|
+
from boundedfbm.cells.rectangular_cell import RectangularCell, make_RectangularCell
|
4
|
+
from boundedfbm.cells.rod_cell import RodCell, make_RodCell
|
5
|
+
from boundedfbm.cells.spherical_cell import SphericalCell, make_SphericalCell
|
6
|
+
from boundedfbm.cells.typedefs import Vector3D
|
4
7
|
|
5
|
-
|
8
|
+
from .budding_yeast_cell import BuddingCell, make_BuddingCell
|
9
|
+
from .cell_factory import (
|
10
|
+
CellType,
|
11
|
+
create_cell,
|
12
|
+
validate_cell_parameters,
|
13
|
+
)
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
"SphericalCell",
|
17
|
+
"RectangularCell",
|
18
|
+
"RodCell",
|
19
|
+
"OvoidCell",
|
20
|
+
"BuddingCell",
|
21
|
+
"make_SphericalCell",
|
22
|
+
"make_RodCell",
|
23
|
+
"make_RectangularCell",
|
24
|
+
"make_BuddingCell",
|
25
|
+
"make_OvoidCell",
|
26
|
+
"create_cell",
|
27
|
+
"CellType",
|
28
|
+
"validate_cell_parameters",
|
29
|
+
"BaseCell",
|
30
|
+
"Vector3D",
|
31
|
+
]
|
@@ -0,0 +1,274 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import List, Tuple
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
import pyvista as pv
|
6
|
+
from boundedfbm.cells.base_cell import BaseCell
|
7
|
+
from boundedfbm.cells.typedefs import Vector3D
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class BuddingCell(BaseCell):
|
12
|
+
"""
|
13
|
+
Represents a budding yeast cell composed of two connected ovoids (mother and bud).
|
14
|
+
The cells are connected at a "neck" region, with the bud growing from the mother cell.
|
15
|
+
|
16
|
+
Attributes:
|
17
|
+
center (np.ndarray): The (x, y, z) coordinates of the mother cell's center in XYZ plane
|
18
|
+
mother_radius_x (float): Mother cell radius along X axis
|
19
|
+
mother_radius_y (float): Mother cell radius along Y axis
|
20
|
+
mother_radius_z (float): Mother cell radius along Z axis
|
21
|
+
bud_radius_x (float): Bud radius along X axis
|
22
|
+
bud_radius_y (float): Bud radius along Y axis
|
23
|
+
bud_radius_z (float): Bud radius along Z axis
|
24
|
+
bud_angle (float): Angle in radians from x-axis where bud emerges
|
25
|
+
bud_distance (float): Distance between mother and bud centers
|
26
|
+
neck_radius (float): Radius of the connecting neck region
|
27
|
+
"""
|
28
|
+
|
29
|
+
center: np.ndarray | List[float] | Tuple
|
30
|
+
mother_radius_x: float
|
31
|
+
mother_radius_y: float
|
32
|
+
mother_radius_z: float
|
33
|
+
bud_radius_x: float
|
34
|
+
bud_radius_y: float
|
35
|
+
bud_radius_z: float
|
36
|
+
bud_angle: float
|
37
|
+
bud_distance: float
|
38
|
+
neck_radius: float
|
39
|
+
|
40
|
+
def is_point_inside(
|
41
|
+
self, x: float, y: float, z: float, tolerance: float = 1e-3
|
42
|
+
) -> bool:
|
43
|
+
"""
|
44
|
+
Determines if a given point is inside the BuddingCell.
|
45
|
+
A point is inside if it's within the mother cell, the bud, or the neck region.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
point (np.ndarray | List[float] | Tuple): The (x, y, z) coordinates of the point to check
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
bool: True if the point is inside the cell, False otherwise
|
52
|
+
"""
|
53
|
+
# Ensure point is a numpy array for vector operations
|
54
|
+
point = np.array([x, y, z])
|
55
|
+
mother_center = np.array(self.center)
|
56
|
+
|
57
|
+
# Calculate bud center based on angle and distance
|
58
|
+
bud_x = mother_center[0] + self.bud_distance * np.cos(self.bud_angle)
|
59
|
+
bud_y = mother_center[1] + self.bud_distance * np.sin(self.bud_angle)
|
60
|
+
bud_center = np.array([bud_x, bud_y, mother_center[2]])
|
61
|
+
|
62
|
+
# Check if point is inside the mother cell (scaled ellipsoid)
|
63
|
+
x, y, z = point - mother_center
|
64
|
+
mother_distance_squared = (
|
65
|
+
(x / self.mother_radius_x) ** 2
|
66
|
+
+ (y / self.mother_radius_y) ** 2
|
67
|
+
+ (z / self.mother_radius_z) ** 2
|
68
|
+
)
|
69
|
+
if mother_distance_squared <= 1:
|
70
|
+
return True
|
71
|
+
|
72
|
+
# Check if point is inside the bud (scaled ellipsoid)
|
73
|
+
x, y, z = point - bud_center
|
74
|
+
bud_distance_squared = (
|
75
|
+
(x / self.bud_radius_x) ** 2
|
76
|
+
+ (y / self.bud_radius_y) ** 2
|
77
|
+
+ (z / self.bud_radius_z) ** 2
|
78
|
+
)
|
79
|
+
if bud_distance_squared <= 1:
|
80
|
+
return True
|
81
|
+
|
82
|
+
# Check if point is inside the neck region
|
83
|
+
# First, project the point onto the line between mother and bud centers
|
84
|
+
mother_to_bud_vec = bud_center - mother_center
|
85
|
+
mother_to_bud_length = np.linalg.norm(mother_to_bud_vec)
|
86
|
+
mother_to_bud_unit = mother_to_bud_vec / mother_to_bud_length
|
87
|
+
|
88
|
+
mother_to_point_vec = point - mother_center
|
89
|
+
projection_length = np.dot(mother_to_point_vec, mother_to_bud_unit)
|
90
|
+
|
91
|
+
# Calculate minimum distance from point to the center line
|
92
|
+
if 0 <= projection_length <= mother_to_bud_length:
|
93
|
+
projection_point = mother_center + projection_length * mother_to_bud_unit
|
94
|
+
distance_to_line = np.linalg.norm(point - projection_point)
|
95
|
+
|
96
|
+
# The neck is modeled as a truncated cone
|
97
|
+
# Interpolate the radius at this point along the neck
|
98
|
+
if projection_length <= self.mother_radius_x:
|
99
|
+
# Within mother cell radius from the center
|
100
|
+
local_radius = self.neck_radius
|
101
|
+
elif projection_length >= mother_to_bud_length - self.bud_radius_x:
|
102
|
+
# Within bud cell radius from the bud center
|
103
|
+
local_radius = self.neck_radius
|
104
|
+
else:
|
105
|
+
local_radius = self.neck_radius
|
106
|
+
|
107
|
+
return distance_to_line <= local_radius
|
108
|
+
|
109
|
+
return False
|
110
|
+
|
111
|
+
|
112
|
+
def make_BuddingCell(
|
113
|
+
center: np.ndarray | List[float] | Tuple,
|
114
|
+
mother_radius_x: float,
|
115
|
+
mother_radius_y: float,
|
116
|
+
mother_radius_z: float,
|
117
|
+
bud_radius_x: float,
|
118
|
+
bud_radius_y: float,
|
119
|
+
bud_radius_z: float,
|
120
|
+
bud_angle: float,
|
121
|
+
bud_distance: float,
|
122
|
+
neck_radius: float,
|
123
|
+
) -> BuddingCell:
|
124
|
+
"""
|
125
|
+
Create a budding yeast cell using PyVista meshes.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
center: Center point of the mother cell
|
129
|
+
mother_radius_x/y/z: Radii of the mother cell along each axis
|
130
|
+
bud_radius_x/y/z: Radii of the bud cell along each axis
|
131
|
+
bud_angle: Angle in radians from x-axis where bud emerges
|
132
|
+
bud_distance: Distance between mother and bud centers
|
133
|
+
neck_radius: Radius of the connecting neck region
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
BuddingCell: Instance with PyVista mesh
|
137
|
+
"""
|
138
|
+
# Validate inputs
|
139
|
+
center = np.array(center)
|
140
|
+
if center.shape != (3,):
|
141
|
+
raise ValueError("Center must be a 3D point")
|
142
|
+
|
143
|
+
# Calculate bud center
|
144
|
+
bud_center = np.array(
|
145
|
+
[
|
146
|
+
center[0] + bud_distance * np.cos(bud_angle),
|
147
|
+
center[1] + bud_distance * np.sin(bud_angle),
|
148
|
+
center[2],
|
149
|
+
]
|
150
|
+
)
|
151
|
+
|
152
|
+
# Create mother cell ellipsoid
|
153
|
+
mother = pv.ParametricEllipsoid(
|
154
|
+
xradius=mother_radius_x,
|
155
|
+
yradius=mother_radius_y,
|
156
|
+
zradius=mother_radius_z,
|
157
|
+
center=center,
|
158
|
+
)
|
159
|
+
|
160
|
+
# Create bud cell ellipsoid
|
161
|
+
bud = pv.ParametricEllipsoid(
|
162
|
+
xradius=bud_radius_x,
|
163
|
+
yradius=bud_radius_y,
|
164
|
+
zradius=bud_radius_z,
|
165
|
+
center=bud_center,
|
166
|
+
)
|
167
|
+
|
168
|
+
# Create neck region (cylinder)
|
169
|
+
# Calculate direction vector from mother to bud
|
170
|
+
direction = bud_center - center
|
171
|
+
direction = direction / np.linalg.norm(direction)
|
172
|
+
|
173
|
+
# Create cylinder for neck
|
174
|
+
cylinder = pv.Cylinder(
|
175
|
+
center=(center + bud_center) / 2, # Midpoint
|
176
|
+
direction=direction,
|
177
|
+
radius=neck_radius,
|
178
|
+
height=bud_distance,
|
179
|
+
)
|
180
|
+
|
181
|
+
# Combine shapes using boolean operations
|
182
|
+
# First combine mother and neck
|
183
|
+
mother_and_neck = mother.boolean_union(cylinder)
|
184
|
+
|
185
|
+
# Then add the bud
|
186
|
+
complete_cell = mother_and_neck.boolean_union(bud)
|
187
|
+
|
188
|
+
# Clean up the mesh
|
189
|
+
complete_cell = complete_cell.clean()
|
190
|
+
complete_cell = complete_cell.fill_holes(1)
|
191
|
+
|
192
|
+
# Verify mesh integrity
|
193
|
+
edges = complete_cell.extract_feature_edges(
|
194
|
+
feature_edges=False, manifold_edges=False
|
195
|
+
)
|
196
|
+
assert edges.n_cells == 0, "Mesh has non-manifold edges"
|
197
|
+
|
198
|
+
return BuddingCell(
|
199
|
+
mesh=complete_cell,
|
200
|
+
center=center,
|
201
|
+
mother_radius_x=mother_radius_x,
|
202
|
+
mother_radius_y=mother_radius_y,
|
203
|
+
mother_radius_z=mother_radius_z,
|
204
|
+
bud_radius_x=bud_radius_x,
|
205
|
+
bud_radius_y=bud_radius_y,
|
206
|
+
bud_radius_z=bud_radius_z,
|
207
|
+
bud_angle=bud_angle,
|
208
|
+
bud_distance=bud_distance,
|
209
|
+
neck_radius=neck_radius,
|
210
|
+
)
|
211
|
+
|
212
|
+
|
213
|
+
@dataclass
|
214
|
+
class BuddingCellParams:
|
215
|
+
center: Vector3D
|
216
|
+
mother_radius_x: float
|
217
|
+
mother_radius_y: float
|
218
|
+
mother_radius_z: float
|
219
|
+
bud_radius_x: float
|
220
|
+
bud_radius_y: float
|
221
|
+
bud_radius_z: float
|
222
|
+
bud_angle: float
|
223
|
+
bud_distance: float
|
224
|
+
neck_radius: float
|
225
|
+
|
226
|
+
@classmethod
|
227
|
+
def validate_center(cls, value):
|
228
|
+
if not isinstance(value, (list, tuple, np.ndarray)) or len(value) != 3:
|
229
|
+
raise ValueError("center must be a 3D vector")
|
230
|
+
|
231
|
+
@classmethod
|
232
|
+
def validate_mother_radius_x(cls, value):
|
233
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
234
|
+
raise ValueError("mother_radius_x must be a positive number")
|
235
|
+
|
236
|
+
@classmethod
|
237
|
+
def validate_mother_radius_y(cls, value):
|
238
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
239
|
+
raise ValueError("mother_radius_y must be a positive number")
|
240
|
+
|
241
|
+
@classmethod
|
242
|
+
def validate_mother_radius_z(cls, value):
|
243
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
244
|
+
raise ValueError("mother_radius_z must be a positive number")
|
245
|
+
|
246
|
+
@classmethod
|
247
|
+
def validate_bud_radius_x(cls, value):
|
248
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
249
|
+
raise ValueError("bud_radius_x must be a positive number")
|
250
|
+
|
251
|
+
@classmethod
|
252
|
+
def validate_bud_radius_y(cls, value):
|
253
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
254
|
+
raise ValueError("bud_radius_y must be a positive number")
|
255
|
+
|
256
|
+
@classmethod
|
257
|
+
def validate_bud_radius_z(cls, value):
|
258
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
259
|
+
raise ValueError("bud_radius_z must be a positive number")
|
260
|
+
|
261
|
+
@classmethod
|
262
|
+
def validate_bud_angle(cls, value):
|
263
|
+
if not isinstance(value, (int, float)):
|
264
|
+
raise ValueError("bud_angle must be a number")
|
265
|
+
|
266
|
+
@classmethod
|
267
|
+
def validate_bud_distance(cls, value):
|
268
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
269
|
+
raise ValueError("bud_distance must be a positive number")
|
270
|
+
|
271
|
+
@classmethod
|
272
|
+
def validate_neck_radius(cls, value):
|
273
|
+
if not isinstance(value, (int, float)) or value <= 0:
|
274
|
+
raise ValueError("neck_radius must be a positive number")
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import inspect
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Any, Callable, Dict, List, Tuple, Union
|
4
|
+
|
5
|
+
from boundedfbm.cells.base_cell import BaseCell
|
6
|
+
|
7
|
+
# Import cell creation functions and parameter classes
|
8
|
+
from boundedfbm.cells.ovoid_cell import OvoidCellParams, make_OvoidCell
|
9
|
+
from boundedfbm.cells.rectangular_cell import (
|
10
|
+
RectangularCellParams,
|
11
|
+
make_RectangularCell,
|
12
|
+
)
|
13
|
+
from boundedfbm.cells.rod_cell import RodCellParams, make_RodCell
|
14
|
+
from boundedfbm.cells.spherical_cell import SphericalCellParams, make_SphericalCell
|
15
|
+
|
16
|
+
from .budding_yeast_cell import BuddingCellParams, make_BuddingCell
|
17
|
+
|
18
|
+
|
19
|
+
# ===== Exposed Enum =====
|
20
|
+
class CellType(str, Enum):
|
21
|
+
SPHERICAL = "SphericalCell"
|
22
|
+
ROD = "RodCell"
|
23
|
+
RECTANGULAR = "RectangularCell"
|
24
|
+
OVOID = "OvoidCell"
|
25
|
+
BUDDINGYEAST = "BuddingCell"
|
26
|
+
|
27
|
+
|
28
|
+
# ===== Internal Mappings =====
|
29
|
+
_CELL_PARAM_CLASSES: Dict[CellType, Any] = {
|
30
|
+
CellType.SPHERICAL: SphericalCellParams,
|
31
|
+
CellType.ROD: RodCellParams,
|
32
|
+
CellType.RECTANGULAR: RectangularCellParams,
|
33
|
+
CellType.OVOID: OvoidCellParams,
|
34
|
+
CellType.BUDDINGYEAST: BuddingCellParams,
|
35
|
+
}
|
36
|
+
|
37
|
+
_CELL_CREATION_MAP: Dict[CellType, Tuple[Callable, List[str]]] = {
|
38
|
+
CellType.SPHERICAL: (make_SphericalCell, ["center", "radius"]),
|
39
|
+
CellType.ROD: (make_RodCell, ["center", "direction", "height", "radius"]),
|
40
|
+
CellType.RECTANGULAR: (make_RectangularCell, ["bounds"]),
|
41
|
+
CellType.OVOID: (
|
42
|
+
make_OvoidCell,
|
43
|
+
["center", "direction", "xradius", "yradius", "zradius"],
|
44
|
+
),
|
45
|
+
CellType.BUDDINGYEAST: (
|
46
|
+
make_BuddingCell,
|
47
|
+
[
|
48
|
+
"center",
|
49
|
+
"mother_radius_x",
|
50
|
+
"mother_radius_y",
|
51
|
+
"mother_radius_z",
|
52
|
+
"bud_radius_x",
|
53
|
+
"bud_radius_y",
|
54
|
+
"bud_radius_z",
|
55
|
+
"bud_angle",
|
56
|
+
"bud_distance",
|
57
|
+
"neck_radius",
|
58
|
+
],
|
59
|
+
),
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
# ===== Internal Validator =====
|
64
|
+
def _validate_parameters(
|
65
|
+
cell_type: CellType, params: Dict[str, Any]
|
66
|
+
) -> Tuple[bool, List[str]]:
|
67
|
+
"""Internal function to validate cell parameters against the expected schema."""
|
68
|
+
param_class = _CELL_PARAM_CLASSES[cell_type]
|
69
|
+
|
70
|
+
# Validate against expected parameters
|
71
|
+
expected_params = {
|
72
|
+
name for name in inspect.signature(param_class).parameters if name != "self"
|
73
|
+
}
|
74
|
+
unexpected = set(params.keys()) - expected_params
|
75
|
+
missing = expected_params - set(params.keys())
|
76
|
+
|
77
|
+
errors = []
|
78
|
+
if unexpected:
|
79
|
+
errors.append(f"Unexpected parameter(s): {', '.join(unexpected)}")
|
80
|
+
if missing:
|
81
|
+
errors.append(f"Missing parameter(s): {', '.join(missing)}")
|
82
|
+
|
83
|
+
if errors:
|
84
|
+
return False, errors
|
85
|
+
|
86
|
+
try:
|
87
|
+
param_class(**params)
|
88
|
+
return True, []
|
89
|
+
except ValueError as e:
|
90
|
+
return False, str(e).split("\n")
|
91
|
+
|
92
|
+
|
93
|
+
# ===== Exposed Validation Function =====
|
94
|
+
def validate_cell_parameters(
|
95
|
+
cell_type: Union[str, CellType], params: Dict[str, Any]
|
96
|
+
) -> Tuple[bool, List[str]]:
|
97
|
+
"""
|
98
|
+
Validate parameters for a given cell type.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
cell_type: The cell type (string or enum)
|
102
|
+
params: Dictionary of parameters
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
Tuple of (is_valid, error_messages)
|
106
|
+
"""
|
107
|
+
if isinstance(cell_type, str):
|
108
|
+
try:
|
109
|
+
cell_type = CellType(cell_type)
|
110
|
+
except ValueError:
|
111
|
+
return False, [f"Unknown cell type: {cell_type}"]
|
112
|
+
|
113
|
+
if cell_type not in _CELL_PARAM_CLASSES:
|
114
|
+
return False, [f"Unknown cell type: {cell_type}"]
|
115
|
+
|
116
|
+
return _validate_parameters(cell_type, params)
|
117
|
+
|
118
|
+
|
119
|
+
# ===== Exposed Factory Function =====
|
120
|
+
def create_cell(cell_type: Union[str, CellType], params: Dict[str, Any]) -> BaseCell:
|
121
|
+
"""
|
122
|
+
Create a validated cell instance.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
cell_type: Cell type (string or enum)
|
126
|
+
params: Dictionary of cell parameters.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
BaseCell instance.
|
130
|
+
|
131
|
+
Raises:
|
132
|
+
ValueError: If validation fails.
|
133
|
+
"""
|
134
|
+
if isinstance(cell_type, str):
|
135
|
+
try:
|
136
|
+
cell_type = CellType(cell_type)
|
137
|
+
except ValueError:
|
138
|
+
raise ValueError(f"Unknown cell type: {cell_type}")
|
139
|
+
|
140
|
+
if cell_type not in _CELL_CREATION_MAP:
|
141
|
+
raise ValueError(f"Unknown cell type: {cell_type}")
|
142
|
+
|
143
|
+
is_valid, errors = validate_cell_parameters(cell_type, params)
|
144
|
+
if not is_valid:
|
145
|
+
raise ValueError(f"Invalid {cell_type} configuration: {'; '.join(errors)}")
|
146
|
+
|
147
|
+
func, param_keys = _CELL_CREATION_MAP[cell_type]
|
148
|
+
return func(**{key: params[key] for key in param_keys})
|
AMS_BP/configio/configmodels.py
CHANGED
@@ -1,16 +1,19 @@
|
|
1
|
-
from typing import List, Literal
|
1
|
+
from typing import Any, Dict, List, Literal, Union
|
2
2
|
|
3
3
|
import numpy as np
|
4
4
|
from pydantic import BaseModel, Field, field_validator
|
5
5
|
|
6
|
+
from ..cells import CellType, validate_cell_parameters
|
7
|
+
|
6
8
|
|
7
9
|
class CellParameters(BaseModel):
|
8
|
-
|
9
|
-
|
10
|
+
cell_type: Union[str, CellType]
|
11
|
+
params: Dict[str, Any]
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
def model_post_init(self, __context):
|
14
|
+
is_valid = validate_cell_parameters(self.cell_type, self.params)
|
15
|
+
if not is_valid:
|
16
|
+
raise ValueError(f"Cell model creation unsuccessful: {is_valid[1]}")
|
14
17
|
|
15
18
|
|
16
19
|
class MoleculeParameters(BaseModel):
|
AMS_BP/configio/convertconfig.py
CHANGED
@@ -5,8 +5,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
5
5
|
import tomli
|
6
6
|
from pydantic import BaseModel
|
7
7
|
|
8
|
-
from ..cells import
|
9
|
-
from ..cells.base_cell import BaseCell
|
8
|
+
from ..cells import BaseCell, create_cell
|
10
9
|
from ..motion import Track_generator, create_condensate_dict
|
11
10
|
from ..motion.track_gen import (
|
12
11
|
_convert_tracks_to_trajectory,
|
@@ -672,7 +671,7 @@ class ConfigLoader:
|
|
672
671
|
# make initial sample plane
|
673
672
|
sample_plane = make_sample(
|
674
673
|
global_params=base_config.GlobalParameters,
|
675
|
-
|
674
|
+
cell=cell,
|
676
675
|
)
|
677
676
|
|
678
677
|
# make condensates_dict
|
@@ -739,23 +738,19 @@ class ConfigLoader:
|
|
739
738
|
|
740
739
|
def make_cell(cell_params) -> BaseCell:
|
741
740
|
# make cell
|
742
|
-
|
743
|
-
|
744
|
-
cell_params.cell_space[0][1] - cell_params.cell_space[0][0],
|
745
|
-
cell_params.cell_space[1][1] - cell_params.cell_space[1][0],
|
746
|
-
cell_params.cell_axial_radius * 2,
|
747
|
-
)
|
748
|
-
cell = RectangularCell(origin=cell_origin, dimensions=cell_dimensions)
|
741
|
+
|
742
|
+
cell = create_cell(cell_params.cell_type, cell_params.params)
|
749
743
|
|
750
744
|
return cell
|
751
745
|
|
752
746
|
|
753
|
-
def make_sample(global_params,
|
747
|
+
def make_sample(global_params: GlobalParameters, cell: BaseCell) -> SamplePlane:
|
748
|
+
bounds = cell.boundingbox
|
754
749
|
sample_space = SampleSpace(
|
755
750
|
x_max=global_params.sample_plane_dim[0],
|
756
751
|
y_max=global_params.sample_plane_dim[1],
|
757
|
-
z_max=
|
758
|
-
z_min
|
752
|
+
z_max=bounds[-1],
|
753
|
+
z_min=bounds[-2],
|
759
754
|
)
|
760
755
|
|
761
756
|
# total time
|
@@ -769,15 +764,17 @@ def make_sample(global_params, cell_params) -> SamplePlane:
|
|
769
764
|
fov=(
|
770
765
|
(0, global_params.sample_plane_dim[0]),
|
771
766
|
(0, global_params.sample_plane_dim[1]),
|
772
|
-
(-
|
773
|
-
),
|
767
|
+
(bounds[-2], bounds[-1]),
|
768
|
+
), # simulates the whole simulation space to avoid the issue of PSF bleeding into FOV if the molecule's location is technically outside of the FOV dictated by the camera detector size and objective magnification.
|
774
769
|
oversample_motion_time=global_params.oversample_motion_time,
|
775
770
|
t_end=totaltime,
|
776
771
|
)
|
777
772
|
return sample_plane
|
778
773
|
|
779
774
|
|
780
|
-
def make_condensatedict(
|
775
|
+
def make_condensatedict(
|
776
|
+
condensate_params: CondensateParameters, cell: BaseCell
|
777
|
+
) -> List[dict]:
|
781
778
|
condensates_dict = []
|
782
779
|
for i in range(len(condensate_params.initial_centers)):
|
783
780
|
condensates_dict.append(
|
@@ -807,26 +804,29 @@ def make_samplingfunction(condensate_params, cell) -> List[Callable]:
|
|
807
804
|
return sampling_functions
|
808
805
|
|
809
806
|
|
810
|
-
def gen_initial_positions(
|
807
|
+
def gen_initial_positions(
|
808
|
+
molecule_params: MoleculeParameters,
|
809
|
+
cell: BaseCell,
|
810
|
+
condensate_params: CondensateParameters,
|
811
|
+
sampling_functions: List[Callable],
|
812
|
+
) -> List:
|
811
813
|
initials = []
|
812
814
|
for i in range(len(molecule_params.num_molecules)):
|
813
815
|
num_molecules = molecule_params.num_molecules[i]
|
814
816
|
initial_positions = gen_points(
|
815
817
|
pdf=sampling_functions[i],
|
816
818
|
total_points=num_molecules,
|
817
|
-
|
818
|
-
|
819
|
-
min_y=cell.origin[1],
|
820
|
-
max_y=cell.origin[1] + cell.dimensions[1],
|
821
|
-
min_z=-cell.dimensions[2] / 2,
|
822
|
-
max_z=cell.dimensions[2] / 2,
|
819
|
+
volume=cell.volume,
|
820
|
+
bounds=cell.boundingbox,
|
823
821
|
density_dif=condensate_params.density_dif[i],
|
824
822
|
)
|
825
823
|
initials.append(initial_positions)
|
826
824
|
return initials
|
827
825
|
|
828
826
|
|
829
|
-
def create_track_generator(
|
827
|
+
def create_track_generator(
|
828
|
+
global_params: GlobalParameters, cell: BaseCell
|
829
|
+
) -> Track_generator:
|
830
830
|
totaltime = int(
|
831
831
|
global_params.cycle_count
|
832
832
|
* (global_params.exposure_time + global_params.interval_time)
|
@@ -834,15 +834,18 @@ def create_track_generator(global_params, cell):
|
|
834
834
|
# make track generator
|
835
835
|
track_generator = Track_generator(
|
836
836
|
cell=cell,
|
837
|
-
|
838
|
-
exposure_time=global_params.exposure_time,
|
839
|
-
interval_time=global_params.interval_time,
|
837
|
+
total_time=totaltime,
|
840
838
|
oversample_motion_time=global_params.oversample_motion_time,
|
841
839
|
)
|
842
840
|
return track_generator
|
843
841
|
|
844
842
|
|
845
|
-
def get_tracks(
|
843
|
+
def get_tracks(
|
844
|
+
molecule_params: MoleculeParameters,
|
845
|
+
global_params: GlobalParameters,
|
846
|
+
initial_positions: List,
|
847
|
+
track_generator: Track_generator,
|
848
|
+
) -> Tuple[List, List]:
|
846
849
|
totaltime = int(
|
847
850
|
global_params.cycle_count
|
848
851
|
* (global_params.exposure_time + global_params.interval_time)
|
@@ -897,7 +900,12 @@ def get_tracks(molecule_params, global_params, initial_positions, track_generato
|
|
897
900
|
return tracks_collection, points_per_time_collection
|
898
901
|
|
899
902
|
|
900
|
-
def add_tracks_to_sample(
|
903
|
+
def add_tracks_to_sample(
|
904
|
+
tracks: List,
|
905
|
+
sample_plane: SamplePlane,
|
906
|
+
fluorophore: List[Fluorophore],
|
907
|
+
ID_counter=0,
|
908
|
+
) -> SamplePlane:
|
901
909
|
counter = ID_counter
|
902
910
|
for track_type in range(len(tracks)):
|
903
911
|
for j in tracks[track_type].values():
|