FlowCyPy 0.5.3__tar.gz → 0.5.7__tar.gz

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 (157) hide show
  1. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/__init__.py +1 -1
  2. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/_version.py +2 -2
  3. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/coupling_mechanism/empirical.py +2 -2
  4. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/coupling_mechanism/mie.py +6 -6
  5. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/coupling_mechanism/rayleigh.py +5 -5
  6. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/coupling_mechanism/uniform.py +2 -2
  7. flowcypy-0.5.7/FlowCyPy/cytometer.py +363 -0
  8. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/event_correlator.py +0 -19
  9. flowcypy-0.5.7/FlowCyPy/flow_cell.py +295 -0
  10. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/logger.py +2 -2
  11. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/particle_count.py +24 -10
  12. flowcypy-0.5.7/FlowCyPy/population.py +139 -0
  13. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/populations_instances.py +27 -11
  14. flowcypy-0.5.3/FlowCyPy/scatterer.py → flowcypy-0.5.7/FlowCyPy/scatterer_collection.py +21 -95
  15. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy.egg-info/PKG-INFO +7 -7
  16. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy.egg-info/SOURCES.txt +17 -18
  17. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy.egg-info/requires.txt +1 -1
  18. {flowcypy-0.5.3 → flowcypy-0.5.7}/PKG-INFO +7 -7
  19. {flowcypy-0.5.3 → flowcypy-0.5.7}/README.rst +5 -5
  20. flowcypy-0.5.7/developments/scripts/data_analysis.py +43 -0
  21. flowcypy-0.5.7/developments/scripts/dev_classifier.py +149 -0
  22. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/dev_study_on_ri.py +8 -7
  23. flowcypy-0.5.7/developments/scripts/dev_temp.py +154 -0
  24. flowcypy-0.5.7/developments/scripts/mat2csv.py +65 -0
  25. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/density_plots/1_populations.py +18 -14
  26. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/density_plots/2_populations.py +25 -19
  27. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/density_plots/3_populations.py +24 -17
  28. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/density_plots/custom_populations.py +21 -17
  29. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/extras/flow_cytometer_signal.py +23 -19
  30. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/extras/full_workflow.py +24 -20
  31. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/extras/scatterer_distribution.py +6 -6
  32. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/tutorials/workflow.py +32 -27
  33. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/_static/default.css +1 -1
  34. flowcypy-0.5.7/docs/source/_static/thumbnail.png +0 -0
  35. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/scatterer.rst +1 -1
  36. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/conf.py +5 -2
  37. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/examples.rst +4 -3
  38. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal/core_components.rst +2 -2
  39. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal/getting_started.rst +2 -2
  40. flowcypy-0.5.7/docs/source/internal/objectives/main.rst +83 -0
  41. flowcypy-0.5.7/docs/source/internal/objectives/pre.rst +31 -0
  42. flowcypy-0.5.7/docs/source/internal/objectives/stretch.rst +9 -0
  43. flowcypy-0.5.7/docs/source/internal/prerequisites/index.rst +12 -0
  44. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal/prerequisites/mathematics.rst +2 -2
  45. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal/prerequisites/optics.rst +2 -2
  46. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal/prerequisites/programming.rst +5 -6
  47. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal/ressources.rst +2 -2
  48. flowcypy-0.5.7/docs/source/internal/tasks.rst +15 -0
  49. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/internal.rst +6 -8
  50. flowcypy-0.5.7/docs/source/sg_execution_times.rst +70 -0
  51. {flowcypy-0.5.3 → flowcypy-0.5.7}/pyproject.toml +1 -1
  52. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_coupling_mechanism.py +7 -6
  53. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_distribution.py +0 -2
  54. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_flow_cytometer.py +29 -19
  55. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_peak_analyzer.py +20 -18
  56. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_population.py +13 -12
  57. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_scatterer_distribution.py +40 -12
  58. flowcypy-0.5.3/.condarc +0 -37
  59. flowcypy-0.5.3/.readthedocs.yml +0 -35
  60. flowcypy-0.5.3/FlowCyPy/cytometer.py +0 -198
  61. flowcypy-0.5.3/FlowCyPy/flow_cell.py +0 -122
  62. flowcypy-0.5.3/FlowCyPy/plottings.py +0 -270
  63. flowcypy-0.5.3/FlowCyPy/population.py +0 -239
  64. flowcypy-0.5.3/FlowCyPy/report.py +0 -236
  65. flowcypy-0.5.3/developments/dev_classifier.py +0 -140
  66. flowcypy-0.5.3/developments/get_started.md +0 -23
  67. flowcypy-0.5.3/developments/image.png +0 -0
  68. flowcypy-0.5.3/developments/output_file.prof +0 -0
  69. flowcypy-0.5.3/developments/test.pdf +0 -0
  70. flowcypy-0.5.3/docs/source/_static/thumbnail.png +0 -0
  71. flowcypy-0.5.3/docs/source/internal/prerequisites/index.rst +0 -12
  72. flowcypy-0.5.3/docs/source/internal/tasks.rst +0 -136
  73. {flowcypy-0.5.3 → flowcypy-0.5.7}/.flake8 +0 -0
  74. {flowcypy-0.5.3 → flowcypy-0.5.7}/.github/dependabot.yml +0 -0
  75. {flowcypy-0.5.3 → flowcypy-0.5.7}/.github/workflows/deploy_PyPi.yml +0 -0
  76. {flowcypy-0.5.3 → flowcypy-0.5.7}/.github/workflows/deploy_anaconda.yml +0 -0
  77. {flowcypy-0.5.3 → flowcypy-0.5.7}/.github/workflows/deploy_coverage.yml +0 -0
  78. {flowcypy-0.5.3 → flowcypy-0.5.7}/.github/workflows/deploy_documentation.yml +0 -0
  79. {flowcypy-0.5.3 → flowcypy-0.5.7}/.gitignore +0 -0
  80. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/classifier.py +0 -0
  81. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/coupling_mechanism/__init__.py +0 -0
  82. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/detector.py +0 -0
  83. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/directories.py +0 -0
  84. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/__init__.py +0 -0
  85. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/base_class.py +0 -0
  86. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/delta.py +0 -0
  87. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/lognormal.py +0 -0
  88. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/normal.py +0 -0
  89. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/particle_size_distribution.py +0 -0
  90. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/uniform.py +0 -0
  91. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/distribution/weibull.py +0 -0
  92. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/helper.py +0 -0
  93. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/noises.py +0 -0
  94. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/peak_locator/__init__.py +0 -0
  95. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/peak_locator/base_class.py +0 -0
  96. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/peak_locator/basic.py +0 -0
  97. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/peak_locator/derivative.py +0 -0
  98. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/peak_locator/moving_average.py +0 -0
  99. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/physical_constant.py +0 -0
  100. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/source.py +0 -0
  101. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/units.py +0 -0
  102. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy/utils.py +0 -0
  103. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy.egg-info/dependency_links.txt +0 -0
  104. {flowcypy-0.5.3 → flowcypy-0.5.7}/FlowCyPy.egg-info/top_level.txt +0 -0
  105. {flowcypy-0.5.3 → flowcypy-0.5.7}/LICENSE +0 -0
  106. {flowcypy-0.5.3 → flowcypy-0.5.7}/Untitled.ipynb +0 -0
  107. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/doc}/internship.pdf +0 -0
  108. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/create_images.py +0 -0
  109. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/dev_beads_analysis.py +0 -0
  110. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/dev_canto.py +0 -0
  111. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/dev_shot_noise_check.py +0 -0
  112. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/dev_study_on_size.py +0 -0
  113. {flowcypy-0.5.3/developments → flowcypy-0.5.7/developments/scripts}/profiler.py +0 -0
  114. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/Makefile +0 -0
  115. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/density_plots/README.rst +0 -0
  116. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/extras/README.rst +0 -0
  117. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/extras/distributions.py +0 -0
  118. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/noise_sources/README.rst +0 -0
  119. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/noise_sources/dark_current.py +0 -0
  120. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/noise_sources/shot_noise.py +0 -0
  121. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/noise_sources/thermal.py +0 -0
  122. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/examples/tutorials/README.rst +0 -0
  123. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/distributions/Delta.png +0 -0
  124. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/distributions/LogNormal.png +0 -0
  125. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/distributions/Normal.png +0 -0
  126. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/distributions/RosinRammler.png +0 -0
  127. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/distributions/Uniform.png +0 -0
  128. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/distributions/Weibull.png +0 -0
  129. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/example_0.png +0 -0
  130. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/example_1.png +0 -0
  131. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/example_2.png +0 -0
  132. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/example_3.png +0 -0
  133. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/flow_cytometer.png +0 -0
  134. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/images/logo.png +0 -0
  135. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/make.bat +0 -0
  136. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/_static/logo.png +0 -0
  137. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/analysis.rst +0 -0
  138. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/base.rst +0 -0
  139. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/detector.rst +0 -0
  140. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/distributions.rst +0 -0
  141. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/flow_cell.rst +0 -0
  142. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/flow_cytometer.rst +0 -0
  143. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/peak_locator.rst +0 -0
  144. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code/source.rst +0 -0
  145. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/code.rst +0 -0
  146. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/index.rst +0 -0
  147. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/references.rst +0 -0
  148. {flowcypy-0.5.3 → flowcypy-0.5.7}/docs/source/theory.rst +0 -0
  149. {flowcypy-0.5.3 → flowcypy-0.5.7}/meta.yaml +0 -0
  150. {flowcypy-0.5.3 → flowcypy-0.5.7}/notebook.ipynb +0 -0
  151. {flowcypy-0.5.3 → flowcypy-0.5.7}/setup.cfg +0 -0
  152. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/__init__.py +0 -0
  153. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_detector_noise.py +0 -0
  154. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_extra.py +0 -0
  155. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_noises.py +0 -0
  156. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_peak_algorithm.py +0 -0
  157. {flowcypy-0.5.3 → flowcypy-0.5.7}/tests/test_source.py +0 -0
