AMS-BP 0.0.26__py3-none-any.whl → 0.0.32__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 CHANGED
@@ -10,4 +10,4 @@ Last updated: 2024-12-16
10
10
 
11
11
  """
12
12
 
13
- __version__ = "0.0.26"
13
+ __version__ = "0.0.32"
@@ -770,7 +770,7 @@ def make_sample(global_params, cell_params) -> SamplePlane:
770
770
  (0, global_params.sample_plane_dim[0]),
771
771
  (0, global_params.sample_plane_dim[1]),
772
772
  (-cell_params.cell_axial_radius, cell_params.cell_axial_radius),
773
- ),
773
+ ), # 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
774
  oversample_motion_time=global_params.oversample_motion_time,
775
775
  t_end=totaltime,
776
776
  )
@@ -0,0 +1,3 @@
1
+ from .nuclearporecomplexes import generate_nup96_positions
2
+
3
+ __all__ = ["generate_nup96_positions"]
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+
3
+
4
+ def generate_nup96_positions(
5
+ ring_diameter: float = 107.0,
6
+ molecule_spacing: float = 12.0,
7
+ ring_spacing: float = 50.0,
8
+ ) -> np.ndarray:
9
+ """
10
+ Generate the 3D coordinates of Nup96 proteins in the nuclear pore complex.
11
+
12
+ Parameters:
13
+ -----------
14
+ ring_diameter : float
15
+ Diameter of the main ring in nanometers (default: 107.0 nm)
16
+ molecule_spacing : float
17
+ Distance between two Nup96 molecules within same section (default: 12.0 nm)
18
+ ring_spacing : float
19
+ Distance between nuclear and cytoplasmic rings in z-direction (default: 50.0 nm)
20
+
21
+ Returns:
22
+ --------
23
+ numpy.ndarray
24
+ Array of shape (32, 3) containing x, y, z coordinates for all Nup96 proteins in um
25
+ First 32 coordinates are the main structural Nup96 (16 nuclear, 16 cytoplasmic)
26
+ """
27
+
28
+ # Initialize array to store coordinates
29
+ coordinates = np.zeros((32, 3))
30
+ ring_radius = ring_diameter / 2
31
+
32
+ # Generate positions for both main rings (nuclear and cytoplasmic)
33
+ for ring in range(2): # 0 = nuclear side, 1 = cytoplasmic side
34
+ z = ring_spacing / 2 if ring == 0 else -ring_spacing / 2
35
+
36
+ for octant in range(8):
37
+ # Calculate base angle for this octant
38
+ base_angle = octant * 2 * np.pi / 8
39
+
40
+ # Calculate the center position of this octamer
41
+ center_x = ring_radius * np.cos(base_angle)
42
+ center_y = ring_radius * np.sin(base_angle)
43
+
44
+ # Place two Nup96 molecules in each octant section
45
+ for molecule in range(2):
46
+ # Calculate the offset direction perpendicular to the radius
47
+ perpendicular_angle = (
48
+ base_angle
49
+ + np.pi / 2
50
+ + molecule_spacing / 3 * (1 if molecule == 0 else -1)
51
+ )
52
+
53
+ # Offset from center position
54
+ offset = (molecule_spacing / 3) * (-1 if molecule == 0 else 1) + (
55
+ molecule_spacing / 5
56
+ ) * (-1 if ring == 0 else 1)
57
+
58
+ # Calculate final x and y coordinates
59
+ x = center_x + offset * np.cos(perpendicular_angle)
60
+ y = center_y + offset * np.sin(perpendicular_angle)
61
+
62
+ # Store coordinates
63
+ idx = ring * 16 + octant * 2 + molecule
64
+ coordinates[idx] = [x, y, z]
65
+ # add 1 nm gitter
66
+ coordinates[idx] += np.random.normal(0, 1, 3)
67
+
68
+ return coordinates / 1000.0
@@ -1,6 +1,7 @@
1
1
  import numpy as np
2
- from .boundary_conditions import _refecting_boundary, _absorbing_boundary
2
+
3
3
  from ...probabilityfuncs.markov_chain import MCMC_state_selection
4
+ from .boundary_conditions import _absorbing_boundary, _refecting_boundary
4
5
 
5
6
  BOUNDARY_CONDITIONS = {
6
7
  "reflecting": _refecting_boundary,
@@ -161,10 +162,8 @@ class FBM_BP:
161
162
  phi = np.zeros(self.n)
162
163
  psi = np.zeros(self.n)
163
164
  # 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))
165
+ gn = np.random.normal(0, 1, self.n) * np.sqrt(
166
+ 2 * self._diff_a_n * (self.dt ** (2 * self._hurst_n))
168
167
  )
169
168
  # catch is all hurst are 0.5 then use the gaussian noise vector corresponding to the scale defined by the diffusion parameter
170
169
  if np.all(self._hurst_n == 0.5):
@@ -124,12 +124,12 @@ class Track_generator:
124
124
  rel_space_lim[i] = self.space_lim[i] - initials[i]
125
125
 
126
126
  # convert the diffusion_coefficients
127
- diffusion_coefficient = self._convert_diffcoef_um2s_um2xms(
128
- diffusion_coefficient
129
- )
127
+ # diffusion_coefficient = self._convert_diffcoef_um2s_um2xms(
128
+ # diffusion_coefficient
129
+ # )
130
130
  fbm = FBM_BP(
131
131
  n=track_length,
132
- dt=1,
132
+ dt=self.oversample_motion_time / 1000.0,
133
133
  hurst_parameters=[hurst_exponent],
134
134
  diffusion_parameters=[diffusion_coefficient],
135
135
  diffusion_parameter_transition_matrix=[1],
@@ -216,11 +216,11 @@ class Track_generator:
216
216
  for i in range(3):
217
217
  rel_space_lim[i] = self.space_lim[i] - initials[i]
218
218
  # convert the diffusion_coefficients
219
- diffusion_parameters = self._convert_diffcoef_um2s_um2xms(diffusion_parameters)
219
+ # diffusion_parameters = self._convert_diffcoef_um2s_um2xms(diffusion_parameters)
220
220
  # initialize the fbm class
221
221
  fbm = FBM_BP(
222
222
  n=track_length,
223
- dt=1,
223
+ dt=self.oversample_motion_time / 1000.0,
224
224
  hurst_parameters=hurst_parameters,
225
225
  diffusion_parameters=diffusion_parameters,
226
226
  diffusion_parameter_transition_matrix=diffusion_transition_matrix,
@@ -23,17 +23,21 @@ class LaserParameters:
23
23
 
24
24
  wavelength: float # Wavelength in nanometers
25
25
  power: Union[float, Callable[[float], float]] # Power in watts
26
- beam_width: float # 1/e² beam width at waist in microns
26
+ beam_width: Optional[float] = None # 1/e² beam width at waist in microns
27
27
  numerical_aperture: Optional[float] = None # NA of focusing lens
28
- position: Union[Tuple[float, float, float], Callable[[float], np.ndarray]] = (
28
+ position: Union[
29
+ Tuple[float, float, float], Callable[[float], Tuple[float, float, float]]
30
+ ] = (
29
31
  0.0,
30
32
  0.0,
31
33
  0.0,
32
34
  )
33
- refractive_index: float = 1.0 # Refractive index of medium
35
+ refractive_index: Optional[float] = 1.0 # Refractive index of medium
34
36
 
35
37
  def __post_init__(self):
36
38
  """Validate parameters after initialization."""
39
+ if not self.beam_width:
40
+ self.beam_width = self.diffraction_limited_width
37
41
  self._validate_parameters()
38
42
  self._compute_derived_parameters()
39
43
  self.max_power = self.power
@@ -0,0 +1,102 @@
1
+ import math
2
+ from functools import partial
3
+ from typing import Callable, List, Tuple
4
+
5
+ import numpy as np
6
+
7
+ """Currently unused module"""
8
+
9
+
10
+ def plane_point_scan(
11
+ x_lims: List[float],
12
+ y_lims: List[float],
13
+ step_xy: float,
14
+ ) -> np.ndarray:
15
+ """
16
+ Generate a point scanning pattern for a confocal microscope plane scan.
17
+
18
+ Args:
19
+ x_lims (List[float]): [min_x, max_x] scanning limits in x direction
20
+ y_lims (List[float]): [min_y, max_y] scanning limits in y direction
21
+ step_xy (float): Step size between points in both x and y directions
22
+
23
+ Returns:
24
+ np.ndarray: Array of shape (n_points, 2) containing [x, y] coordinates for the scan
25
+ """
26
+ # Calculate number of points in each dimension
27
+ nx = math.ceil((x_lims[1] - x_lims[0]) / step_xy) + 1
28
+ ny = math.ceil((y_lims[1] - y_lims[0]) / step_xy) + 1
29
+
30
+ # Generate coordinate arrays
31
+ x = np.linspace(x_lims[0], x_lims[1], nx)
32
+ y = np.linspace(y_lims[0], y_lims[1], ny)
33
+
34
+ # Create meshgrid for all coordinates
35
+ xx, yy = np.meshgrid(x, y)
36
+
37
+ # Convert to scan pattern array
38
+ # For even rows, reverse x direction for serpentine scan
39
+ scan_points = []
40
+ for i in range(ny):
41
+ row_x = xx[i]
42
+ row_y = yy[i]
43
+
44
+ if i % 2 == 1: # Reverse even rows for serpentine pattern
45
+ row_x = row_x[::-1]
46
+
47
+ points = np.column_stack((row_x, row_y))
48
+ scan_points.append(points)
49
+
50
+ # Combine all points into final array
51
+ scan_pattern = np.vstack(scan_points)
52
+
53
+ return scan_pattern
54
+
55
+
56
+ def confocal_pointscan_time_z(
57
+ x_lims: List[float],
58
+ y_lims: List[float],
59
+ step_xy: float, # can be defined as the beam width at the focus plane
60
+ frame_exposure_time: float, # s
61
+ ) -> Tuple[Callable[[float, float], Tuple[float, float, float]], float]:
62
+ scan_pattern = plane_point_scan(x_lims=x_lims, y_lims=y_lims, step_xy=step_xy)
63
+ scan_pattern_len = len(scan_pattern)
64
+
65
+ dwell_time = frame_exposure_time / scan_pattern_len
66
+
67
+ def return_laser_position(
68
+ z_position: float, time: float
69
+ ) -> Tuple[float, float, float]:
70
+ index_frame = time % frame_exposure_time
71
+ ind = int(index_frame / dwell_time)
72
+ # print(index_frame, ind)
73
+ return (*scan_pattern[ind], z_position)
74
+
75
+ return return_laser_position, dwell_time
76
+
77
+
78
+ def confocal_pointscan_time_z0(
79
+ x_lims: List[float],
80
+ y_lims: List[float],
81
+ step_xy: float, # can be defined as the beam width at the focus plane
82
+ frame_exposure_time: float, # s
83
+ z_val: float, # um
84
+ ) -> Tuple[Callable[[float], Tuple[float, float, float]], float]:
85
+ """
86
+ Create a generator for a point scanning pattern for a confocal microscope plane scan which takes in a time and returns the postion of the laser.
87
+
88
+ Args:
89
+ x_lims (List[float]): [min_x, max_x] scanning limits in x direction
90
+ y_lims (List[float]): [min_y, max_y] scanning limits in y direction
91
+ step_xy (float): Step size between points in both x and y directions
92
+ frame_exposure_time (float): exposure time of the frame
93
+ z_val (float): z value of the sample plane
94
+
95
+ Returns:
96
+ Callable[time]: (x,y,z) position of the laser
97
+ dwell_time (float): the dwell time per position
98
+ """
99
+ func, dwell_time = confocal_pointscan_time_z(
100
+ x_lims, y_lims, step_xy, frame_exposure_time
101
+ )
102
+ return partial(func, z_val), dwell_time
@@ -85,15 +85,19 @@ class PSFEngine:
85
85
  self._grid_xy = _generate_grid(self._psf_size, self.params.pixel_size)
86
86
 
87
87
  # Pre-calculate normalized sigma values
88
- self._norm_sigma_xy = self._sigma_xy / 2.355
89
- self._norm_sigma_z = self._sigma_z / 2.355
88
+ self._norm_sigma_xy = self._sigma_xy / 2.0
89
+ self._norm_sigma_z = self._sigma_z / 2.0
90
90
 
91
91
  # Generate pinhole mask if specified
92
92
  if self.params.pinhole_radius is not None:
93
93
  if self.params.pinhole_radius < AIRYFACTOR * self._sigma_xy:
94
- raise ValueError(
94
+ RuntimeWarning(
95
95
  f"Pinhole size ({self.params.pinhole_radius} um) is smaller than {AIRYFACTOR} times the Airy lobe. This will diffract the emission light in the pinhole; an ideal pinhole size for this setup is {self._sigma_xy} um."
96
96
  )
97
+ #
98
+ # raise ValueError(
99
+ # f"Pinhole size ({self.params.pinhole_radius} um) is smaller than {AIRYFACTOR} times the Airy lobe. This will diffract the emission light in the pinhole; an ideal pinhole size for this setup is {self._sigma_xy} um."
100
+ # )
97
101
  self._pinhole_mask = self._generate_pinhole_mask()
98
102
  else:
99
103
  self._pinhole_mask = None
@@ -116,7 +120,9 @@ class PSFEngine:
116
120
  return (r <= self.params.pinhole_radius).astype(np.float64)
117
121
 
118
122
  @lru_cache(maxsize=128)
119
- def psf_z(self, x_val: float, y_val: float, z_val: float) -> NDArray[np.float64]:
123
+ def psf_z(
124
+ self, x_val: float, y_val: float, z_val: float, norm_scale: bool = True
125
+ ) -> NDArray[np.float64]:
120
126
  """Calculate the PSF at the detector for a point source at z_val.
