FlowCyPy 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- FlowCyPy/__init__.py +15 -0
- FlowCyPy/_version.py +16 -0
- FlowCyPy/classifier.py +196 -0
- FlowCyPy/coupling_mechanism/__init__.py +4 -0
- FlowCyPy/coupling_mechanism/empirical.py +47 -0
- FlowCyPy/coupling_mechanism/mie.py +205 -0
- FlowCyPy/coupling_mechanism/rayleigh.py +115 -0
- FlowCyPy/coupling_mechanism/uniform.py +39 -0
- FlowCyPy/cytometer.py +198 -0
- FlowCyPy/detector.py +616 -0
- FlowCyPy/directories.py +36 -0
- FlowCyPy/distribution/__init__.py +16 -0
- FlowCyPy/distribution/base_class.py +59 -0
- FlowCyPy/distribution/delta.py +86 -0
- FlowCyPy/distribution/lognormal.py +94 -0
- FlowCyPy/distribution/normal.py +95 -0
- FlowCyPy/distribution/particle_size_distribution.py +110 -0
- FlowCyPy/distribution/uniform.py +96 -0
- FlowCyPy/distribution/weibull.py +80 -0
- FlowCyPy/event_correlator.py +244 -0
- FlowCyPy/flow_cell.py +122 -0
- FlowCyPy/helper.py +85 -0
- FlowCyPy/logger.py +322 -0
- FlowCyPy/noises.py +29 -0
- FlowCyPy/particle_count.py +102 -0
- FlowCyPy/peak_locator/__init__.py +4 -0
- FlowCyPy/peak_locator/base_class.py +163 -0
- FlowCyPy/peak_locator/basic.py +108 -0
- FlowCyPy/peak_locator/derivative.py +143 -0
- FlowCyPy/peak_locator/moving_average.py +114 -0
- FlowCyPy/physical_constant.py +19 -0
- FlowCyPy/plottings.py +270 -0
- FlowCyPy/population.py +239 -0
- FlowCyPy/populations_instances.py +49 -0
- FlowCyPy/report.py +236 -0
- FlowCyPy/scatterer.py +373 -0
- FlowCyPy/source.py +249 -0
- FlowCyPy/units.py +26 -0
- FlowCyPy/utils.py +191 -0
- FlowCyPy-0.5.0.dist-info/LICENSE +21 -0
- FlowCyPy-0.5.0.dist-info/METADATA +252 -0
- FlowCyPy-0.5.0.dist-info/RECORD +44 -0
- FlowCyPy-0.5.0.dist-info/WHEEL +5 -0
- FlowCyPy-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import Optional, Tuple
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from MPSPlots.styles import mps
|
|
5
|
+
from FlowCyPy.units import particle, Quantity
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Base:
|
|
9
|
+
"""
|
|
10
|
+
Base class for distributions used to define particle sizes in the flow cytometer.
|
|
11
|
+
|
|
12
|
+
This class provides a structure for generating random scatterer sizes based on different statistical distributions.
|
|
13
|
+
Each subclass must implement the `generate` method to generate a distribution of sizes and `get_pdf` to compute the
|
|
14
|
+
probability density function (PDF) values.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
scale_factor : float
|
|
19
|
+
A scaling factor applied to the PDF of the distribution. By default, it is set to 1 (equal weight).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
scale_factor: Optional[float] = 1.0
|
|
23
|
+
|
|
24
|
+
def generate(self, n_samples: int) -> np.ndarray:
|
|
25
|
+
"""Generate a distribution of scatterer sizes."""
|
|
26
|
+
raise NotImplementedError("Must be implemented by subclasses")
|
|
27
|
+
|
|
28
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
29
|
+
"""Compute the probability density function (PDF) values."""
|
|
30
|
+
raise NotImplementedError("Must be implemented by subclasses")
|
|
31
|
+
|
|
32
|
+
def plot(self, n_samples: int = 4000, bins: int = 50) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Plots a histogram of the generated particle sizes based on the log-normal distribution.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
n_samples : int, optional
|
|
39
|
+
The number of particle sizes to generate for the histogram (default is 1000).
|
|
40
|
+
bins : int, optional
|
|
41
|
+
The number of bins in the histogram (default is 50).
|
|
42
|
+
"""
|
|
43
|
+
samples = self.generate(Quantity(n_samples, particle))
|
|
44
|
+
|
|
45
|
+
# Plotting the PDF
|
|
46
|
+
with plt.style.context(mps): # Assuming mps is a custom style
|
|
47
|
+
figure, ax = plt.subplots(1, 1)
|
|
48
|
+
ax.hist(samples, bins=bins, color='blue', edgecolor='black', alpha=0.7)
|
|
49
|
+
|
|
50
|
+
ax.set(
|
|
51
|
+
title='Distribution',
|
|
52
|
+
xlabel=f'Distributed parameter [{self._main_units}]',
|
|
53
|
+
ylabel='Probability Density Function (PDF)'
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
plt.show()
|
|
57
|
+
|
|
58
|
+
def __str__(self) -> str:
|
|
59
|
+
return self.__repr__()
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
from FlowCyPy.distribution.base_class import Base
|
|
3
|
+
import numpy as np
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
from PyMieSim.units import Quantity
|
|
6
|
+
from pydantic.dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
config_dict = dict(
|
|
9
|
+
arbitrary_types_allowed=True,
|
|
10
|
+
kw_only=True,
|
|
11
|
+
slots=True,
|
|
12
|
+
extra='forbid'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(config=config_dict)
|
|
17
|
+
class Delta(Base):
|
|
18
|
+
r"""
|
|
19
|
+
Represents a delta-like distribution for particle sizes.
|
|
20
|
+
|
|
21
|
+
In a delta distribution, all particle sizes are the same, representing a delta function:
|
|
22
|
+
|
|
23
|
+
.. math::
|
|
24
|
+
f(x) = \delta(x - x_0)
|
|
25
|
+
|
|
26
|
+
where:
|
|
27
|
+
- :math:`x_0` is the singular particle size.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
position : Quantity
|
|
32
|
+
The particle size for the delta distribution in meters.
|
|
33
|
+
scale_factor : float, optional
|
|
34
|
+
A scaling factor applied to the PDF (not the sizes).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
position: Quantity
|
|
38
|
+
_name = 'Delta'
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
self._main_units = self.position.units
|
|
42
|
+
|
|
43
|
+
def generate(self, n_samples: int) -> np.ndarray:
|
|
44
|
+
r"""
|
|
45
|
+
Generates a singular distribution of scatterer sizes.
|
|
46
|
+
|
|
47
|
+
All sizes generated will be exactly the same as `position`.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
n_samples : int
|
|
52
|
+
The number of particle sizes to generate.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
np.ndarray
|
|
57
|
+
An array of identical scatterer sizes in meters.
|
|
58
|
+
"""
|
|
59
|
+
return np.ones(n_samples.magnitude) * self.position.magnitude * self._main_units
|
|
60
|
+
|
|
61
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
62
|
+
r"""
|
|
63
|
+
Returns the x-values and the scaled PDF values for the singular distribution.
|
|
64
|
+
|
|
65
|
+
The PDF is represented as a delta-like function centered at `position`.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
x : np.ndarray
|
|
70
|
+
The input x-values (particle sizes) over which to compute the PDF.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
Tuple[np.ndarray, np.ndarray]
|
|
75
|
+
The input x-values and the corresponding scaled PDF values.
|
|
76
|
+
"""
|
|
77
|
+
common_units = x.units
|
|
78
|
+
|
|
79
|
+
pdf = np.zeros_like(x)
|
|
80
|
+
|
|
81
|
+
idx = (np.abs(x.magnitude - self.position.to(common_units).magnitude)).argmin() # Delta-like function for singular value
|
|
82
|
+
pdf[idx] = 1.0
|
|
83
|
+
return x, pdf
|
|
84
|
+
|
|
85
|
+
def __repr__(self) -> str:
|
|
86
|
+
return f"Delta({self.position:.3f~P})"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from scipy.stats import lognorm
|
|
5
|
+
from PyMieSim.units import Quantity
|
|
6
|
+
from pydantic.dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
config_dict = dict(
|
|
9
|
+
arbitrary_types_allowed=True,
|
|
10
|
+
kw_only=True,
|
|
11
|
+
slots=True,
|
|
12
|
+
extra='forbid'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(config=config_dict)
|
|
17
|
+
class LogNormal(Base):
|
|
18
|
+
r"""
|
|
19
|
+
Represents a log-normal distribution for particle sizes.
|
|
20
|
+
|
|
21
|
+
The log-normal distribution is described by its mean and standard deviation of the logarithm of the values:
|
|
22
|
+
|
|
23
|
+
.. math::
|
|
24
|
+
f(x) = \frac{1}{x \sigma \sqrt{2 \pi}} \exp \left( - \frac{(\ln(x) - \mu)^2}{2 \sigma^2} \right)
|
|
25
|
+
|
|
26
|
+
where:
|
|
27
|
+
- :math:`\mu` is the mean of the natural logarithm of the particle sizes.
|
|
28
|
+
- :math:`\sigma` is the standard deviation of the logarithm of particle sizes.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
mean : Quantity
|
|
33
|
+
The mean particle size in meters.
|
|
34
|
+
std_dev : Quantity
|
|
35
|
+
The standard deviation of the logarithm of particle sizes.
|
|
36
|
+
scale_factor : float, optional
|
|
37
|
+
A scaling factor applied to the PDF (not the sizes).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
mean: Quantity
|
|
41
|
+
std_dev: Quantity
|
|
42
|
+
_name = 'Log-normal'
|
|
43
|
+
|
|
44
|
+
def __post_init__(self):
|
|
45
|
+
self._main_units = self.mean.units
|
|
46
|
+
|
|
47
|
+
def generate(self, n_samples: int) -> Quantity:
|
|
48
|
+
"""
|
|
49
|
+
Generates a log-normal distribution of scatterer sizes.
|
|
50
|
+
|
|
51
|
+
The generated sizes follow a log-normal distribution, where the logarithm of the sizes is normally distributed.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
n_samples : Quantity
|
|
56
|
+
The number of particle sizes to generate.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
Quantity
|
|
61
|
+
An array of scatterer sizes in meters.
|
|
62
|
+
"""
|
|
63
|
+
return np.random.lognormal(
|
|
64
|
+
mean=self.mean.to(self._main_units).magnitude,
|
|
65
|
+
sigma=self.std_dev.to(self._main_units).magnitude,
|
|
66
|
+
size=n_samples.magnitude
|
|
67
|
+
) * self._main_units
|
|
68
|
+
|
|
69
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
70
|
+
"""
|
|
71
|
+
Returns the x-values and the scaled PDF values for the log-normal distribution.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
x : np.ndarray
|
|
76
|
+
The input x-values (particle sizes) over which to compute the PDF.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
Tuple[np.ndarray, np.ndarray]
|
|
81
|
+
The input x-values and the corresponding scaled PDF values.
|
|
82
|
+
"""
|
|
83
|
+
common_units = x.units
|
|
84
|
+
|
|
85
|
+
pdf = lognorm.pdf(
|
|
86
|
+
x.to(common_units).magnitude,
|
|
87
|
+
s=self.std_dev.to(common_units).magnitude,
|
|
88
|
+
scale=self.mean.to(common_units).magnitude
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
return x, pdf
|
|
92
|
+
|
|
93
|
+
def __repr__(self) -> str:
|
|
94
|
+
return f"Log-Normal({self.mean:.3f~P}, {self.std_dev:.3f~P})"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from scipy.stats import norm
|
|
5
|
+
from PyMieSim.units import Quantity
|
|
6
|
+
from pydantic.dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
config_dict = dict(
|
|
9
|
+
arbitrary_types_allowed=True,
|
|
10
|
+
kw_only=True,
|
|
11
|
+
slots=True,
|
|
12
|
+
extra='forbid'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(config=config_dict)
|
|
17
|
+
class Normal(Base):
|
|
18
|
+
r"""
|
|
19
|
+
Represents a normal (Gaussian) distribution for particle sizes.
|
|
20
|
+
|
|
21
|
+
The normal distribution is described by its mean and standard deviation:
|
|
22
|
+
|
|
23
|
+
.. math::
|
|
24
|
+
f(x) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp \left( - \frac{(x - \mu)^2}{2 \sigma^2} \right)
|
|
25
|
+
|
|
26
|
+
where:
|
|
27
|
+
- :math:`\mu` is the mean of the distribution (average particle size).
|
|
28
|
+
- :math:`\sigma` is the standard deviation (width of the distribution).
|
|
29
|
+
- :math:`x` represents particle sizes.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
mean : Quantity
|
|
34
|
+
The mean (average) particle size in meters.
|
|
35
|
+
std_dev : Quantity
|
|
36
|
+
The standard deviation of particle sizes in meters.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
mean: Quantity
|
|
40
|
+
std_dev: Quantity
|
|
41
|
+
_name = 'Normal'
|
|
42
|
+
|
|
43
|
+
def __post_init__(self):
|
|
44
|
+
self._main_units = self.mean.units
|
|
45
|
+
|
|
46
|
+
def generate(self, n_samples: int) -> np.ndarray:
|
|
47
|
+
"""
|
|
48
|
+
Generates a normal distribution of scatterer sizes.
|
|
49
|
+
|
|
50
|
+
The generated sizes are based on the normal distribution's mean and standard deviation.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
n_samples : int
|
|
55
|
+
The number of particle sizes to generate.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
np.ndarray
|
|
60
|
+
An array of scatterer sizes in meters.
|
|
61
|
+
"""
|
|
62
|
+
return np.random.normal(
|
|
63
|
+
loc=self.mean.to(self._main_units).magnitude,
|
|
64
|
+
scale=self.std_dev.to(self._main_units).magnitude,
|
|
65
|
+
size=int(n_samples.magnitude)
|
|
66
|
+
) * self._main_units
|
|
67
|
+
|
|
68
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
69
|
+
"""
|
|
70
|
+
Returns the x-values and the scaled PDF values for the normal distribution.
|
|
71
|
+
|
|
72
|
+
The `scale_factor` is applied to the PDF, not the generated sizes.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
x : np.ndarray
|
|
77
|
+
The input x-values (particle sizes) over which to compute the PDF.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
Tuple[np.ndarray, np.ndarray]
|
|
82
|
+
The input x-values and the corresponding scaled PDF values.
|
|
83
|
+
"""
|
|
84
|
+
common_units = x.units
|
|
85
|
+
|
|
86
|
+
pdf = norm.pdf(
|
|
87
|
+
x.magnitude,
|
|
88
|
+
loc=self.mean.to(common_units).magnitude,
|
|
89
|
+
scale=self.std_dev.to(common_units).magnitude
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return x, pdf
|
|
93
|
+
|
|
94
|
+
def __repr__(self) -> str:
|
|
95
|
+
return f"Normal({self.mean:.3f~P}, {self.std_dev:.3f~P})"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from PyMieSim.units import Quantity
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
config_dict = dict(
|
|
8
|
+
arbitrary_types_allowed=True,
|
|
9
|
+
kw_only=True,
|
|
10
|
+
slots=True,
|
|
11
|
+
extra='forbid'
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(config=config_dict)
|
|
16
|
+
class RosinRammler(Base):
|
|
17
|
+
r"""
|
|
18
|
+
Represents a Particle Size Distribution using the Rosin-Rammler model.
|
|
19
|
+
|
|
20
|
+
The Rosin-Rammler distribution is described by its characteristic size and
|
|
21
|
+
spread parameter, and is used to model particle sizes in systems such as
|
|
22
|
+
powders or granular materials.
|
|
23
|
+
|
|
24
|
+
The distribution function is given by:
|
|
25
|
+
|
|
26
|
+
.. math::
|
|
27
|
+
F(x) = 1 - \exp \left( - \left( \frac{x}{d} \right)^k \right)
|
|
28
|
+
|
|
29
|
+
where:
|
|
30
|
+
- :math:`x` is the particle size.
|
|
31
|
+
- :math:`d` is the characteristic particle size.
|
|
32
|
+
- :math:`k` is the spread parameter.
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
characteristic_size : Quantity
|
|
37
|
+
The characteristic particle size in meters.
|
|
38
|
+
spread : float
|
|
39
|
+
The spread parameter (shape factor).
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
characteristic_size: Quantity
|
|
43
|
+
spread: float
|
|
44
|
+
_name = 'Rosin-Rammler'
|
|
45
|
+
|
|
46
|
+
def __post_init__(self):
|
|
47
|
+
self._main_units = self.characteristic_size.units
|
|
48
|
+
|
|
49
|
+
def generate(self, n_samples: Quantity) -> Quantity:
|
|
50
|
+
"""
|
|
51
|
+
Generates a particle size distribution based on the Rosin-Rammler model.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
n_samples : Quantity
|
|
56
|
+
The number of particle sizes to generate (dimensionless).
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
Quantity
|
|
61
|
+
An array of particle sizes in meters (or other units).
|
|
62
|
+
"""
|
|
63
|
+
# Validate inputs
|
|
64
|
+
if not isinstance(n_samples, Quantity) or not n_samples.check("particle"):
|
|
65
|
+
raise ValueError("n_samples must be a dimensionless Quantity.")
|
|
66
|
+
if self.spread <= 0:
|
|
67
|
+
raise ValueError("Spread parameter must be greater than zero.")
|
|
68
|
+
|
|
69
|
+
# Convert characteristic size to main units
|
|
70
|
+
d = self.characteristic_size.to(self._main_units).magnitude
|
|
71
|
+
|
|
72
|
+
# Generate uniform random samples in [0, 1)
|
|
73
|
+
u = np.random.uniform(size=n_samples.magnitude)
|
|
74
|
+
u = np.clip(u, 1e-10, 1 - 1e-10) # Avoid numerical issues
|
|
75
|
+
|
|
76
|
+
# Apply inverse CDF of Rosin-Rammler distribution
|
|
77
|
+
sizes = d * (-np.log(1 - u))**(1 / self.spread)
|
|
78
|
+
|
|
79
|
+
return sizes * self._main_units
|
|
80
|
+
|
|
81
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
82
|
+
r"""
|
|
83
|
+
Returns the x-values and the scaled PDF values for the particle size distribution.
|
|
84
|
+
|
|
85
|
+
The PDF for the Rosin-Rammler distribution is derived from the CDF:
|
|
86
|
+
|
|
87
|
+
.. math::
|
|
88
|
+
f(x) = \frac{k}{d} \left( \frac{x}{d} \right)^{k-1} \exp \left( - \left( \frac{x}{d} \right)^k \right)
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
x : np.ndarray
|
|
93
|
+
The input x-values (particle sizes) over which to compute the PDF.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
Tuple[np.ndarray, np.ndarray]
|
|
98
|
+
The input x-values and the corresponding scaled PDF values.
|
|
99
|
+
"""
|
|
100
|
+
common_units = x.units
|
|
101
|
+
d = self.characteristic_size.to(common_units).magnitude
|
|
102
|
+
k = self.spread
|
|
103
|
+
|
|
104
|
+
# Rosin-Rammler PDF formula
|
|
105
|
+
pdf = (k / d) * (x.magnitude / d)**(k - 1) * np.exp(-(x.magnitude / d)**k)
|
|
106
|
+
|
|
107
|
+
return x, pdf
|
|
108
|
+
|
|
109
|
+
def __repr__(self) -> str:
|
|
110
|
+
return f"RR({self.characteristic_size:.3f~P}, {self.spread:.3f})"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from scipy.stats import uniform
|
|
5
|
+
from PyMieSim.units import Quantity
|
|
6
|
+
from pydantic.dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
config_dict = dict(
|
|
9
|
+
arbitrary_types_allowed=True,
|
|
10
|
+
kw_only=True,
|
|
11
|
+
slots=True,
|
|
12
|
+
extra='forbid'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(config=config_dict)
|
|
17
|
+
class Uniform(Base):
|
|
18
|
+
r"""
|
|
19
|
+
Represents a uniform distribution for particle sizes.
|
|
20
|
+
|
|
21
|
+
The uniform distribution assigns equal probability to all particle sizes within a specified range:
|
|
22
|
+
|
|
23
|
+
.. math::
|
|
24
|
+
f(x) = \frac{1}{b - a} \quad \text{for} \quad a \leq x \leq b
|
|
25
|
+
|
|
26
|
+
where:
|
|
27
|
+
- :math:`a` is the lower bound of the distribution.
|
|
28
|
+
- :math:`b` is the upper bound of the distribution.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
lower_bound : float
|
|
33
|
+
The lower bound for particle sizes in meters.
|
|
34
|
+
upper_bound : float
|
|
35
|
+
The upper bound for particle sizes in meters.
|
|
36
|
+
scale_factor : float, optional
|
|
37
|
+
A scaling factor applied to the PDF (not the sizes).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
lower_bound: Quantity
|
|
41
|
+
upper_bound: Quantity
|
|
42
|
+
_name = 'Uniform'
|
|
43
|
+
|
|
44
|
+
def __post_init__(self):
|
|
45
|
+
self._main_units = self.lower_bound.units
|
|
46
|
+
|
|
47
|
+
def generate(self, n_samples: Quantity) -> Quantity:
|
|
48
|
+
"""
|
|
49
|
+
Generates a uniform distribution of scatterer sizes.
|
|
50
|
+
|
|
51
|
+
The generated sizes are uniformly distributed between the specified `lower_bound` and `upper_bound`.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
n_samples : Quantity
|
|
56
|
+
The number of particle sizes to generate.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
np.ndarray
|
|
61
|
+
An array of scatterer sizes in meters.
|
|
62
|
+
"""
|
|
63
|
+
return np.random.uniform(
|
|
64
|
+
self.lower_bound.to(self._main_units).magnitude,
|
|
65
|
+
self.upper_bound.to(self._main_units).magnitude,
|
|
66
|
+
n_samples.magnitude
|
|
67
|
+
) * self._main_units
|
|
68
|
+
|
|
69
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
70
|
+
"""
|
|
71
|
+
Returns the x-values and the scaled PDF values for the uniform distribution.
|
|
72
|
+
|
|
73
|
+
The `scale_factor` is applied to the PDF, not the generated sizes.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
x : np.ndarray
|
|
78
|
+
The input x-values (particle sizes) over which to compute the PDF.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
Tuple[np.ndarray, np.ndarray]
|
|
83
|
+
The input x-values and the corresponding scaled PDF values.
|
|
84
|
+
"""
|
|
85
|
+
common_unit = self.lower_bound.units
|
|
86
|
+
|
|
87
|
+
pdf = uniform.pdf(
|
|
88
|
+
x.to(common_unit).magnitude,
|
|
89
|
+
loc=self.lower_bound.magnitude,
|
|
90
|
+
scale=self.upper_bound.to(common_unit).magnitude - self.lower_bound.magnitude
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return x, pdf
|
|
94
|
+
|
|
95
|
+
def __repr__(self) -> str:
|
|
96
|
+
return f"Uniform({self.lower_bound:.3f~P}, {self.upper_bound:.3f~P})"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
from PyMieSim.units import Quantity
|
|
4
|
+
from FlowCyPy.distribution.base_class import Base
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
config_dict = dict(
|
|
9
|
+
arbitrary_types_allowed=True,
|
|
10
|
+
kw_only=True,
|
|
11
|
+
slots=True,
|
|
12
|
+
extra='forbid'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(config=config_dict)
|
|
17
|
+
class Weibull(Base):
|
|
18
|
+
r"""
|
|
19
|
+
Represents a Weibull distribution for particle sizes.
|
|
20
|
+
|
|
21
|
+
The Weibull distribution is commonly used for modeling size distributions in biological systems.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
shape : Quantity
|
|
26
|
+
The shape parameter (k), controls the skewness of the distribution.
|
|
27
|
+
scale : Quantity
|
|
28
|
+
The scale parameter (λ), controls the spread of the distribution.
|
|
29
|
+
scale_factor : float, optional
|
|
30
|
+
A scaling factor applied to the PDF (not the sizes).
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
shape: Quantity
|
|
34
|
+
scale: Quantity
|
|
35
|
+
_name = 'Weibull'
|
|
36
|
+
|
|
37
|
+
def __post_init__(self):
|
|
38
|
+
self._main_units = self.shape.units
|
|
39
|
+
|
|
40
|
+
def generate(self, n_samples: int) -> np.ndarray:
|
|
41
|
+
"""
|
|
42
|
+
Generates a Weibull distribution of scatterer sizes.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
n_samples : int
|
|
47
|
+
The number of particle sizes to generate.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
np.ndarray
|
|
52
|
+
An array of particle sizes in meters.
|
|
53
|
+
"""
|
|
54
|
+
common_unit = self.shape.units
|
|
55
|
+
|
|
56
|
+
return np.random.weibull(
|
|
57
|
+
self.shape.to(self._main_units).magnitude,
|
|
58
|
+
size=n_samples.magnitude
|
|
59
|
+
) * common_unit
|
|
60
|
+
|
|
61
|
+
def get_pdf(self, x: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
|
|
62
|
+
"""
|
|
63
|
+
Returns the x-values and the PDF values for the Weibull distribution.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
x : np.ndarray
|
|
68
|
+
The input x-values (particle sizes) over which to compute the PDF.
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
Tuple[np.ndarray, np.ndarray]
|
|
73
|
+
The input x-values and the corresponding PDF values.
|
|
74
|
+
"""
|
|
75
|
+
a = self.shape / self.scale
|
|
76
|
+
b = (x / self.scale)
|
|
77
|
+
c = np.exp(-(x / self.scale) ** self.shape)
|
|
78
|
+
pdf = a * b ** (self.shape - 1) * c
|
|
79
|
+
|
|
80
|
+
return x, self.scale_factor * pdf
|