@@ -7,7 +7,7 @@ except ImportError:
7
7
  from .units import ureg, watt, meter, second, liter, particle
8
8
  from .cytometer import FlowCytometer
9
9
  from .event_correlator import EventCorrelator
10
- from .scatterer import Scatterer, CouplingModel
10
+ from .scatterer_collection import ScattererCollection, CouplingModel
11
11
  from .population import Population
12
12
  from .detector import Detector
13
13
  from .flow_cell import FlowCell
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.5.3'
16
- __version_tuple__ = version_tuple = (0, 5, 3)
15
+ __version__ = version = '0.5.7'
16
+ __version_tuple__ = version_tuple = (0, 5, 7)
@@ -1,10 +1,10 @@
1
1
  import numpy as np
2
- from FlowCyPy import Scatterer, Detector
2
+ from FlowCyPy import ScattererCollection, Detector
3
3
  from FlowCyPy.source import BaseBeam
4
4
  from FlowCyPy.units import watt, meter
5
5
 
6
6
 
7
- def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: Scatterer, granularity: float = 1.0, A: float = 1.5, n: float = 2.0) -> float:
7
+ def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: ScattererCollection, granularity: float = 1.0, A: float = 1.5, n: float = 2.0) -> float:
8
8
  """
9
9
  Empirical model for scattering intensity based on particle size, granularity, and detector angle.
10
10
 
@@ -1,5 +1,5 @@
1
1
  import numpy as np
2
- from FlowCyPy import Scatterer, Detector
2
+ from FlowCyPy import ScattererCollection, Detector
3
3
  from FlowCyPy.source import BaseBeam
4
4
  from PyMieSim.experiment.scatterer import Sphere as PMS_SPHERE
5
5
  from PyMieSim.experiment.source import PlaneWave
@@ -94,13 +94,13 @@ def apply_rin_noise(source: BaseBeam, total_size: int, bandwidth: float) -> np.n
94
94
  return amplitude_with_rin
95
95
 
96
96
 
97
- def initialize_scatterer(scatterer: Scatterer, source: PlaneWave) -> PMS_SPHERE:
97
+ def initialize_scatterer(scatterer: ScattererCollection, source: PlaneWave) -> PMS_SPHERE:
98
98
  """