121
127
 
122
128
  This represents how light from a point source at position z_val
@@ -132,13 +138,20 @@ class PSFEngine:
132
138
  2D array containing the light intensity pattern at the detector
133
139
  """
134
140
  x, y = self._grid_xy
141
+ sigma_xy_z_squared = (self._norm_sigma_xy**2) * (
142
+ 1 + (z_val / self._norm_sigma_z) ** 2
143
+ )
135
144
 
136
145
  # Calculate how light from the point source diffracts through collection optics
137
- r_squared = (
138
- (x - x_val % self.params.pixel_size) / self._norm_sigma_xy
139
- ) ** 2 + ((y - y_val % self.params.pixel_size) / self._norm_sigma_xy) ** 2
140
- z_term = (z_val / self._norm_sigma_z) ** 2
141
- psf_at_detector = np.exp(-0.5 * (r_squared + z_term))
146
+ r_squared = (x - x_val % self.params.pixel_size) ** 2 + (
147
+ y - y_val % self.params.pixel_size
148
+ ) ** 2
149
+ psf_at_detector = np.exp(-0.5 * (r_squared / sigma_xy_z_squared))
150
+
151
+ if norm_scale:
152
+ psf_at_detector = self.normalize_psf(
153
+ psf_at_detector, mode="sum"
154
+ ) * self.psf_z_xy0(z_val)
142
155
 
143
156
  if self._pinhole_mask is not None:
144
157
  # Apply pinhole's spatial filtering
@@ -45,15 +45,33 @@ class StateTransitionCalculator:
45
45
  self.fluorescent_state_history[i.name] = [0, laser_intensities]
46
46
  return laser_intensities
47
47
 
48
+ def _get_intensities(self, time_pos: int, time_laser: float) -> dict:
49
+ laser_intensities = self.laser_intensity_generator(
50
+ florPos=self.flurophoreobj.position_history[time_pos],
51
+ time=time_laser,
52
+ )
53
+ return laser_intensities
54
+
48
55
  def MCMC(self) -> Tuple[State, ErnoMsg]:
49
56
  time = 0
50
57
  transitions = self.flurophoreobj.state_history[self.current_global_time][2]
58
+ if not transitions:
59
+ self.fluorescent_state_history[
60
+ self.flurophoreobj.fluorophore.states[
61
+ self.flurophoreobj.state_history[self.current_global_time][0].name
62
+ ]
63
+ ][0] += self.time_duration
64
+ return self.flurophoreobj.fluorophore.states[
65
+ self.flurophoreobj.state_history[self.current_global_time][0].name
66
+ ], ErnoMsg(success=True)
51
67
  final_state_name = transitions[0].from_state
52
68
  laser_intensities = self._initialize_state_hist(
53
69
  self.current_global_time, time + self.current_global_time_s
54
70
  )
55
-
56
71
  while time < self.time_duration:
72
+ laser_intensities = self._get_intensities(
73
+ self.current_global_time, self.current_global_time_s + time
74
+ )
57
75
  stateTransitionMatrixR = [
58
76
  sum(
59
77
  state_transitions.rate()(laser["wavelength"], laser["intensity"])
@@ -62,8 +80,22 @@ class StateTransitionCalculator:
62
80
  for state_transitions in transitions
63
81
  ] # 1/s
64
82
  if not stateTransitionMatrixR:
83
+ if (
84
+ self.flurophoreobj.fluorophore.states[final_state_name].state_type
85
+ == StateType.FLUORESCENT
86
+ ):
87
+ self.fluorescent_state_history[
88
+ self.flurophoreobj.fluorophore.states[final_state_name].name
89
+ ][0] += self.time_duration
65
90
  break
66
91
  if sum(stateTransitionMatrixR) == 0:
92
+ if (
93
+ self.flurophoreobj.fluorophore.states[final_state_name].state_type
94
+ == StateType.FLUORESCENT
95
+ ):
96
+ self.fluorescent_state_history[
97
+ self.flurophoreobj.fluorophore.states[final_state_name].name
98
+ ][0] += self.time_duration
67
99
  break
68
100
 
69
101
  # print(final_state_name)
@@ -133,8 +133,8 @@ class SampleSpace:
133
133
  z_min, z_max = fov.z_bounds
134
134
 
135
135
  return (
136
- 0 <= x_min <= x_max <= self.x_max
137
- and 0 <= y_min <= y_max <= self.y_max
136
+ x_min <= x_min <= x_max <= self.x_max
137
+ and x_min <= y_min <= y_max <= self.y_max
138
138
  and self.z_min <= z_min <= z_max <= self.z_max
139
139
  )
140
140
 
@@ -332,3 +332,34 @@ class SamplePlane:
332
332
  ) -> Tuple[Tuple[float, float], Tuple[float, float], Tuple[float, float]]:
333
333
  """Return the sample space bounds"""
334
334
  return self._space.bounds
335
+
336
+
337
+ def _FOV_lims(
338
+ xyoffset: Tuple[float, float],
339
+ detector_pixelcount: Tuple[int, int],
340
+ detector_pixelsize_magnification: float,
341
+ ) -> Tuple[Tuple[float, float], Tuple[float, float]]:
342
+ """
343
+ Utility to determine the FOV (what regions in the sample space the camera can capture) -> mainly used for defining laser position callables.
344
+
345
+ Parameters:
346
+ -----------
347
+ xyoffset: Tuple[float, float]
348
+ position in the sample plane which defines the bottom left corner on the detector pixel array; in units of the sample space (um)
349
+
350
+ detector_pixelcount: Tuple[int, int]
351
+ number of pixels in the detector in the both x and y
352
+
353
+ detector_pixelsize_magnification: float
354
+ the pixel size of each pixel (of detector_pixelcount) after the magnification of the optical setup is considered. I.e what each pixel in the final image represents in the units of the sample space (um).
355
+
356
+ Returns:
357
+ --------
358
+ (x_lims, y_lims): Tuple[Tuple[float, float], Tuple[float, float]]
359
+ min < max for each lim
360
+ """
361
+ x_min = xyoffset[0]
362
+ y_min = xyoffset[1]
363
+ x_max = x_min + detector_pixelcount[0] * detector_pixelsize_magnification
364
+ y_max = y_min + detector_pixelcount[1] * detector_pixelsize_magnification
365
+ return ((x_min, x_max), (y_min, y_max))
AMS_BP/sim_microscopy.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from typing import Callable, Dict, List, Literal, Optional, Tuple
2
+ from typing import Callable, Dict, List, Literal, Optional, Tuple, Union
3
3
 
4
4
  import numpy as np
5
5
 
@@ -16,7 +16,7 @@ from .photophysics.photon_physics import (
16
16
  incident_photons,
17
17
  )
18
18
  from .photophysics.state_kinetics import StateTransitionCalculator
19
- from .sample.flurophores.flurophore_schema import WavelengthDependentProperty
19
+ from .sample.flurophores.flurophore_schema import StateType, WavelengthDependentProperty
20
20
  from .sample.sim_sampleplane import EMPTY_STATE_HISTORY_DICT, SamplePlane
21
21
  from .utils.util_functions import ms_to_seconds
22
22
 
@@ -70,7 +70,9 @@ class VirtualMicroscope:
70
70
  "_time": self._time,
71
71
  }
72
72
 
73
- def _set_laser_powers(self, laser_power: Dict[str, float]) -> None:
73
+ def _set_laser_powers(
74
+ self, laser_power: Dict[str, Union[float, Callable[[float], float]]]
75
+ ) -> None:
74
76
  if laser_power is not None:
75
77
  for laser in laser_power.keys():
76
78
  if isinstance(self.lasers[laser].params.power, float) and isinstance(
@@ -86,7 +88,14 @@ class VirtualMicroscope:
86
88
  self.lasers[laser].params.power = laser_power[laser]
87
89
 
88
90
  def _set_laser_positions(
89
- self, laser_positions: Dict[str, Tuple[float, float, float]]
91
+ self,
92
+ laser_positions: Dict[
93
+ str,
94
+ Union[
95
+ Tuple[float, float, float],
96
+ Callable[[float], Tuple[float, float, float]],
97
+ ],
98
+ ],
90
99
  ) -> None:
91
100
  if laser_positions is not None:
92
101
  for laser in laser_positions.keys():
@@ -95,15 +104,28 @@ class VirtualMicroscope:
95
104
  def run_sim(
96
105
  self,
97
106
  z_val: float, # um
98
- laser_power: Dict[str, float], # str = lasername, float = power in W
107
+ laser_power: Dict[
108
+ str, Union[float, Callable[[float], float]] # power or f(t) -> power
109
+ ], # str = lasername, float = power in W
99
110
  xyoffset: Tuple[
100
111
  float, float
101
112
  ], # location of the bottom left corner of the field of view -> sample -> camera
102
- laser_position: Dict[str, Tuple[float, float, float]]
103
- | None, # str = lasername, Tuple = x, y, z in um at the sample plane
113
+ laser_position: Optional[
114
+ Dict[
115
+ str, # laser name
116
+ Union[
117
+ Tuple[float, float, float], # x, y, z
118
+ Callable[[float], Tuple[float, float, float]], # f(t) -> x, y, z
119
+ ],
120
+ ]
121
+ ] = None, # str = lasername, Tuple = x, y, z in um at the sample plane
104
122
  duration_total: Optional[int] = None, # ms
105
123
  exposure_time: Optional[int] = None,
106
124
  interval_time: Optional[int] = None,
125
+ scanning: Optional[
126
+ bool
127
+ ] = False, # True if scanning -> laser will autoposition to the xy location of the fluorophore but at the z_val of the plane being imaged and NOT the z value of the fluorophore.
128
+ # as a consequence the "effective" time spent by the laser of on the fluorophore position is not the dwell time of traditional confocal, but "always on". TODO: better fix.
107
129
  ) -> Tuple[np.ndarray, MetaData]:
108
130
  self._set_laser_powers(laser_power=laser_power)
109
131
  if laser_position is not None:
@@ -151,7 +173,9 @@ class VirtualMicroscope:
151
173
  statehist = fluorObj.state_history[time]
152
174
  # if not aval transitions go next
153
175
  # this assumes that the bleached state is the only state from which there are no more transitions
154
- if not statehist[2]:
176
+ if (not statehist[2]) and (
177
+ statehist[0].state_type != StateType.FLUORESCENT
178
+ ):
155
179
  fluorObj.state_history[time + self.sample_plane.dt] = statehist
156
180
  continue
157
181
 
@@ -167,6 +191,11 @@ class VirtualMicroscope:
167
191
  ) -> dict:
168
192
  laser_intensities = {}
169
193
  for laserID in laser_power.keys():
194
+ if scanning:
195
+ self.lasers[laserID].params.position = (
196
+ *florPos[:2],
197
+ 0,
198
+ ) # z value is 0 since this is the neew focus plane
170
199
  laser_intensities[laserID] = {
171
200
  "wavelength": self.lasers[laserID].params.wavelength,
172
201
  "intensity": (
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  import pickle
4
+ from bisect import bisect_right
4
5
 
5
6
  import numpy as np
6
7
  import skimage as skimage
@@ -9,6 +10,14 @@ from PIL import Image
9
10
  from ..utils.decorators import cache
10
11
 
11
12
 
13
+ def find_le(a, x) -> int:
14
+ "Find rightmost value less than or equal to x"
15
+ i = bisect_right(a, x)
16
+ if i:
17
+ return a[i - 1]
18
+ raise ValueError
19
+
20
+
12
21
  def convert_arrays_to_lists(obj: np.ndarray | dict) -> list | dict:
13
22
  """
