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.
Files changed (55) hide show
  1. AMS_BP/__init__.py +13 -0
  2. AMS_BP/cells/__init__.py +5 -0
  3. AMS_BP/cells/base_cell.py +55 -0
  4. AMS_BP/cells/rectangular_cell.py +82 -0
  5. AMS_BP/cells/rod_cell.py +98 -0
  6. AMS_BP/cells/spherical_cell.py +74 -0
  7. AMS_BP/configio/__init__.py +0 -0
  8. AMS_BP/configio/configmodels.py +93 -0
  9. AMS_BP/configio/convertconfig.py +910 -0
  10. AMS_BP/configio/experiments.py +121 -0
  11. AMS_BP/configio/saving.py +32 -0
  12. AMS_BP/metadata/__init__.py +0 -0
  13. AMS_BP/metadata/metadata.py +87 -0
  14. AMS_BP/motion/__init__.py +4 -0
  15. AMS_BP/motion/condensate_movement.py +356 -0
  16. AMS_BP/motion/movement/__init__.py +10 -0
  17. AMS_BP/motion/movement/boundary_conditions.py +75 -0
  18. AMS_BP/motion/movement/fbm_BP.py +244 -0
  19. AMS_BP/motion/track_gen.py +541 -0
  20. AMS_BP/optics/__init__.py +0 -0
  21. AMS_BP/optics/camera/__init__.py +4 -0
  22. AMS_BP/optics/camera/detectors.py +320 -0
  23. AMS_BP/optics/camera/quantum_eff.py +66 -0
  24. AMS_BP/optics/filters/__init__.py +17 -0
  25. AMS_BP/optics/filters/channels/__init__.py +0 -0
  26. AMS_BP/optics/filters/channels/channelschema.py +27 -0
  27. AMS_BP/optics/filters/filters.py +184 -0
  28. AMS_BP/optics/lasers/__init__.py +28 -0
  29. AMS_BP/optics/lasers/laser_profiles.py +691 -0
  30. AMS_BP/optics/psf/__init__.py +7 -0
  31. AMS_BP/optics/psf/psf_engine.py +215 -0
  32. AMS_BP/photophysics/__init__.py +0 -0
  33. AMS_BP/photophysics/photon_physics.py +181 -0
  34. AMS_BP/photophysics/state_kinetics.py +146 -0
  35. AMS_BP/probabilityfuncs/__init__.py +0 -0
  36. AMS_BP/probabilityfuncs/markov_chain.py +143 -0
  37. AMS_BP/probabilityfuncs/probability_functions.py +350 -0
  38. AMS_BP/run_cell_simulation.py +217 -0
  39. AMS_BP/sample/__init__.py +0 -0
  40. AMS_BP/sample/flurophores/__init__.py +16 -0
  41. AMS_BP/sample/flurophores/flurophore_schema.py +290 -0
  42. AMS_BP/sample/sim_sampleplane.py +334 -0
  43. AMS_BP/sim_config.toml +418 -0
  44. AMS_BP/sim_microscopy.py +453 -0
  45. AMS_BP/utils/__init__.py +0 -0
  46. AMS_BP/utils/constants.py +11 -0
  47. AMS_BP/utils/decorators.py +227 -0
  48. AMS_BP/utils/errors.py +37 -0
  49. AMS_BP/utils/maskMaker.py +12 -0
  50. AMS_BP/utils/util_functions.py +319 -0
  51. ams_bp-0.0.2.dist-info/METADATA +173 -0
  52. ams_bp-0.0.2.dist-info/RECORD +55 -0
  53. ams_bp-0.0.2.dist-info/WHEEL +4 -0
  54. ams_bp-0.0.2.dist-info/entry_points.txt +2 -0
  55. 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"
@@ -0,0 +1,5 @@
1
+ from .spherical_cell import SphericalCell
2
+ from .rectangular_cell import RectangularCell
3
+ from .rod_cell import RodCell
4
+
5
+ __all__ = ["SphericalCell", "RectangularCell", "RodCell"]
@@ -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)
@@ -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