99
99
  Initializes the scatterer object for the PyMieSim experiment.
100
100
 
101
101
  Parameters
102
102
  ----------
103
- scatterer : Scatterer
103
+ scatterer : ScattererCollection
104
104
  The scatterer object containing particle data.
105
105
  source : PlaneWave
106
106
  The light source for the simulation.
@@ -114,7 +114,7 @@ def initialize_scatterer(scatterer: Scatterer, source: PlaneWave) -> PMS_SPHERE:
114
114
  ri_list = scatterer.dataframe['RefractiveIndex'].values
115
115
 
116
116
  if len(size_list) == 0:
117
- raise ValueError("Scatterer size list is empty.")
117
+ raise ValueError("ScattererCollection size list is empty.")
118
118
 
119
119
  size_list = size_list.quantity.magnitude * size_list.units
120
120
  ri_list = ri_list.quantity.magnitude * ri_list.units
@@ -155,7 +155,7 @@ def initialize_detector(detector: Detector, total_size: int) -> PMS_PHOTODIODE:
155
155
  )
156
156
 
157
157
 
158
- def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: Scatterer, tolerance: float = 1e-5) -> np.ndarray:
158
+ def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: ScattererCollection, tolerance: float = 1e-5) -> np.ndarray:
159
159
  """
160
160
  Computes the detected signal by analyzing the scattering properties of particles.
161
161
 
@@ -165,7 +165,7 @@ def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: Sca
165
165
  The light source object containing wavelength, power, and other optical properties.
166
166
  detector : Detector
167
167
  The detector object containing properties such as numerical aperture and angles.
168
- scatterer : Scatterer
168
+ scatterer : ScattererCollection
169
169
  The scatterer object containing particle size and refractive index data.
170
170
  tolerance : float, optional
171
171
  The tolerance for deciding if two values of size and refractive index are "close enough" to be cached.
@@ -1,11 +1,11 @@
1
1
 
2
2
  import numpy as np
3
- from FlowCyPy import Scatterer, Detector
3
+ from FlowCyPy import ScattererCollection, Detector
4
4
  from FlowCyPy.source import BaseBeam
5
5
  from FlowCyPy.units import meter
6
6
 
7
7
 
8
- def compute_scattering_cross_section(scatterer: Scatterer, source: BaseBeam, detector: Detector) -> np.ndarray:
8
+ def compute_scattering_cross_section(scatterer: ScattererCollection, source: BaseBeam, detector: Detector) -> np.ndarray:
9
9
  r"""