14
23
  Recursively convert NumPy arrays to lists.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AMS_BP
3
- Version: 0.0.26
3
+ Version: 0.0.32
4
4
  Summary: Advanced Microscopy Simulations developed for the Weber Lab by Baljyot Singh Parmar
5
5
  Project-URL: Documentation, https://joemans3.github.io/AMS_BP/
6
6
  Project-URL: Source code, https://github.com/joemans3/AMS_BP
@@ -1,7 +1,7 @@
1
- AMS_BP/__init__.py,sha256=PRd0eVcZs7H8ezQRMXS-q-AES78dJ-DWPSQZT3j5QP0,327
1
+ AMS_BP/__init__.py,sha256=cpKNY-v_X4yiqwdfXMUdEc-j-zmY5npRlWs-Q6Sd4eI,327
2
2
  AMS_BP/run_cell_simulation.py,sha256=7InopFikjo0HfaLO2siXskBIbyCIte9avG4YXjjaWCI,7420
3
3
  AMS_BP/sim_config.toml,sha256=3IqOQIJYmP5g4okk15nqQiNZb3ij7Pt63HbpI-5tySw,11672
4
- AMS_BP/sim_microscopy.py,sha256=u60ApTA6MTUmqSAd7EsAxweKya_Typput8NumDq9fp8,18697
4
+ AMS_BP/sim_microscopy.py,sha256=Vsga9lTWqP0pjEnfFj0qKaSz7YZ_F4E3TqsaErwhi3E,19991
5
5
  AMS_BP/cells/__init__.py,sha256=yWFScBC1uOGDkeC8i1m1ZBtIREcyt4JHxYa72LxbBZU,177
