From d5fb86d694a9b7cb33fd6e305ce89b3e765feafc Mon Sep 17 00:00:00 2001 From: Nicolas Mounet Date: Sun, 30 May 2021 19:28:51 +0200 Subject: [PATCH] Removing _MaskBase and old Mask class, which is replaced by MeasuredMask --- cara/models.py | 100 ++++++++++++++----------------------------------- 1 file changed, 28 insertions(+), 72 deletions(-) diff --git a/cara/models.py b/cara/models.py index a72f1b48..736e0992 100644 --- a/cara/models.py +++ b/cara/models.py @@ -472,61 +472,25 @@ Virus.types = { @dataclass(frozen=True) -class _MaskBase: - """ - Represents the filtration of aerosols by a mask, both inward and - outward. - The nature of the various mask models means that it is expected - for subclasses of _MaskBase to exist. - """ +class Mask: + #: Filtration efficiency of masks when inhaling. + η_inhale: _VectorisedFloat + + #: Global factor applied to filtration efficiency of masks when exhaling. + factor_exhale: _VectorisedFloat = 1. + #: Pre-populated examples of Masks. - types: typing.ClassVar[typing.Dict[str, "_MaskBase"]] + types: typing.ClassVar[typing.Dict[str, "Mask"]] def exhale_efficiency(self, diameter: float) -> _VectorisedFloat: - # Overall exhale efficiency, including the effect of the leaks. - raise NotImplementedError("Subclass must implement") - - def inhale_efficiency(self) -> _VectorisedFloat: - # Overall inhale efficiency, including the effect of the leaks. - raise NotImplementedError("Subclass must implement") - - -@dataclass(frozen=True) -class Mask(_MaskBase): - #: Filtration efficiency. - η_exhale: _VectorisedFloat - - #: Leakage through side of masks. - η_leaks: _VectorisedFloat - - #: Filtration efficiency of masks when inhaling. - η_inhale: _VectorisedFloat - - def exhale_efficiency(self, diameter: float) -> _VectorisedFloat: - # Overall efficiency with the effect of the leaks for aerosol emission - # Gammaitoni et al (1997). Diameter is in cm. - if diameter < 3e-4: - eta_out = 0. - else: - eta_out = self.η_exhale * (1 - self.η_leaks) - return eta_out - - def inhale_efficiency(self) -> _VectorisedFloat: - # Overall inhale efficiency, including the effect of the leaks. - return self.η_inhale - - -@dataclass(frozen=True) -class MeasuredMask(_MaskBase): - #: Filtration efficiency of masks when inhaling. - η_inhale: _VectorisedFloat - - def exhale_efficiency(self, diameter: float) -> _VectorisedFloat: - # See CERN-OPEN-2021-004 (doi: 10.17181/CERN.1GDQ.5Y75), and Ref. - # therein (Asadi 2020). - # Obtained from measurements of filtration efficiency and of - # the leakage through the sides. - # Diameter is in cm. + """ + Overall exhale efficiency, including the effect of the leaks. + See CERN-OPEN-2021-004 (doi: 10.17181/CERN.1GDQ.5Y75), and Ref. + therein (Asadi 2020). + Obtained from measurements of filtration efficiency and of + the leakage through the sides. + Diameter is in cm. + """ if diameter < 0.5e-4: eta_out = 0. elif diameter < 0.94614e-4: @@ -535,29 +499,21 @@ class MeasuredMask(_MaskBase): eta_out = 0.0509 * diameter * 1e4 + 0.664 else: eta_out = 0.8167 - return eta_out + return eta_out*self.factor_exhale def inhale_efficiency(self) -> _VectorisedFloat: - # Overall inhale efficiency, including the effect of the leaks. + """ + Overall inhale efficiency, including the effect of the leaks. + """ return self.η_inhale -_MaskBase.types = { - 'No mask': Mask(0, 0, 0), +Mask.types = { + 'No mask': Mask(0, 0), 'Type I': Mask( - η_exhale=0.95, - η_leaks=0.15, # (Huang 2007) - η_inhale=0.3, # (Browen 2010) - ), - 'FFP2': Mask( - η_exhale=0.95, # (same outward effect as type 1 - Asadi 2020) - η_leaks=0.15, # (same outward effect as type 1 - Asadi 2020) - η_inhale=0.865, # (94% penetration efficiency + 8% max inward leakage -> EN 149) - ), - 'Type I measured': MeasuredMask( η_inhale=0.5, # (CERN-OPEN-2021-004) ), - 'FFP2 measured': MeasuredMask( + 'FFP2': Mask( η_inhale=0.865, # (94% penetration efficiency + 8% max inward leakage -> EN 149) ), } @@ -569,10 +525,10 @@ class _ExpirationBase: Represents the expiration of aerosols by a person. Subclasses of _ExpirationBase represent different models. """ - #: Pre-populated examples of Masks. + #: Pre-populated examples of Expirations. types: typing.ClassVar[typing.Dict[str, "_ExpirationBase"]] - def aerosols(self, mask: _MaskBase): + def aerosols(self, mask: Mask): # total volume of aerosols expired per volume of air (mL/cm^3). raise NotImplementedError("Subclass must implement") @@ -589,7 +545,7 @@ class Expiration(_ExpirationBase): ejection_factor: typing.Tuple[float, ...] particle_sizes: typing.Tuple[float, ...] = (0.8e-4, 1.8e-4, 3.5e-4, 5.5e-4) # In cm. - def aerosols(self, mask: _MaskBase): + def aerosols(self, mask: Mask): def volume(diameter): return (4 * np.pi * (diameter/2)**3) / 3 total = 0 @@ -617,7 +573,7 @@ class MultipleExpiration(_ExpirationBase): raise ValueError("expirations and weigths should contain the" "same number of elements") - def aerosols(self, mask: _MaskBase): + def aerosols(self, mask: Mask): return np.array([ weight * expiration.aerosols(mask) / sum(self.weights) for weight,expiration in zip(self.weights,self.expirations) @@ -665,7 +621,7 @@ class Population: presence: Interval #: The kind of mask being worn by the people. - mask: _MaskBase + mask: Mask #: The physical activity being carried out by the people. activity: Activity