10
10
  Computes the Rayleigh scattering cross-section for a spherical particle with angle dependency.
11
11
 
@@ -28,8 +28,8 @@ def compute_scattering_cross_section(scatterer: Scatterer, source: BaseBeam, det
28
28
 
29
29
  Parameters
30
30
  ----------
31
- scatterer : Scatterer
32
- An instance of `Scatterer` containing the scatterer properties such as size and refractive index.
31
+ scatterer : ScattererCollection
32
+ An instance of `ScattererCollection` containing the scatterer properties such as size and refractive index.
33
33
  source : BaseBeam
34
34
  An instance of `BaseBeam` containing the laser properties, including the wavelength.
35
35
  detector : Detector
@@ -63,7 +63,7 @@ def compute_scattering_cross_section(scatterer: Scatterer, source: BaseBeam, det
63
63
  return cross_section.magnitude * meter**2
64
64
 
65
65
 
66
- def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: Scatterer) -> float:
66
+ def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: ScattererCollection) -> float:
67
67
  r"""
68
68
  Computes the power detected by a detector from a Rayleigh scattering event.
69
69
 
@@ -1,9 +1,9 @@
1
1
  import numpy as np
2
- from FlowCyPy import Scatterer, Detector, ureg
2
+ from FlowCyPy import ScattererCollection, Detector, ureg
3
3
  from FlowCyPy.source import BaseBeam
4
4
 
5
5
 
6
- def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: Scatterer) -> np.ndarray:
6
+ def compute_detected_signal(source: BaseBeam, detector: Detector, scatterer: ScattererCollection) -> np.ndarray:
7
7
  r"""