6
6
  AMS_BP/cells/base_cell.py,sha256=FIPB9J8F40tb53vv7C6qG-SaAFLOI8-MGIk1mmZ-gnI,1503
7
7
  AMS_BP/cells/rectangular_cell.py,sha256=5yGxvTXYvgldLXyWXpE_SD9Zx2NLerC-I2j02reHsJ0,2515
@@ -9,17 +9,19 @@ AMS_BP/cells/rod_cell.py,sha256=jQ1kLEk74Pv2rcXPRJ6-QJJhux-mYiDSytzqlxCNWfA,3181
9
9
  AMS_BP/cells/spherical_cell.py,sha256=n3ou3tW0nCxXIwv6uLkVKHkYCfgoNn8VI6CVTLBIll0,2140
10
10
  AMS_BP/configio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  AMS_BP/configio/configmodels.py,sha256=Gd2Qdut0u5zS0IpjPwGIB3ur-b4Dfa9e8SbhotBKymc,2894
12
- AMS_BP/configio/convertconfig.py,sha256=Fg9pOCZSxmWuHnrg-5xZRvhPEK6Qc1kXqu6LL9e9QYw,34741
12
+ AMS_BP/configio/convertconfig.py,sha256=zLyHWmqQnpKDGcIoVlI03dCtXa4L953kzRvPx7-ZqUM,34952
13
13
  AMS_BP/configio/experiments.py,sha256=HdfaSi0gPPJ_wLF87XcW5ICja19Uezx7-ygFEwNzi30,3995
