AMS-BP 0.0.2__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 +13 -0
- AMS_BP/cells/__init__.py +5 -0
- AMS_BP/cells/base_cell.py +55 -0
- AMS_BP/cells/rectangular_cell.py +82 -0
- AMS_BP/cells/rod_cell.py +98 -0
- AMS_BP/cells/spherical_cell.py +74 -0
- AMS_BP/configio/__init__.py +0 -0
- AMS_BP/configio/configmodels.py +93 -0
- AMS_BP/configio/convertconfig.py +910 -0
- AMS_BP/configio/experiments.py +121 -0
- AMS_BP/configio/saving.py +32 -0
- AMS_BP/metadata/__init__.py +0 -0
- AMS_BP/metadata/metadata.py +87 -0
- AMS_BP/motion/__init__.py +4 -0
- AMS_BP/motion/condensate_movement.py +356 -0
- AMS_BP/motion/movement/__init__.py +10 -0
- AMS_BP/motion/movement/boundary_conditions.py +75 -0
- AMS_BP/motion/movement/fbm_BP.py +244 -0
- AMS_BP/motion/track_gen.py +541 -0
- AMS_BP/optics/__init__.py +0 -0
- AMS_BP/optics/camera/__init__.py +4 -0
- AMS_BP/optics/camera/detectors.py +320 -0
- AMS_BP/optics/camera/quantum_eff.py +66 -0
- AMS_BP/optics/filters/__init__.py +17 -0
- AMS_BP/optics/filters/channels/__init__.py +0 -0
- AMS_BP/optics/filters/channels/channelschema.py +27 -0
- AMS_BP/optics/filters/filters.py +184 -0
- AMS_BP/optics/lasers/__init__.py +28 -0
- AMS_BP/optics/lasers/laser_profiles.py +691 -0
- AMS_BP/optics/psf/__init__.py +7 -0
- AMS_BP/optics/psf/psf_engine.py +215 -0
- AMS_BP/photophysics/__init__.py +0 -0
- AMS_BP/photophysics/photon_physics.py +181 -0
- AMS_BP/photophysics/state_kinetics.py +146 -0
- AMS_BP/probabilityfuncs/__init__.py +0 -0
- AMS_BP/probabilityfuncs/markov_chain.py +143 -0
- AMS_BP/probabilityfuncs/probability_functions.py +350 -0
- AMS_BP/run_cell_simulation.py +217 -0
- AMS_BP/sample/__init__.py +0 -0
- AMS_BP/sample/flurophores/__init__.py +16 -0
- AMS_BP/sample/flurophores/flurophore_schema.py +290 -0
- AMS_BP/sample/sim_sampleplane.py +334 -0
- AMS_BP/sim_config.toml +418 -0
- AMS_BP/sim_microscopy.py +453 -0
- AMS_BP/utils/__init__.py +0 -0
- AMS_BP/utils/constants.py +11 -0
- AMS_BP/utils/decorators.py +227 -0
- AMS_BP/utils/errors.py +37 -0
- AMS_BP/utils/maskMaker.py +12 -0
- AMS_BP/utils/util_functions.py +319 -0
- ams_bp-0.0.2.dist-info/METADATA +173 -0
- ams_bp-0.0.2.dist-info/RECORD +55 -0
- ams_bp-0.0.2.dist-info/WHEEL +4 -0
- ams_bp-0.0.2.dist-info/entry_points.txt +2 -0
- ams_bp-0.0.2.dist-info/licenses/LICENSE +21 -0
AMS_BP/__init__.py
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
"""
|
2
|
+
AMS_BP is a Python package for simulating advanced fluroscence microscopy experiments.
|
3
|
+
See the README.md file for more information.
|
4
|
+
GitHub: https://github.com/joemans3/AMS_BP
|
5
|
+
Pypi: https://pypi.org/project/AMS_BP/
|
6
|
+
- pip install AMS_BP
|
7
|
+
Author: Baljyot Singh Parmar
|
8
|
+
Last updated: 2024-12-16
|
9
|
+
|
10
|
+
|
11
|
+
"""
|
12
|
+
|
13
|
+
__version__ = "0.0.2"
|
AMS_BP/cells/__init__.py
ADDED
@@ -0,0 +1,55 @@
|
|
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
|
@@ -0,0 +1,82 @@
|
|
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
ADDED
@@ -0,0 +1,98 @@
|
|
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
|
@@ -0,0 +1,74 @@
|
|
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
|
File without changes
|
@@ -0,0 +1,93 @@
|
|
1
|
+
from typing import List, Literal
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
from pydantic import BaseModel, Field, field_validator
|
5
|
+
|
6
|
+
|
7
|
+
class CellParameters(BaseModel):
|
8
|
+
cell_space: List[List[float]] = Field(description="Cell space dimensions in um")
|
9
|
+
cell_axial_radius: float = Field(description="Axial radius in um")
|
10
|
+
|
11
|
+
@field_validator("cell_space")
|
12
|
+
def convert_cell_space(cls, v):
|
13
|
+
return np.array(v)
|
14
|
+
|
15
|
+
|
16
|
+
class MoleculeParameters(BaseModel):
|
17
|
+
num_molecules: List[int]
|
18
|
+
track_type: List[Literal["fbm", "constant"]] = Field(
|
19
|
+
description="Type of molecular motion, either fbm or constant"
|
20
|
+
)
|
21
|
+
diffusion_coefficient: List[List[float]] = Field(
|
22
|
+
description="Diffusion coefficients in um^2/s"
|
23
|
+
)
|
24
|
+
diffusion_track_amount: List[List[float]]
|
25
|
+
hurst_exponent: List[List[float]]
|
26
|
+
hurst_track_amount: List[List[float]]
|
27
|
+
allow_transition_probability: List[bool]
|
28
|
+
transition_matrix_time_step: List[int] = Field(description="Time step in ms")
|
29
|
+
diffusion_transition_matrix: List[List[List[float]]]
|
30
|
+
hurst_transition_matrix: List[List[List[float]]]
|
31
|
+
state_probability_diffusion: List[List[float]]
|
32
|
+
state_probability_hurst: List[List[float]]
|
33
|
+
|
34
|
+
@field_validator(
|
35
|
+
"diffusion_coefficient",
|
36
|
+
"diffusion_track_amount",
|
37
|
+
"hurst_exponent",
|
38
|
+
"hurst_track_amount",
|
39
|
+
"state_probability_diffusion",
|
40
|
+
"state_probability_hurst",
|
41
|
+
)
|
42
|
+
def convert_to_array(cls, v):
|
43
|
+
return np.array(v)
|
44
|
+
|
45
|
+
@field_validator("diffusion_transition_matrix", "hurst_transition_matrix")
|
46
|
+
def convert_matrix_to_array(cls, v):
|
47
|
+
return np.array(v)
|
48
|
+
|
49
|
+
|
50
|
+
class GlobalParameters(BaseModel):
|
51
|
+
sample_plane_dim: List[float] = Field(description="Sample plane dimensions in um")
|
52
|
+
cycle_count: int
|
53
|
+
exposure_time: int = Field(description="Exposure time in ms")
|
54
|
+
interval_time: int = Field(description="Interval time in ms")
|
55
|
+
oversample_motion_time: int = Field(description="Oversample motion time in ms")
|
56
|
+
|
57
|
+
@field_validator("sample_plane_dim")
|
58
|
+
def convert_sample_plane_dim(cls, v):
|
59
|
+
return np.array(v)
|
60
|
+
|
61
|
+
|
62
|
+
class CondensateParameters(BaseModel):
|
63
|
+
initial_centers: List[List[List[float]]] = Field(
|
64
|
+
description="Initial centers in um"
|
65
|
+
)
|
66
|
+
initial_scale: List[List[float]] = Field(description="Initial scale in um")
|
67
|
+
diffusion_coefficient: List[List[float]] = Field(
|
68
|
+
description="Diffusion coefficients in um^2/s"
|
69
|
+
)
|
70
|
+
hurst_exponent: List[List[float]]
|
71
|
+
density_dif: List[int]
|
72
|
+
|
73
|
+
# @field_validator(
|
74
|
+
# "initial_centers", "initial_scale", "diffusion_coefficient", "hurst_exponent"
|
75
|
+
# )
|
76
|
+
# def convert_to_array(cls, v):
|
77
|
+
# return np.array(v)
|
78
|
+
#
|
79
|
+
|
80
|
+
|
81
|
+
class OutputParameters(BaseModel):
|
82
|
+
output_path: str
|
83
|
+
output_name: str
|
84
|
+
subsegment_type: str
|
85
|
+
subsegment_number: int
|
86
|
+
|
87
|
+
|
88
|
+
class ConfigList(BaseModel):
|
89
|
+
CellParameters: CellParameters
|
90
|
+
MoleculeParameters: MoleculeParameters
|
91
|
+
GlobalParameters: GlobalParameters
|
92
|
+
CondensateParameters: CondensateParameters
|
93
|
+
OutputParameters: OutputParameters
|