FlowCyPy 0.7.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 +13 -0
- FlowCyPy/_version.py +16 -0
- FlowCyPy/acquisition.py +652 -0
- FlowCyPy/classifier.py +208 -0
- FlowCyPy/coupling_mechanism/__init__.py +4 -0
- FlowCyPy/coupling_mechanism/empirical.py +47 -0
- FlowCyPy/coupling_mechanism/mie.py +207 -0
- FlowCyPy/coupling_mechanism/rayleigh.py +116 -0
- FlowCyPy/coupling_mechanism/uniform.py +40 -0
- FlowCyPy/coupling_mechanism.py +205 -0
- FlowCyPy/cytometer.py +314 -0
- FlowCyPy/detector.py +439 -0
- FlowCyPy/directories.py +36 -0
- FlowCyPy/distribution/__init__.py +16 -0
- FlowCyPy/distribution/base_class.py +79 -0
- FlowCyPy/distribution/delta.py +104 -0
- FlowCyPy/distribution/lognormal.py +124 -0
- FlowCyPy/distribution/normal.py +128 -0
- FlowCyPy/distribution/particle_size_distribution.py +132 -0
- FlowCyPy/distribution/uniform.py +117 -0
- FlowCyPy/distribution/weibull.py +115 -0
- FlowCyPy/flow_cell.py +198 -0
- FlowCyPy/helper.py +81 -0
- FlowCyPy/logger.py +136 -0
- FlowCyPy/noises.py +34 -0
- FlowCyPy/particle_count.py +127 -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 +166 -0
- FlowCyPy/physical_constant.py +19 -0
- FlowCyPy/plottings.py +269 -0
- FlowCyPy/population.py +136 -0
- FlowCyPy/populations_instances.py +65 -0
- FlowCyPy/scatterer_collection.py +306 -0
- FlowCyPy/signal_digitizer.py +90 -0
- FlowCyPy/source.py +249 -0
- FlowCyPy/units.py +30 -0
- FlowCyPy/utils.py +191 -0
- FlowCyPy-0.7.0.dist-info/LICENSE +21 -0
- FlowCyPy-0.7.0.dist-info/METADATA +252 -0
- FlowCyPy-0.7.0.dist-info/RECORD +45 -0
- FlowCyPy-0.7.0.dist-info/WHEEL +5 -0
- FlowCyPy-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base, config_dict
|
|
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
|
+
|
|
9
|
+
@dataclass(config=config_dict)
|
|
10
|
+
class LogNormal(Base):
|
|
11
|
+
r"""
|
|
12
|
+
Represents a log-normal distribution for particle sizes.
|
|
13
|
+
|
|
14
|
+
The log-normal distribution is described by its mean and standard deviation of the logarithm of the values:
|
|
15
|
+
|
|
16
|
+
.. math::
|
|
17
|
+
f(x) = \frac{1}{x \sigma \sqrt{2 \pi}} \exp \left( - \frac{(\ln(x) - \mu)^2}{2 \sigma^2} \right)
|
|
18
|
+
|
|
19
|
+
where:
|
|
20
|
+
- :math:`\mu` is the mean of the natural logarithm of the particle sizes.
|
|
21
|
+
- :math:`\sigma` is the standard deviation of the logarithm of particle sizes.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
mean : Quantity
|
|
26
|
+
The mean particle size in meters.
|
|
27
|
+
std_dev : Quantity
|
|
28
|
+
The standard deviation of the logarithm of particle sizes.
|
|
29
|
+
scale_factor : float, optional
|
|
30
|
+
A scaling factor applied to the PDF (not the sizes).
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
mean: Quantity
|
|
34
|
+
std_dev: Quantity
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def _units(self) -> Quantity:
|
|
38
|
+
return self.mean.units
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def _mean(self) -> Quantity:
|
|
42
|
+
return self.mean.to(self._units)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def _std_dev(self) -> Quantity:
|
|
46
|
+
return self.std_dev.to(self._units)
|
|
47
|
+
|
|
48
|
+
@Base.pre_generate
|
|
49
|
+
def generate(self, n_samples: int) -> Quantity:
|
|
50
|
+
"""
|
|
51
|
+
Generates a log-normal distribution of scatterer sizes.
|
|
52
|
+
|
|
53
|
+
The generated sizes follow a log-normal distribution, where the logarithm of the sizes is normally distributed.
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
n_samples : Quantity
|
|
58
|
+
The number of particle sizes to generate.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
Quantity
|
|
63
|
+
An array of scatterer sizes in meters.
|
|
64
|
+
"""
|
|
65
|
+
return np.random.lognormal(
|
|
66
|
+
mean=self._mean.magnitude,
|
|
67
|
+
sigma=self._std_dev.magnitude,
|
|
68
|
+
size=n_samples
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def _generate_default_x(self, x_min: float = 0.01, x_max: float = 5, n_samples: int = 40) -> np.ndarray:
|
|
72
|
+
"""
|
|
73
|
+
Generates a range of x-values based on the log-normal distribution parameters.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
x_min : float, optional
|
|
78
|
+
Factor for the minimum x-value as a multiple of the mean. Default is 0.01.
|
|
79
|
+
x_max : float, optional
|
|
80
|
+
Factor for the maximum x-value as a multiple of the mean. Default is 5.
|
|
81
|
+
n_samples : int, optional
|
|
82
|
+
Number of points in the generated range. Default is 500.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
np.ndarray
|
|
87
|
+
A range of x-values with appropriate units.
|
|
88
|
+
"""
|
|
89
|
+
if x_min <= 0:
|
|
90
|
+
raise ValueError("x_min must be greater than 0.")
|
|
91
|
+
if x_min >= x_max:
|
|
92
|
+
raise ValueError("x_min must be less than x_max.")
|
|
93
|
+
|
|
94
|
+
scale = self.mean.magnitude
|
|
95
|
+
x_min_value = x_min * scale
|
|
96
|
+
x_max_value = x_max * scale
|
|
97
|
+
return np.linspace(x_min_value, x_max_value, n_samples) * self._units
|
|
98
|
+
|
|
99
|
+
def get_pdf(self, x_min: float = 0.9, x_max: float = 1.1, n_samples: int = 40) -> Tuple[np.ndarray, np.ndarray]:
|
|
100
|
+
"""
|
|
101
|
+
Returns the x-values and the PDF values for the log-normal distribution.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
x_min : float, optional
|
|
106
|
+
Factor for the minimum x-value as a multiple of the mean. Default is 0.01.
|
|
107
|
+
x_max : float, optional
|
|
108
|
+
Factor for the maximum x-value as a multiple of the mean. Default is 5.
|
|
109
|
+
n_samples : int, optional
|
|
110
|
+
Number of points in the generated range. Default is 500.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
Tuple[np.ndarray, np.ndarray]
|
|
115
|
+
The input x-values and the corresponding PDF values.
|
|
116
|
+
"""
|
|
117
|
+
x = self._generate_default_x(x_min=x_min, x_max=x_max, n_samples=n_samples)
|
|
118
|
+
|
|
119
|
+
pdf = lognorm.pdf(x.magnitude, s=self._std_dev, scale=self._mean.magnitude)
|
|
120
|
+
|
|
121
|
+
return x, pdf
|
|
122
|
+
|
|
123
|
+
def __repr__(self) -> str:
|
|
124
|
+
return f"Log-Normal({self.mean:.3f~P}, {self.std_dev:.3f~P})"
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base, config_dict
|
|
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
|
+
|
|
9
|
+
@dataclass(config=config_dict)
|
|
10
|
+
class Normal(Base):
|
|
11
|
+
r"""
|
|
12
|
+
Represents a normal (Gaussian) distribution for particle sizes.
|
|
13
|
+
|
|
14
|
+
The normal distribution is described by its mean and standard deviation:
|
|
15
|
+
|
|
16
|
+
.. math::
|
|
17
|
+
f(x) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp \left( - \frac{(x - \mu)^2}{2 \sigma^2} \right)
|
|
18
|
+
|
|
19
|
+
where:
|
|
20
|
+
- :math:`\mu` is the mean of the distribution (average particle size).
|
|
21
|
+
- :math:`\sigma` is the standard deviation (width of the distribution).
|
|
22
|
+
- :math:`x` represents particle sizes.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
mean : Quantity
|
|
27
|
+
The mean (average) particle size in meters.
|
|
28
|
+
std_dev : Quantity
|
|
29
|
+
The standard deviation of particle sizes in meters.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
mean: Quantity
|
|
33
|
+
std_dev: Quantity
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def _units(self) -> Quantity:
|
|
37
|
+
return self.mean.units
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def _mean(self) -> Quantity:
|
|
41
|
+
return self.mean.to(self._units)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def _std_dev(self) -> Quantity:
|
|
45
|
+
return self.std_dev.to(self._units)
|
|
46
|
+
|
|
47
|
+
@Base.pre_generate
|
|
48
|
+
def generate(self, n_samples: int) -> np.ndarray:
|
|
49
|
+
"""
|
|
50
|
+
Generates a normal distribution of scatterer sizes.
|
|
51
|
+
|
|
52
|
+
The generated sizes are based on the normal distribution's mean and standard deviation.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
n_samples : int
|
|
57
|
+
The number of particle sizes to generate.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
np.ndarray
|
|
62
|
+
An array of scatterer sizes in meters.
|
|
63
|
+
"""
|
|
64
|
+
return np.random.normal(
|
|
65
|
+
loc=self._mean.magnitude,
|
|
66
|
+
scale=self._std_dev.magnitude,
|
|
67
|
+
size=n_samples
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def _generate_default_x(self, x_min: float = -3, x_max: float = 3, n_samples: int = 20) -> np.ndarray:
|
|
71
|
+
"""
|
|
72
|
+
Generates a range of x-values based on the mean and standard deviation.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
x_min : float, optional
|
|
77
|
+
Factor for the minimum x-value as a multiple of the standard deviation from the mean. Default is -3.
|
|
78
|
+
x_max : float, optional
|
|
79
|
+
Factor for the maximum x-value as a multiple of the standard deviation from the mean. Default is 3.
|
|
80
|
+
n_samples : int, optional
|
|
81
|
+
Number of points in the generated range. Default is 500.
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
np.ndarray
|
|
86
|
+
A range of x-values with appropriate units.
|
|
87
|
+
"""
|
|
88
|
+
if x_min >= x_max:
|
|
89
|
+
raise ValueError("x_min must be less than x_max.")
|
|
90
|
+
|
|
91
|
+
mu = self._mean.magnitude
|
|
92
|
+
sigma = self._std_dev.magnitude
|
|
93
|
+
x_min_value = mu + x_min * sigma
|
|
94
|
+
x_max_value = mu + x_max * sigma
|
|
95
|
+
return np.linspace(x_min_value, x_max_value, n_samples) * self._units
|
|
96
|
+
|
|
97
|
+
def get_pdf(self, x: np.ndarray = None, x_min: float = -3, x_max: float = 3, n_samples: int = 20) -> Tuple[np.ndarray, np.ndarray]:
|
|
98
|
+
"""
|
|
99
|
+
Returns the x-values and the scaled PDF values for the normal distribution.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
x : np.ndarray, optional
|
|
104
|
+
The input x-values (particle sizes) over which to compute the PDF. If not provided, a range is generated.
|
|
105
|
+
x_min : float, optional
|
|
106
|
+
Factor for the minimum x-value as a multiple of the standard deviation from the mean. Default is -3.
|
|
107
|
+
x_max : float, optional
|
|
108
|
+
Factor for the maximum x-value as a multiple of the standard deviation from the mean. Default is 3.
|
|
109
|
+
n_samples : int, optional
|
|
110
|
+
Number of points in the generated range. Default is 500.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
Tuple[np.ndarray, np.ndarray]
|
|
115
|
+
The input x-values and the corresponding PDF values.
|
|
116
|
+
"""
|
|
117
|
+
x = self._generate_default_x(x_min=x_min, x_max=x_max, n_samples=n_samples)
|
|
118
|
+
|
|
119
|
+
pdf = norm.pdf(
|
|
120
|
+
x.magnitude,
|
|
121
|
+
loc=self.mean.magnitude,
|
|
122
|
+
scale=self.std_dev.magnitude
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return x, pdf
|
|
126
|
+
|
|
127
|
+
def __repr__(self) -> str:
|
|
128
|
+
return f"Normal({self.mean:.3f~P}, {self.std_dev:.3f~P})"
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base, config_dict
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from PyMieSim.units import Quantity
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(config=config_dict)
|
|
9
|
+
class RosinRammler(Base):
|
|
10
|
+
r"""
|
|
11
|
+
Represents a Particle Size Distribution using the Rosin-Rammler model.
|
|
12
|
+
|
|
13
|
+
The Rosin-Rammler distribution is described by its characteristic size and
|
|
14
|
+
spread parameter, and is used to model particle sizes in systems such as
|
|
15
|
+
powders or granular materials.
|
|
16
|
+
|
|
17
|
+
The distribution function is given by:
|
|
18
|
+
|
|
19
|
+
.. math::
|
|
20
|
+
F(x) = 1 - \exp \left( - \left( \frac{x}{d} \right)^k \right)
|
|
21
|
+
|
|
22
|
+
where:
|
|
23
|
+
- :math:`x` is the particle size.
|
|
24
|
+
- :math:`d` is the characteristic particle size.
|
|
25
|
+
- :math:`k` is the spread parameter.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
characteristic_size : Quantity
|
|
30
|
+
The characteristic particle size in meters.
|
|
31
|
+
spread : float
|
|
32
|
+
The spread parameter (shape factor).
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
characteristic_size: Quantity
|
|
36
|
+
spread: float
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def _units(self) -> Quantity:
|
|
40
|
+
return self.characteristic_size.units
|
|
41
|
+
|
|
42
|
+
@Base.pre_generate
|
|
43
|
+
def generate(self, n_samples: int) -> Quantity:
|
|
44
|
+
"""
|
|
45
|
+
Generates a particle size distribution based on the Rosin-Rammler model.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
n_samples : Quantity
|
|
50
|
+
The number of particle sizes to generate (dimensionless).
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
Quantity
|
|
55
|
+
An array of particle sizes in meters (or other units).
|
|
56
|
+
"""
|
|
57
|
+
# Convert characteristic size to main units
|
|
58
|
+
d = self.characteristic_size.magnitude
|
|
59
|
+
|
|
60
|
+
# Generate uniform random samples in [0, 1)
|
|
61
|
+
u = np.random.uniform(size=n_samples)
|
|
62
|
+
u = np.clip(u, 1e-10, 1 - 1e-10) # Avoid numerical issues
|
|
63
|
+
|
|
64
|
+
# Apply inverse CDF of Rosin-Rammler distribution
|
|
65
|
+
return d * (-np.log(1 - u))**(1 / self.spread)
|
|
66
|
+
|
|
67
|
+
def _generate_default_x(self, x_min: float, x_max: float, n_samples: int = 20) -> np.ndarray:
|
|
68
|
+
"""
|
|
69
|
+
Generates a default range for x-values based on the characteristic size
|
|
70
|
+
and spread of the Rosin-Rammler distribution.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
x_min : float
|
|
75
|
+
Factor for the minimum x-value as a fraction of the characteristic size.
|
|
76
|
+
x_max : float
|
|
77
|
+
Factor for the maximum x-value as a multiple of the characteristic size.
|
|
78
|
+
n_samples : int, optional
|
|
79
|
+
Number of points in the generated range. Default is 500.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
np.ndarray
|
|
84
|
+
A default range of x-values with appropriate units.
|
|
85
|
+
"""
|
|
86
|
+
if x_min <= 0:
|
|
87
|
+
raise ValueError("x_min must be greater than 0.")
|
|
88
|
+
if x_max <= x_min:
|
|
89
|
+
raise ValueError("x_max must be greater than x_min.")
|
|
90
|
+
|
|
91
|
+
d = self.characteristic_size.magnitude # Characteristic size in base units
|
|
92
|
+
x_min = d * x_min # Scale x_min by characteristic size
|
|
93
|
+
x_max = d * x_max # Scale x_max by characteristic size
|
|
94
|
+
return np.linspace(x_min, x_max, n_samples) * self._units
|
|
95
|
+
|
|
96
|
+
def get_pdf(self, x_min: float = 0.01, x_max: float = 2, n_samples: int = 20) -> Tuple[np.ndarray, np.ndarray]:
|
|
97
|
+
r"""
|
|
98
|
+
Returns the x-values and the scaled PDF values for the particle size distribution.
|
|
99
|
+
|
|
100
|
+
The PDF for the Rosin-Rammler distribution is derived from the CDF:
|
|
101
|
+
|
|
102
|
+
.. math::
|
|
103
|
+
f(x) = \frac{k}{d} \left( \frac{x}{d} \right)^{k-1} \exp \left( - \left( \frac{x}{d} \right)^k \right)
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
x_min : float, optional
|
|
108
|
+
Factor for the minimum x-value as a fraction of the characteristic size. Default is 0.01.
|
|
109
|
+
x_max : float, optional
|
|
110
|
+
Factor for the maximum x-value as a multiple of the characteristic size. Default is 5.
|
|
111
|
+
n_samples : int, optional
|
|
112
|
+
Number of points in the generated range. Default is 500.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
Tuple[np.ndarray, np.ndarray]
|
|
117
|
+
The input x-values and the corresponding scaled PDF values.
|
|
118
|
+
"""
|
|
119
|
+
# Generate x-values based on user-defined or default parameters
|
|
120
|
+
x = self._generate_default_x(x_min=x_min, x_max=x_max, n_samples=n_samples)
|
|
121
|
+
|
|
122
|
+
common_units = x.units
|
|
123
|
+
d = self.characteristic_size.to(common_units).magnitude
|
|
124
|
+
k = self.spread
|
|
125
|
+
|
|
126
|
+
# Rosin-Rammler PDF formula
|
|
127
|
+
pdf = (k / d) * (x.magnitude / d)**(k - 1) * np.exp(-(x.magnitude / d)**k)
|
|
128
|
+
|
|
129
|
+
return x, pdf
|
|
130
|
+
|
|
131
|
+
def __repr__(self) -> str:
|
|
132
|
+
return f"RR({self.characteristic_size:.3f~P}, {self.spread:.3f})"
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base, config_dict
|
|
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
|
+
@dataclass(config=config_dict)
|
|
9
|
+
class Uniform(Base):
|
|
10
|
+
r"""
|
|
11
|
+
Represents a uniform distribution for particle sizes.
|
|
12
|
+
|
|
13
|
+
The uniform distribution assigns equal probability to all particle sizes within a specified range:
|
|
14
|
+
|
|
15
|
+
.. math::
|
|
16
|
+
f(x) = \frac{1}{b - a} \quad \text{for} \quad a \leq x \leq b
|
|
17
|
+
|
|
18
|
+
where:
|
|
19
|
+
- :math:`a` is the lower bound of the distribution.
|
|
20
|
+
- :math:`b` is the upper bound of the distribution.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
lower_bound : Quantity
|
|
25
|
+
The lower bound for particle sizes in meters.
|
|
26
|
+
upper_bound : Quantity
|
|
27
|
+
The upper bound for particle sizes in meters.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
lower_bound: Quantity
|
|
31
|
+
upper_bound: Quantity
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def _units(self) -> Quantity:
|
|
35
|
+
return self.lower_bound.units
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def _lower_bound(self) -> Quantity:
|
|
39
|
+
return self.lower_bound.to(self._units)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def _upper_bound(self) -> Quantity:
|
|
43
|
+
return self.upper_bound.to(self._units)
|
|
44
|
+
|
|
45
|
+
def __post_init__(self):
|
|
46
|
+
self.lower_bound = self.lower_bound.to(self.upper_bound.units)
|
|
47
|
+
|
|
48
|
+
def _generate_default_x(self, n_samples: int = 100) -> Quantity:
|
|
49
|
+
"""
|
|
50
|
+
Generates a default range of x-values for the uniform distribution.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
n_points : int, optional
|
|
55
|
+
Number of points in the generated range. Default is 100.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
Quantity
|
|
60
|
+
A range of x-values with appropriate units.
|
|
61
|
+
"""
|
|
62
|
+
x_min = self._lower_bound.magnitude / 1.1
|
|
63
|
+
x_max = self._upper_bound.magnitude * 1.1
|
|
64
|
+
|
|
65
|
+
return np.linspace(x_min, x_max, n_samples) * self._units
|
|
66
|
+
|
|
67
|
+
@Base.pre_generate
|
|
68
|
+
def generate(self, n_samples: int) -> Quantity:
|
|
69
|
+
"""
|
|
70
|
+
Generates a uniform distribution of scatterer sizes.
|
|
71
|
+
|
|
72
|
+
The generated sizes are uniformly distributed between the specified `lower_bound` and `upper_bound`.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
n_samples : int
|
|
77
|
+
The number of particle sizes to generate.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
Quantity
|
|
82
|
+
An array of scatterer sizes in meters.
|
|
83
|
+
"""
|
|
84
|
+
return np.random.uniform(
|
|
85
|
+
self._lower_bound.magnitude,
|
|
86
|
+
self._upper_bound.magnitude,
|
|
87
|
+
n_samples
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def get_pdf(self, n_samples: int = 100) -> Tuple[Quantity, np.ndarray]:
|
|
91
|
+
"""
|
|
92
|
+
Returns the x-values and the PDF values for the uniform distribution.
|
|
93
|
+
|
|
94
|
+
If `x` is not provided, a default range of x-values is generated.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
n_samples : int, optional
|
|
99
|
+
Number of points in the generated range if `x` is not provided. Default is 100.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
Tuple[Quantity, np.ndarray]
|
|
104
|
+
The input x-values and the corresponding PDF values.
|
|
105
|
+
"""
|
|
106
|
+
x = self._generate_default_x(n_samples=n_samples)
|
|
107
|
+
|
|
108
|
+
pdf = uniform.pdf(
|
|
109
|
+
x.magnitude,
|
|
110
|
+
loc=self._lower_bound.magnitude,
|
|
111
|
+
scale=self._upper_bound.magnitude - self._lower_bound.magnitude
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return x, pdf
|
|
115
|
+
|
|
116
|
+
def __repr__(self) -> str:
|
|
117
|
+
return f"Uniform(lower_bound={self.lower_bound:.3f~P}, upper_bound={self.upper_bound:.3f~P})"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from FlowCyPy.distribution.base_class import Base, config_dict
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from PyMieSim.units import Quantity
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(config=config_dict)
|
|
9
|
+
class Weibull(Base):
|
|
10
|
+
r"""
|
|
11
|
+
Represents a Weibull distribution for particle sizes.
|
|
12
|
+
|
|
13
|
+
The Weibull distribution is commonly used for modeling size distributions in biological systems.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
shape : Quantity
|
|
18
|
+
The shape parameter (k), controls the skewness of the distribution.
|
|
19
|
+
scale : Quantity
|
|
20
|
+
The scale parameter (λ), controls the spread of the distribution.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
shape: Quantity
|
|
24
|
+
scale: Quantity
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def _units(self) -> Quantity:
|
|
28
|
+
return self.shape.units
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def _shape(self) -> Quantity:
|
|
32
|
+
return self.shape.to(self._units)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def _scale(self) -> Quantity:
|
|
36
|
+
return self.scale.to(self._units)
|
|
37
|
+
|
|
38
|
+
def _generate_default_x(self, n_samples: int, x_min_factor: float, x_max_factor: float) -> Quantity:
|
|
39
|
+
"""
|
|
40
|
+
Generates a default range of x-values for the Weibull distribution.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
n_samples : int, optional
|
|
45
|
+
Number of points in the generated range.
|
|
46
|
+
x_min_factor : float, optional
|
|
47
|
+
Factor for the minimum x-value relative to the scale parameter.
|
|
48
|
+
x_max_factor : float, optional
|
|
49
|
+
Factor for the maximum x-value relative to the scale parameter.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
Quantity
|
|
54
|
+
A range of x-values with appropriate units.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
if x_min_factor <= 0:
|
|
58
|
+
raise ValueError("x_min_factor must be greater than 0.")
|
|
59
|
+
|
|
60
|
+
x_min = self.scale.magnitude * x_min_factor
|
|
61
|
+
x_max = self.scale.magnitude * x_max_factor
|
|
62
|
+
return np.linspace(x_min, x_max, n_samples) * self.scale.units
|
|
63
|
+
|
|
64
|
+
@Base.pre_generate
|
|
65
|
+
def generate(self, n_samples: int) -> Quantity:
|
|
66
|
+
"""
|
|
67
|
+
Generates a Weibull distribution of scatterer sizes.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
n_samples : int
|
|
72
|
+
The number of particle sizes to generate.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
Quantity
|
|
77
|
+
An array of particle sizes in meters.
|
|
78
|
+
"""
|
|
79
|
+
return np.random.weibull(
|
|
80
|
+
self.shape.magnitude,
|
|
81
|
+
size=n_samples
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def get_pdf(self, n_samples: int = 100) -> Tuple[Quantity, np.ndarray]:
|
|
85
|
+
"""
|
|
86
|
+
Returns the x-values and the PDF values for the Weibull distribution.
|
|
87
|
+
|
|
88
|
+
If `x` is not provided, a default range of x-values is generated.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
x : Quantity, optional
|
|
93
|
+
The input x-values (particle sizes) over which to compute the PDF. If not provided, a range is generated.
|
|
94
|
+
n_points : int, optional
|
|
95
|
+
Number of points in the generated range if `x` is not provided. Default is 100.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
Tuple[Quantity, np.ndarray]
|
|
100
|
+
The input x-values and the corresponding PDF values.
|
|
101
|
+
"""
|
|
102
|
+
x = self._generate_default_x(n_samples=n_samples)
|
|
103
|
+
|
|
104
|
+
common_units = self.scale.units
|
|
105
|
+
scale_magnitude = self.scale.to(common_units).magnitude
|
|
106
|
+
shape_magnitude = self.shape.to(common_units).magnitude
|
|
107
|
+
|
|
108
|
+
pdf = (shape_magnitude / scale_magnitude) * \
|
|
109
|
+
((x.to(common_units).magnitude / scale_magnitude) ** (shape_magnitude - 1)) * \
|
|
110
|
+
np.exp(-(x.to(common_units).magnitude / scale_magnitude) ** shape_magnitude)
|
|
111
|
+
|
|
112
|
+
return x, pdf
|
|
113
|
+
|
|
114
|
+
def __repr__(self) -> str:
|
|
115
|
+
return f"Weibull(shape={self.shape:.3f~P}, scale={self.scale:.3f~P})"
|