14
14
  AMS_BP/configio/saving.py,sha256=596QgAadV32rzsN4B2FngGFcBWCzCDnLFN-qtQsv3bM,857
15
+ AMS_BP/groundtruth_generators/__init__.py,sha256=UPVmhiB81OfyqAes5LoN-n6XgQuBCYCqRPAGd2jpMfc,99
16
+ AMS_BP/groundtruth_generators/nuclearporecomplexes.py,sha256=1aBcWltsKo0OGd7A9GfuEZ3azQBx5SzpjrSLLMXuYn8,2518
15
17
  AMS_BP/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
18
  AMS_BP/metadata/metadata.py,sha256=YDumjc5sI3lY_UZx8f0ZhMqbG2qKQkysXwl7CY4ZtnY,2927
17
19
  AMS_BP/motion/__init__.py,sha256=cy3W-wCRjjlN1DrTqYc-JltYwcE8SZCXMVPJ2o6q_BQ,178
18
20
  AMS_BP/motion/condensate_movement.py,sha256=eig4WtD7o1cvIafWMjOk6pqxyhe_IIucgLcBEoDvasU,11648
19
- AMS_BP/motion/track_gen.py,sha256=Z3QJLVMP1gX4SlgOXFxBg8sJhBG0Xq25ixnBoEHEAZI,19462
21
+ AMS_BP/motion/track_gen.py,sha256=2ssg8BXxZUEufycqgziL2BOeKOInTmmjzsthfS80gfI,19540
20
22
  AMS_BP/motion/movement/__init__.py,sha256=PqovpG4dAuFFIP9M2_kt-6egQJX3P5ig4MMWVzNaswg,278