8
8
  Computes the power detected by a detector from a uniform distribution.
9
9
 
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import logging
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ from typing import List, Callable, Optional
8
+ from MPSPlots.styles import mps
9
+ from FlowCyPy.flow_cell import FlowCell
10
+ from FlowCyPy.detector import Detector
11
+ import pandas as pd
12
+ import pint_pandas
13
+ from FlowCyPy.units import Quantity, milliwatt
14
+ from FlowCyPy.logger import SimulationLogger
15
+ import seaborn as sns
16
+
17
+ # Set up logging configuration
18
+ logging.basicConfig(
19
+ level=logging.INFO,
20
+ format='%(levelname)s - %(message)s'
21
+ )
22
+
23
+
24
+ class FlowCytometer:
25
+ """
26
+ A simulation class for modeling flow cytometer signals, including Forward Scatter (FSC) and Side Scatter (SSC) channels.
27
+
28
+ The FlowCytometer class integrates optical and flow dynamics to simulate signal generation in a flow cytometer.
29
+ It handles particle distributions, flow cell properties, laser source configurations, and detector behavior to
30
+ replicate realistic cytometry conditions. This includes the generation of synthetic signal pulses for each
31
+ particle event and noise modeling for accurate signal representation.
32
+
33
+ Parameters
34
+ ----------
35
+ flow_cell : FlowCell
36
+ The flow cell object representing the fluidic and optical environment through which particles travel.
37
+ detectors : List[Detector]
38
+ A list of `Detector` objects representing the detectors used to measure optical signals (e.g., FSC and SSC). Exactly two detectors must be provided.
39
+ coupling_mechanism : str, optional
40
+ The scattering mechanism used to couple the signal from the particles to the detectors.
41
+ Supported mechanisms include: 'mie' (default): Mie scattering, 'rayleigh': Rayleigh scattering, 'uniform': Uniform signal coupling, 'empirical': Empirical data-driven coupling
42
+ background_power : Quantity, optional
43
+ The background optical power added to the detector signal. Defaults to 0 milliwatts.
44
+
45
+ Attributes
46
+ ----------
47
+ flow_cell : FlowCell
48
+ The flow cell instance representing the system environment.
49
+ scatterer_collection : ScattererCollection
50
+ A collection of particles or scatterers passing through the flow cytometer.
51
+ source : GaussianBeam
52
+ The laser beam source providing illumination to the flow cytometer.
53
+ detectors : List[Detector]
54
+ The detectors used to collect and process signals from the scatterers.
55
+ coupling_mechanism : str
56
+ The selected mechanism for signal coupling.
57
+ background_power : Quantity
58
+ The optical background power added to the detector signals.
59
+
60
+ Raises
61
+ ------
62
+ AssertionError
63
+ If the number of detectors provided is not exactly two, or if both detectors share the same name.
64
+
65
+ """
66
+ def __init__(
67
+ self,
68
+ flow_cell: FlowCell,
69
+ detectors: List[Detector],
70
+ coupling_mechanism: Optional[str] = 'mie',
71
+ background_power: Optional[Quantity] = 0 * milliwatt):
72
+
73
+ self.flow_cell = flow_cell
74
+ self.scatterer_collection = flow_cell.scatterer_collection
75
+ self.source = flow_cell.source
76
+ self.detectors = detectors
77
+ self.coupling_mechanism = coupling_mechanism
78
+ self.background_power = background_power
79
+
80
+ assert len(self.detectors) == 2, 'For now, FlowCytometer can only take two detectors for the analysis.'
81
+ assert self.detectors[0].name != self.detectors[1].name, 'Both detectors cannot have the same name'
82
+
83
+ def run_coupling_analysis(self) -> None:
84
+ """
85
+ Computes and assigns the optical coupling power for each particle-detection event.
86
+
87
+ This method evaluates the coupling between the scatterers in the flow cell and the detectors
88
+ using the specified detection mechanism. The computed coupling power is stored in the
89
+ `scatterer_collection` dataframe under detector-specific columns.
90
+
91
+ Updates
92
+ -------
93
+ scatterer_collection.dataframe : pandas.DataFrame
94
+ Adds columns for each detector, labeled as "detector: <detector_name>", containing the computed
95
+ coupling power for all particle events.
96
+
97
+ Raises
98
+ ------
99
+ ValueError
100
+ If an invalid coupling mechanism is specified during initialization.
101
+ """
102
+ detection_mechanism = self._get_detection_mechanism()
103
+
104
+ for detector in self.detectors:
105
+ self.coupling_power = detection_mechanism(
106
+ source=self.source,
107
+ detector=detector,
108
+ scatterer=self.scatterer_collection
109
+ )
110
+
111
+ self.scatterer_collection.dataframe["detector: " + detector.name] = pint_pandas.PintArray(self.coupling_power, dtype=self.coupling_power.units)
112
+
113
+ self._generate_pulse_parameters()
114
+
115
+ def initialize_signal(self) -> None:
116
+ """
117
+ Initializes the raw signal for each detector based on the source and flow cell configuration.
118
+
119
+ This method prepares the detectors for signal capture by associating each detector with the
120
+ light source and generating a time-dependent raw signal placeholder.
121
+
122
+ Effects
123
+ -------
124
+ Each detector's `raw_signal` attribute is initialized with time-dependent values
125
+ based on the flow cell's runtime.
126
+
127
+ """
128
+ # Initialize the detectors
129
+ for detector in self.detectors:
130
+ detector.source = self.source
131
+ detector.init_raw_signal(run_time=self.flow_cell.run_time)
132
+
133
+ def simulate_pulse(self) -> None:
134
+ """
135
+ Simulates the generation of optical signal pulses for each particle event.
136
+
137
+ This method calculates Gaussian signal pulses based on particle positions, coupling power, and
138
+ widths. It adds the generated pulses, background power, and noise components (thermal and dark current)
139
+ to each detector's raw signal.
140
+
141
+ Notes
142
+ -----
143
+ - Adds Gaussian pulses to each detector's `raw_signal`.
144
+ - Includes noise and background power in the simulated signals.
145
+ - Updates detector dataframes with captured signal information.
146
+
147
+ Raises
148
+ ------
149
+ ValueError
150
+ If the scatterer collection lacks required data columns ('Widths', 'Time').
151
+ """
152
+ logging.debug("Starting pulse simulation.")
153
+
154
+ _widths = self.scatterer_collection.dataframe['Widths'].values
155
+ _centers = self.scatterer_collection.dataframe['Time'].values
156
+
157
+ for detector in self.detectors:
158
+ _coupling_power = self.scatterer_collection.dataframe["detector: " + detector.name].values
159
+
160
+ # Generate noise components
161
+ detector._add_thermal_noise_to_raw_signal()
162
+
163
+ detector._add_dark_current_noise_to_raw_signal()
164
+
165
+ # Broadcast the time array to the shape of (number of signals, len(detector.time))
166
+ time_grid = np.expand_dims(detector.dataframe.Time.values.numpy_data, axis=0) * _centers.units
167
+
168
+ centers = np.expand_dims(_centers.numpy_data, axis=1) * _centers.units
169
+ widths = np.expand_dims(_widths.numpy_data, axis=1) * _widths.units
170
+
171
+ # Compute the Gaussian for each height, center, and width using broadcasting
172
+ power_gaussians = _coupling_power[:, np.newaxis] * np.exp(- (time_grid - centers) ** 2 / (2 * widths ** 2))
173
+
174
+ total_power = np.sum(power_gaussians, axis=0) + self.background_power
175
+
176
+ # Sum all the Gaussians and add them to the detector.raw_signal
177
+ detector._add_optical_power_to_raw_signal(optical_power=total_power)
178
+
179
+ detector.capture_signal()
180
+
181
+ self._log_statistics()
182
+
183
+ def _log_statistics(self) -> SimulationLogger:
184
+ """
185
+ Logs and displays key statistics about the simulated events.
186
+
187
+ This includes metrics such as:
188
+ - Total number of events processed.
189
+ - Average time between events.
190
+ - First and last event times.
191
+ - Minimum time intervals between events.
192
+
193
+ Returns
194
+ -------
195
+ SimulationLogger
196
+ An instance of the logger containing all recorded statistics.
197
+
198
+ Effects
199
+ -------
200
+ Outputs formatted tables to the console or log file, depending on the logger's configuration.
201
+ """
202
+ logger = SimulationLogger(cytometer=self)
203
+
204
+ logger.log_statistics(include_totals=True, table_format="fancy_grid")
205
+
206
+ return logger
207
+
208
+ def _get_detection_mechanism(self) -> Callable:
209
+ """
210
+ Retrieves the detection mechanism function for signal coupling based on the selected method.
211
+
212
+ Supported Coupling Mechanisms
213
+ -----------------------------
214
+ - 'mie': Mie scattering.
215
+ - 'rayleigh': Rayleigh scattering.
216
+ - 'uniform': Uniform scattering.
217
+ - 'empirical': Empirical (data-driven) scattering.
218
+
219
+ Returns
220
+ -------
221
+ Callable
222
+ A function that computes the detected signal for scatterer sizes and particle distributions.
223
+
224
+ Raises
225
+ ------
226
+ ValueError
227
+ If an unsupported coupling mechanism is specified.
228
+ """
229
+ from FlowCyPy import coupling_mechanism
230
+
231
+ # Determine which coupling mechanism to use and compute the corresponding factors
232
+ match self.coupling_mechanism.lower():
233
+ case 'rayleigh':
234
+ return coupling_mechanism.rayleigh.compute_detected_signal
235
+ case 'uniform':
236
+ return coupling_mechanism.uniform.compute_detected_signal
237
+ case 'mie':
238
+ return coupling_mechanism.mie.compute_detected_signal
239
+ case 'empirical':
240
+ return coupling_mechanism.empirical.compute_detected_signal
241
+ case _:
242
+ raise ValueError("Invalid coupling mechanism. Choose 'rayleigh' or 'uniform'.")
243
+
244
+ def _generate_pulse_parameters(self) -> None:
245
+ """
246
+ Generates and assigns random Gaussian pulse parameters for each particle event.
247
+
248
+ The generated parameters include:
249
+ - Centers: The time at which each pulse occurs.
250
+ - Widths: The standard deviation (spread) of each pulse in seconds.
251
+
252
+ Effects
253
+ -------
254
+ scatterer_collection.dataframe : pandas.DataFrame
255
+ Adds a 'Widths' column with computed pulse widths for each particle.
256
+ Uses the flow speed and beam waist to calculate pulse widths.
257
+ """
258
+ columns = pd.MultiIndex.from_product(
259
+ [[p.name for p in self.detectors], ['Centers', 'Heights']]
260
+ )
261
+
262
+ self.pulse_dataframe = pd.DataFrame(columns=columns)
263
+
264
+ self.pulse_dataframe['Centers'] = self.scatterer_collection.dataframe['Time']
265
+
266
+ widths = self.source.waist / self.flow_cell.flow_speed * np.ones(self.scatterer_collection.n_events)
267
+
268
+ self.scatterer_collection.dataframe['Widths'] = pint_pandas.PintArray(widths, dtype=widths.units)
269
+
270
+ def plot(self, figure_size: tuple = (10, 6), add_peak_locator: bool = False, show: bool = True) -> None:
271
+ """
272
+ Visualizes the raw signals for all detector channels along with the scatterer distribution.
273
+
274
+ Parameters
275
+ ----------
276
+ figure_size : tuple, optional
277
+ Dimensions of the generated plot (default: (10, 6)).
278
+ add_peak_locator : bool, optional
279
+ If True, adds visual markers for detected signal peaks (default: False).
280
+
281
+ Effects
282
+ -------
283
+ Displays a multi-panel plot showing:
284
+ - Raw signals for each detector channel.
285
+ - Scatterer distribution along the time axis.
286
+ """
287
+ logging.info("Plotting the signal for the different channels.")
288
+
289
+ n_detectors = len(self.detectors)
290
+
291
+ with plt.style.context(mps):
292
+ _, axes = plt.subplots(ncols=1, nrows=n_detectors + 1, figsize=figure_size, sharex=True, sharey=True, gridspec_kw={'height_ratios': [1, 1, 0.4]})
293
+
294
+ time_unit, signal_unit = self.detectors[0].plot(ax=axes[0], show=False, add_peak_locator=add_peak_locator)
295
+ self.detectors[1].plot(ax=axes[1], show=False, time_unit=time_unit, signal_unit=signal_unit, add_peak_locator=add_peak_locator)
296
+
297
+ axes[-1].get_yaxis().set_visible(False)
298
+ self.scatterer_collection.add_to_ax(axes[-1])
299
+
300
+ # Add legends to each subplot
301
+ for ax in axes:
302
+ ax.legend()
303
+
304
+ if show: # Display the plot
305
+ plt.show()
306
+
307
+ def plot_coupling_density(self, log_scale: bool = False, show: bool = True) -> None:
308
+ """
309
+ Plots the density distribution of optical coupling in the FSC and SSC channels.
310
+
311
+ This method generates a joint plot showing the relationship between the signals from
312
+ the forward scatter ('detector: forward') and side scatter ('detector: side') detectors.
313
+ The plot is color-coded by particle population and can optionally display axes on a logarithmic scale.
314
+
315
+ Parameters
316
+ ----------
317
+ log_scale : bool, optional
318
+ If True, applies a logarithmic scale to both the x and y axes of the plot (default: False).
319
+ show : bool, optional
320
+ If True, displays the plot immediately. If False, the plot is created but not displayed,
321
+ allowing for further customization or saving externally (default: True).
322
+
323
+ """
324
+ with plt.style.context(mps):
325
+ joint_plot = sns.jointplot(
326
+ data=self.scatterer_collection.dataframe,
327
+ y='detector: side',
328
+ x='detector: forward',
329
+ hue="Population",
330
+ alpha=0.8,
331
+ )
332
+
333
+
334
+ if log_scale:
335
+ joint_plot.ax_joint.set_xscale('log')
336
+ joint_plot.ax_joint.set_yscale('log')
337
+
338
+ if show: # Display the plot
339
+ plt.show()
340
+
341
+ def add_detector(self, **kwargs) -> Detector:
342
+ """
343
+ Dynamically adds a new detector to the system configuration.
344
+
345
+ Parameters
346
+ ----------
347
+ **kwargs : dict
348
+ Keyword arguments passed to the `Detector` constructor.
349
+
350
+ Returns
351
+ -------
352
+ Detector
353
+ The newly added detector instance.
354
+
355
+ Effects
356
+ -------
357
+ - Appends the created detector to the `detectors` list.
358
+ """
359
+ detector = Detector(**kwargs)
360
+
361
+ self.detectors.append(detector)
362
+
363
+ return detector
@@ -8,7 +8,6 @@ from FlowCyPy.units import second
8
8
  import warnings
9
9
  from FlowCyPy.cytometer import FlowCytometer
10
10
  from FlowCyPy.logger import EventCorrelatorLogger
11
- from FlowCyPy.report import Report
12
11
 
13
12
 
14
13
  class EventCorrelator:
@@ -224,21 +223,3 @@ class EventCorrelator:
224
223
 
225
224
  if show:
226
225
  plt.show()
227
-
228
- def generate_report(self, filename: str) -> None:
229
- """
230
- Generates a detailed report summarizing the analysis, including peak features
231
- and detected events.
232
-
233
- Parameters
234
- ----------
235
- filename : str
236
- The filename where the report will be saved.
237
- """
238
- report = Report(
239
- flow_cell=self.cytometer.scatterer.flow_cell,
240
- scatterer=self.cytometer.scatterer,
241
- analyzer=self
242
- )
243
-
244
- report.generate_report()