21
23
  AMS_BP/motion/movement/boundary_conditions.py,sha256=jpfK3AEUY8btrTsu19bpUfx-jri7_HfyxqMFjMoxAVM,2200
22
- AMS_BP/motion/movement/fbm_BP.py,sha256=dH-JZiAInnIaZXH1wAAo8dOIX9zafclqnZ4dOhKtnO0,9327
24
+ AMS_BP/motion/movement/fbm_BP.py,sha256=47d2ph4r8Izso_mBxxgQYH9xjEqj_zXUzIGpEXPEhFM,9292
23
25
  AMS_BP/optics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
26
  AMS_BP/optics/camera/__init__.py,sha256=eCoDUFHcoCWgbgYdLn8EH7AULM53A3XWTXNZnV8QxeY,182
25
27
  AMS_BP/optics/camera/detectors.py,sha256=_815Ovo7Aj375OZh5Xim8pFuZEEcSVtSdnLRYFqb3_8,10355
@@ -29,17 +31,18 @@ AMS_BP/optics/filters/filters.py,sha256=-iw7eqmDO77SEqlFTv5jJNVwpA8y93TLsjy5hhsA
29
31
  AMS_BP/optics/filters/channels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
32
  AMS_BP/optics/filters/channels/channelschema.py,sha256=SConyA5yVdfnI_8sgcxVC8SV7S8tGUJYPPC6jn7lglU,906
31
33
  AMS_BP/optics/lasers/__init__.py,sha256=T7dHohhyLf_pBw4TidarYHWmiwxVXGE71-Bf1aeBbuc,564
32
- AMS_BP/optics/lasers/laser_profiles.py,sha256=7mqf5VMpb0VN_veqYEdeiakr0kaOilfGzNq5mzFQuRw,17136
34
+ AMS_BP/optics/lasers/laser_profiles.py,sha256=Naz42SSqG_VS7TtLfM6kz7Gs7opDh6wa2An9CdR6C9M,17286
35
+ AMS_BP/optics/lasers/scanning_patterns.py,sha256=Xen96e5BlYI892HzZDXdwm80id6TbyNCNj-qvnRnSnI,3393
33
36
  AMS_BP/optics/psf/__init__.py,sha256=ezrKPgpTeR4gTHOvF0mhF6u2zMMTd8Bgp8PGeOf11fA,121
34
- AMS_BP/optics/psf/psf_engine.py,sha256=CDtnZpUvYexIih6ssBZsd5s1RUSa5fjUxgzEVELo6CE,9684
37
+ AMS_BP/optics/psf/psf_engine.py,sha256=FbR4VHQ-VgCWrrDj8AHPPnVgwVUGs-OP19w_TjcbMcU,10215
35
38
  AMS_BP/photophysics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
39
  AMS_BP/photophysics/photon_physics.py,sha256=9FWBXaxuSRaSxW8bY0x1d5R5buooibZbRdYTuQcMXhQ,6624
37
- AMS_BP/photophysics/state_kinetics.py,sha256=IdZtlHCLs--iSjLwDu2IQA617qXC4la8VpqosrM-vgQ,5401
40
+ AMS_BP/photophysics/state_kinetics.py,sha256=hDb1VqoeEkbYcse-N-S7BzyE4hIt4KLMju7ZKOIJDYc,6980
38
41
  AMS_BP/probabilityfuncs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
42
  AMS_BP/probabilityfuncs/markov_chain.py,sha256=LV6KGr8Lv4NIvBPJqsR0CEynssa_mPH30qLaK85GObA,4339
40
43
  AMS_BP/probabilityfuncs/probability_functions.py,sha256=j_rIxrupGBf_FKkQBh1TvEa34A44jAasaZQRg2u3FuY,11793
41
44
  AMS_BP/sample/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
- AMS_BP/sample/sim_sampleplane.py,sha256=A1BnGaFDnJ0VYaXUmpr0bLdZ01UzfZrATBTubGt3gjo,11525
45
+ AMS_BP/sample/sim_sampleplane.py,sha256=-Vb3o3vgUuN1qfoLMR8SanTbI7JtbtBvs78QzRdkps0,12826
43
46
  AMS_BP/sample/flurophores/__init__.py,sha256=arLVUx1-yh5T41fH9Mr6RNY8Ao3ZioFQUtsfEZlzQkU,250
44
47
  AMS_BP/sample/flurophores/flurophore_schema.py,sha256=j8jnr4w4ltuhXUkjd74IoyyuotyH52mn8FWkgahX5Vw,10886
45
48
  AMS_BP/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -47,9 +50,9 @@ AMS_BP/utils/constants.py,sha256=-r3cnEiuxb-XzRgGhpOuJshYYhKnDu_hv0YSffkxAfc,273
47
50
  AMS_BP/utils/decorators.py,sha256=4qFdvzPJne0dhkhD1znPxRln1Rfr5NX8rdcCDcbATRU,6224
48
51
  AMS_BP/utils/errors.py,sha256=7BOd-L4_YeKmWn3Q4EOdTnNF3Bj_exDa3eg5X0yCZrc,759
49
52
  AMS_BP/utils/maskMaker.py,sha256=2ca3n2nc8rFtUh1LurKXOJJsUmhrOpWbRnVX7fjRVvs,335
50
- AMS_BP/utils/util_functions.py,sha256=jI6WBh09_khdABnEoVK7SK1WRvCLHuw40f5ALyflzlc,9478
51
- ams_bp-0.0.26.dist-info/METADATA,sha256=aaB17p23_wqvfOqQQfb90g7zFD1MrgpdXwoCsbYx37s,5869
52
- ams_bp-0.0.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
- ams_bp-0.0.26.dist-info/entry_points.txt,sha256=MFUK9bZWW61djfsavqopMqiVPVn4lJtt6v8qzyEFyNM,76
54
- ams_bp-0.0.26.dist-info/licenses/LICENSE,sha256=k_-JV1DQKvO0FR8WjvOisqdTl0kp6VJ7RFM3YZhao0c,1071
55
- ams_bp-0.0.26.dist-info/RECORD,,
53
+ AMS_BP/utils/util_functions.py,sha256=9Qlr4kjY04fObktR8TrzB0IgoG1yXtcmxPRX9AN34mM,9671
54
+ ams_bp-0.0.32.dist-info/METADATA,sha256=ga6yZDd1sIWFqn1pdMbgjhvw4iEpyU1B4gJTbi7S21w,5869
55
+ ams_bp-0.0.32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ ams_bp-0.0.32.dist-info/entry_points.txt,sha256=MFUK9bZWW61djfsavqopMqiVPVn4lJtt6v8qzyEFyNM,76
57
+ ams_bp-0.0.32.dist-info/licenses/LICENSE,sha256=k_-JV1DQKvO0FR8WjvOisqdTl0kp6VJ7RFM3YZhao0c,1071
58
+ ams_bp-0.0.32.dist-info/RECORD,,