Expand source code
class ConcentrationProduct(MomentProduct):
def __init__(self, *, unit: str, name: str, specific: bool, stp: bool):
+ """
+ `stp` toggles expressing the concentration in terms of standard temperature
+ and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
+ """
super().__init__(unit=unit, name=name)
self.specific = specific
self.stp = stp
@@ -131,6 +141,7 @@ Subclasses
FrozenParticleConcentration
IceNucleiConcentration
ParticleConcentration
+ActivatedParticleConcentration
TotalParticleConcentration
TotalParticleSpecificConcentration
diff --git a/PySDM/products/impl/index.html b/PySDM/products/impl/index.html
index e8525293d..647b1f36d 100644
--- a/PySDM/products/impl/index.html
+++ b/PySDM/products/impl/index.html
@@ -38,6 +38,10 @@ Module PySDM.products.impl
+PySDM.products.impl.activation_filtered_product
+
+common base class for products filtering droplets based on their activation state
+
PySDM.products.impl.concentration_product
common code for products computing particle concentrations with
@@ -86,6 +90,7 @@
Index
Methods
diff --git a/PySDM/products/size_spectral/index.html b/PySDM/products/size_spectral/index.html
index 3145baa3b..ed69c0139 100644
--- a/PySDM/products/size_spectral/index.html
+++ b/PySDM/products/size_spectral/index.html
@@ -36,8 +36,14 @@ Module PySDM.products.size_spectral
)
from .effective_radius import EffectiveRadius
from .mean_radius import MeanRadius
+from .mean_radius_activated import ActivatedMeanRadius
+from .mean_volume_radius import MeanVolumeRadius
from .number_size_spectrum import NumberSizeSpectrum
from .particle_concentration import ParticleConcentration, ParticleSpecificConcentration
+from .particle_concentration_activated import (
+ ActivatedParticleConcentration,
+ ActivatedParticleSpecificConcentration,
+)
from .particle_size_spectrum import (
ParticleSizeSpectrumPerMass,
ParticleSizeSpectrumPerVolume,
@@ -48,6 +54,11 @@ Module PySDM.products.size_spectral
from .radius_binned_number_averaged_terminal_velocity import (
RadiusBinnedNumberAveragedTerminalVelocity,
)
+from .size_standard_deviation import (
+ AreaStandardDeviation,
+ RadiusStandardDeviation,
+ VolumeStandardDeviation,
+)
from .total_particle_concentration import TotalParticleConcentration
from .total_particle_specific_concentration import TotalParticleSpecificConcentration
from .water_mixing_ratio import WaterMixingRatio
@@ -69,6 +80,14 @@
mean radius of particles within a grid cell (optionally restricted to a given size range)
+PySDM.products.size_spectral.mean_radius_activated
+
+mean radius of particles within a grid cell, for activated, unactivated or both
+
+PySDM.products.size_spectral.mean_volume_radius
+
+mean volume radius of particles within a grid cell, for activated, unactivated or both
+
PySDM.products.size_spectral.number_size_spectrum
n(V) particle volume spectrum per volume of air,
@@ -79,6 +98,11 @@
concentration of particles within a grid cell (either per-volume of per-mass-of-dry air,
optionally restricted to a given size range)
+
PySDM.products.size_spectral.particle_concentration_activated
+
+concentration of particles within a grid cell (either per-volume of per-mass-of-dry air),
+activated, unactivated or both
+
PySDM.products.size_spectral.particle_size_spectrum
wet radius-binned particle size spectra (per mass of dry air or per volume of air)
@@ -92,6 +116,11 @@
Provides radius bin-resolved average terminal velocity (average is particle-number weighted)
+ PySDM.products.size_spectral.size_standard_deviation
+
+standard deviation of radius/area/volume of particles within a grid cell,
+for activated, unactivated or both
+
PySDM.products.size_spectral.total_particle_concentration
particle concentration (per volume of air)
@@ -130,11 +159,15 @@ Index
PySDM.products.size_spectral.arbitrary_moment
PySDM.products.size_spectral.effective_radius
PySDM.products.size_spectral.mean_radius
+PySDM.products.size_spectral.mean_radius_activated
+PySDM.products.size_spectral.mean_volume_radius
PySDM.products.size_spectral.number_size_spectrum
PySDM.products.size_spectral.particle_concentration
+PySDM.products.size_spectral.particle_concentration_activated
PySDM.products.size_spectral.particle_size_spectrum
PySDM.products.size_spectral.particle_volume_versus_radius_logarithm_spectrum
PySDM.products.size_spectral.radius_binned_number_averaged_terminal_velocity
+PySDM.products.size_spectral.size_standard_deviation
PySDM.products.size_spectral.total_particle_concentration
PySDM.products.size_spectral.total_particle_specific_concentration
PySDM.products.size_spectral.water_mixing_ratio
diff --git a/PySDM/products/size_spectral/mean_radius.html b/PySDM/products/size_spectral/mean_radius.html
index 5080e4922..4ded1e556 100644
--- a/PySDM/products/size_spectral/mean_radius.html
+++ b/PySDM/products/size_spectral/mean_radius.html
@@ -30,15 +30,30 @@ Module PySDM.products.size_spectral.mean_radius
"""
mean radius of particles within a grid cell (optionally restricted to a given size range)
"""
+import numpy as np
+
from PySDM.products.impl.moment_product import MomentProduct
class MeanRadius(MomentProduct):
- def __init__(self, name=None, unit="m"):
+ def __init__(
+ self,
+ name=None,
+ unit="m",
+ radius_range=(0, np.inf),
+ ):
+ self.radius_range = radius_range
super().__init__(name=name, unit=unit)
def _impl(self, **kwargs):
- self._download_moment_to_buffer(attr="volume", rank=1 / 3)
+ self._download_moment_to_buffer(
+ attr="volume",
+ rank=1 / 3,
+ filter_range=(
+ self.formulae.trivia.volume(self.radius_range[0]),
+ self.formulae.trivia.volume(self.radius_range[1]),
+ ),
+ )
self.buffer[:] /= self.formulae.constants.PI_4_3 ** (1 / 3)
return self.buffer
@@ -54,7 +69,7 @@
class MeanRadius
-( name=None, unit='m')
+( name=None, unit='m', radius_range=(0, inf))
Helper class that provides a standard way to create an ABC using
@@ -64,11 +79,24 @@
Expand source code
class MeanRadius(MomentProduct):
- def __init__(self, name=None, unit="m"):
+ def __init__(
+ self,
+ name=None,
+ unit="m",
+ radius_range=(0, np.inf),
+ ):
+ self.radius_range = radius_range
super().__init__(name=name, unit=unit)
def _impl(self, **kwargs):
- self._download_moment_to_buffer(attr="volume", rank=1 / 3)
+ self._download_moment_to_buffer(
+ attr="volume",
+ rank=1 / 3,
+ filter_range=(
+ self.formulae.trivia.volume(self.radius_range[0]),
+ self.formulae.trivia.volume(self.radius_range[1]),
+ ),
+ )
self.buffer[:] /= self.formulae.constants.PI_4_3 ** (1 / 3)
return self.buffer
diff --git a/PySDM/products/size_spectral/mean_radius_activated.html b/PySDM/products/size_spectral/mean_radius_activated.html
new file mode 100644
index 000000000..ae0be79f9
--- /dev/null
+++ b/PySDM/products/size_spectral/mean_radius_activated.html
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
PySDM.products.size_spectral.mean_radius_activated API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM.products.size_spectral.mean_radius_activated
+
+
+mean radius of particles within a grid cell, for activated, unactivated or both
+
+
+Expand source code
+
+"""
+mean radius of particles within a grid cell, for activated, unactivated or both
+"""
+from PySDM.products.impl.activation_filtered_product import _ActivationFilteredProduct
+from PySDM.products.impl.moment_product import MomentProduct
+
+
+class ActivatedMeanRadius(MomentProduct, _ActivationFilteredProduct):
+ def __init__(
+ self, count_unactivated: bool, count_activated: bool, name=None, unit="m"
+ ):
+ MomentProduct.__init__(self, name=name, unit=unit)
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ for base_class in (_ActivationFilteredProduct, MomentProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr="volume", rank=1 / 3)
+ self.buffer[:] /= self.formulae.constants.PI_4_3 ** (1 / 3)
+ return self.buffer
+
+
+
+
+
+
+
+
+
+class ActivatedMeanRadius
+( count_unactivated: bool, count_activated: bool, name=None, unit='m')
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
+
+Expand source code
+
+class ActivatedMeanRadius(MomentProduct, _ActivationFilteredProduct):
+ def __init__(
+ self, count_unactivated: bool, count_activated: bool, name=None, unit="m"
+ ):
+ MomentProduct.__init__(self, name=name, unit=unit)
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ for base_class in (_ActivationFilteredProduct, MomentProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr="volume", rank=1 / 3)
+ self.buffer[:] /= self.formulae.constants.PI_4_3 ** (1 / 3)
+ return self.buffer
+
+Ancestors
+
+MomentProduct
+Product
+abc.ABC
+PySDM.products.impl.activation_filtered_product._ActivationFilteredProduct
+
+Inherited members
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM/products/size_spectral/mean_volume_radius.html b/PySDM/products/size_spectral/mean_volume_radius.html
new file mode 100644
index 000000000..8c29105dc
--- /dev/null
+++ b/PySDM/products/size_spectral/mean_volume_radius.html
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
PySDM.products.size_spectral.mean_volume_radius API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM.products.size_spectral.mean_volume_radius
+
+
+mean volume radius of particles within a grid cell, for activated, unactivated or both
+
+
+Expand source code
+
+"""
+mean volume radius of particles within a grid cell, for activated, unactivated or both
+"""
+
+from PySDM.products.impl.activation_filtered_product import _ActivationFilteredProduct
+from PySDM.products.impl.moment_product import MomentProduct
+
+
+class MeanVolumeRadius(MomentProduct, _ActivationFilteredProduct):
+ def __init__(
+ self, count_unactivated: bool, count_activated: bool, name=None, unit="m"
+ ):
+ MomentProduct.__init__(self, name=name, unit=unit)
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ for base_class in (_ActivationFilteredProduct, MomentProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr="volume", rank=1)
+ return self.formulae.trivia.radius(self.buffer[:])
+
+
+
+
+
+
+
+
+
+class MeanVolumeRadius
+( count_unactivated: bool, count_activated: bool, name=None, unit='m')
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
+
+Expand source code
+
+class MeanVolumeRadius(MomentProduct, _ActivationFilteredProduct):
+ def __init__(
+ self, count_unactivated: bool, count_activated: bool, name=None, unit="m"
+ ):
+ MomentProduct.__init__(self, name=name, unit=unit)
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ for base_class in (_ActivationFilteredProduct, MomentProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr="volume", rank=1)
+ return self.formulae.trivia.radius(self.buffer[:])
+
+Ancestors
+
+MomentProduct
+Product
+abc.ABC
+PySDM.products.impl.activation_filtered_product._ActivationFilteredProduct
+
+Inherited members
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM/products/size_spectral/particle_concentration.html b/PySDM/products/size_spectral/particle_concentration.html
index d30e02a9d..d390753ce 100644
--- a/PySDM/products/size_spectral/particle_concentration.html
+++ b/PySDM/products/size_spectral/particle_concentration.html
@@ -83,7 +83,9 @@
Helper class that provides a standard way to create an ABC using
-inheritance.
+inheritance.
+stp
toggles expressing the concentration in terms of standard temperature
+and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
Expand source code
@@ -138,7 +140,9 @@ Inherited members
Helper class that provides a standard way to create an ABC using
-inheritance.
+inheritance.
+stp
toggles expressing the concentration in terms of standard temperature
+and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
Expand source code
diff --git a/PySDM/products/size_spectral/particle_concentration_activated.html b/PySDM/products/size_spectral/particle_concentration_activated.html
new file mode 100644
index 000000000..25987cc9c
--- /dev/null
+++ b/PySDM/products/size_spectral/particle_concentration_activated.html
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+PySDM.products.size_spectral.particle_concentration_activated API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM.products.size_spectral.particle_concentration_activated
+
+
+concentration of particles within a grid cell (either per-volume of per-mass-of-dry air),
+activated, unactivated or both
+
+
+Expand source code
+
+"""
+concentration of particles within a grid cell (either per-volume of per-mass-of-dry air),
+ activated, unactivated or both
+"""
+
+from PySDM.products.impl.activation_filtered_product import _ActivationFilteredProduct
+from PySDM.products.impl.concentration_product import ConcentrationProduct
+
+
+class ActivatedParticleConcentration(ConcentrationProduct, _ActivationFilteredProduct):
+ # pylint: disable=too-many-arguments
+ def __init__(
+ self,
+ *,
+ count_unactivated: bool,
+ count_activated: bool,
+ specific=False,
+ stp=False,
+ name=None,
+ unit="m^-3",
+ ):
+ ConcentrationProduct.__init__(
+ self, name=name, unit=unit, specific=specific, stp=stp
+ )
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ for base_class in (_ActivationFilteredProduct, ConcentrationProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr="volume", rank=0)
+ return ConcentrationProduct._impl(self, **kwargs)
+
+
+class ActivatedParticleSpecificConcentration(ActivatedParticleConcentration):
+ def __init__(self, count_unactivated, count_activated, name=None, unit="kg^-1"):
+ super().__init__(
+ count_unactivated=count_unactivated,
+ count_activated=count_activated,
+ specific=True,
+ name=name,
+ unit=unit,
+ )
+
+
+
+
+
+
+
+
+
+class ActivatedParticleConcentration
+( *, count_unactivated: bool, count_activated: bool, specific=False, stp=False, name=None, unit='m^-3')
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
stp
toggles expressing the concentration in terms of standard temperature
+and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
+
+
+Expand source code
+
+class ActivatedParticleConcentration(ConcentrationProduct, _ActivationFilteredProduct):
+ # pylint: disable=too-many-arguments
+ def __init__(
+ self,
+ *,
+ count_unactivated: bool,
+ count_activated: bool,
+ specific=False,
+ stp=False,
+ name=None,
+ unit="m^-3",
+ ):
+ ConcentrationProduct.__init__(
+ self, name=name, unit=unit, specific=specific, stp=stp
+ )
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ for base_class in (_ActivationFilteredProduct, ConcentrationProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr="volume", rank=0)
+ return ConcentrationProduct._impl(self, **kwargs)
+
+Ancestors
+
+Subclasses
+
+Inherited members
+
+
+
+class ActivatedParticleSpecificConcentration
+( count_unactivated, count_activated, name=None, unit='kg^-1')
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
stp
toggles expressing the concentration in terms of standard temperature
+and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
+
+
+Expand source code
+
+class ActivatedParticleSpecificConcentration(ActivatedParticleConcentration):
+ def __init__(self, count_unactivated, count_activated, name=None, unit="kg^-1"):
+ super().__init__(
+ count_unactivated=count_unactivated,
+ count_activated=count_activated,
+ specific=True,
+ name=name,
+ unit=unit,
+ )
+
+Ancestors
+
+Inherited members
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM/products/size_spectral/size_standard_deviation.html b/PySDM/products/size_spectral/size_standard_deviation.html
new file mode 100644
index 000000000..ca14b5e27
--- /dev/null
+++ b/PySDM/products/size_spectral/size_standard_deviation.html
@@ -0,0 +1,287 @@
+
+
+
+
+
+
+PySDM.products.size_spectral.size_standard_deviation API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM.products.size_spectral.size_standard_deviation
+
+
+standard deviation of radius/area/volume of particles within a grid cell,
+for activated, unactivated or both
+
+
+Expand source code
+
+"""
+standard deviation of radius/area/volume of particles within a grid cell,
+for activated, unactivated or both
+"""
+import numpy as np
+
+from PySDM.products.impl.activation_filtered_product import _ActivationFilteredProduct
+from PySDM.products.impl.moment_product import MomentProduct
+
+
+class _SizeStandardDeviation(MomentProduct, _ActivationFilteredProduct):
+ # pylint: disable=too-many-arguments
+ def __init__(
+ self,
+ count_unactivated: bool,
+ count_activated: bool,
+ name=None,
+ unit="m",
+ attr="radius",
+ ):
+ self.attr = attr
+ MomentProduct.__init__(self, name=name, unit=unit)
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ builder.request_attribute(self.attr)
+ for base_class in (_ActivationFilteredProduct, MomentProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr=self.attr, rank=1)
+ tmp = np.empty_like(self.buffer)
+ tmp[:] = -self.buffer**2
+ _ActivationFilteredProduct.impl(self, attr=self.attr, rank=2)
+ tmp[:] += self.buffer
+ tmp[:] = np.sqrt(tmp)
+ return tmp
+
+
+RadiusStandardDeviation = _SizeStandardDeviation
+
+
+class AreaStandardDeviation(_SizeStandardDeviation):
+ def __init__(
+ self, *, name=None, unit="m^2", count_activated: bool, count_unactivated: bool
+ ):
+ super().__init__(
+ name=name,
+ unit=unit,
+ count_activated=count_activated,
+ count_unactivated=count_unactivated,
+ attr="area",
+ )
+
+
+class VolumeStandardDeviation(_SizeStandardDeviation):
+ def __init__(
+ self, *, name=None, unit="m^3", count_activated: bool, count_unactivated: bool
+ ):
+ super().__init__(
+ name=name,
+ unit=unit,
+ count_activated=count_activated,
+ count_unactivated=count_unactivated,
+ attr="volume",
+ )
+
+
+
+
+
+
+
+
+
+class AreaStandardDeviation
+( *, name=None, unit='m^2', count_activated: bool, count_unactivated: bool)
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
+
+Expand source code
+
+class AreaStandardDeviation(_SizeStandardDeviation):
+ def __init__(
+ self, *, name=None, unit="m^2", count_activated: bool, count_unactivated: bool
+ ):
+ super().__init__(
+ name=name,
+ unit=unit,
+ count_activated=count_activated,
+ count_unactivated=count_unactivated,
+ attr="area",
+ )
+
+Ancestors
+
+Inherited members
+
+
+
+class VolumeStandardDeviation
+( *, name=None, unit='m^3', count_activated: bool, count_unactivated: bool)
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
+
+Expand source code
+
+class VolumeStandardDeviation(_SizeStandardDeviation):
+ def __init__(
+ self, *, name=None, unit="m^3", count_activated: bool, count_unactivated: bool
+ ):
+ super().__init__(
+ name=name,
+ unit=unit,
+ count_activated=count_activated,
+ count_unactivated=count_unactivated,
+ attr="volume",
+ )
+
+Ancestors
+
+Inherited members
+
+
+
+class RadiusStandardDeviation
+( count_unactivated: bool, count_activated: bool, name=None, unit='m', attr='radius')
+
+
+Helper class that provides a standard way to create an ABC using
+inheritance.
+
+
+Expand source code
+
+class _SizeStandardDeviation(MomentProduct, _ActivationFilteredProduct):
+ # pylint: disable=too-many-arguments
+ def __init__(
+ self,
+ count_unactivated: bool,
+ count_activated: bool,
+ name=None,
+ unit="m",
+ attr="radius",
+ ):
+ self.attr = attr
+ MomentProduct.__init__(self, name=name, unit=unit)
+ _ActivationFilteredProduct.__init__(
+ self, count_activated=count_activated, count_unactivated=count_unactivated
+ )
+
+ def register(self, builder):
+ builder.request_attribute(self.attr)
+ for base_class in (_ActivationFilteredProduct, MomentProduct):
+ base_class.register(self, builder)
+
+ def _impl(self, **kwargs):
+ _ActivationFilteredProduct.impl(self, attr=self.attr, rank=1)
+ tmp = np.empty_like(self.buffer)
+ tmp[:] = -self.buffer**2
+ _ActivationFilteredProduct.impl(self, attr=self.attr, rank=2)
+ tmp[:] += self.buffer
+ tmp[:] = np.sqrt(tmp)
+ return tmp
+
+Ancestors
+
+MomentProduct
+Product
+abc.ABC
+PySDM.products.impl.activation_filtered_product._ActivationFilteredProduct
+
+Subclasses
+
+Inherited members
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM/products/size_spectral/total_particle_concentration.html b/PySDM/products/size_spectral/total_particle_concentration.html
index f6afe66f9..9c507c0e6 100644
--- a/PySDM/products/size_spectral/total_particle_concentration.html
+++ b/PySDM/products/size_spectral/total_particle_concentration.html
@@ -57,7 +57,9 @@
Helper class that provides a standard way to create an ABC using
-inheritance.
+inheritance.
+stp
toggles expressing the concentration in terms of standard temperature
+and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
Expand source code
diff --git a/PySDM/products/size_spectral/total_particle_specific_concentration.html b/PySDM/products/size_spectral/total_particle_specific_concentration.html
index 025dd0776..a574cd640 100644
--- a/PySDM/products/size_spectral/total_particle_specific_concentration.html
+++ b/PySDM/products/size_spectral/total_particle_specific_concentration.html
@@ -57,7 +57,9 @@
Helper class that provides a standard way to create an ABC using
-inheritance.
+inheritance.
+stp
toggles expressing the concentration in terms of standard temperature
+and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
Expand source code
diff --git a/PySDM_examples/Grabowski_and_Pawlowska_2023/index.html b/PySDM_examples/Grabowski_and_Pawlowska_2023/index.html
new file mode 100644
index 000000000..096b8f401
--- /dev/null
+++ b/PySDM_examples/Grabowski_and_Pawlowska_2023/index.html
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+PySDM_examples.Grabowski_and_Pawlowska_2023 API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM_examples.Grabowski_and_Pawlowska_2023
+
+
+
+
+Expand source code
+
+# pylint: disable=invalid-name
+from .settings import Settings
+from .simulation import Simulation
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.html b/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.html
new file mode 100644
index 000000000..a6eb95ca0
--- /dev/null
+++ b/PySDM_examples/Grabowski_and_Pawlowska_2023/settings.html
@@ -0,0 +1,326 @@
+
+
+
+
+
+
+PySDM_examples.Grabowski_and_Pawlowska_2023.settings API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM_examples.Grabowski_and_Pawlowska_2023.settings
+
+
+
+
+Expand source code
+
+import numpy as np
+from pystrict import strict
+
+from PySDM import Formulae
+from PySDM.initialisation.spectra import Lognormal, Sum
+from PySDM.physics import si
+
+
+@strict
+class Settings:
+ def __init__(
+ self,
+ *,
+ aerosol: str,
+ vertical_velocity: float,
+ dt: float,
+ n_sd: int,
+ initial_temperature: float = 283 * si.K,
+ initial_pressure: float = 900 * si.mbar,
+ initial_relative_humidity: float = 0.97,
+ displacement: float = 1000 * si.m,
+ mass_accommodation_coefficient: float = 0.3,
+ ):
+ self.formulae = Formulae(constants={"MAC": mass_accommodation_coefficient})
+ self.n_sd = n_sd
+ self.aerosol_modes_by_kappa = {
+ "pristine": {
+ 1.28: Sum(
+ (
+ Lognormal(
+ norm_factor=125 / si.cm**3, m_mode=11 * si.nm, s_geom=1.2
+ ),
+ Lognormal(
+ norm_factor=65 / si.cm**3, m_mode=60 * si.nm, s_geom=1.7
+ ),
+ )
+ )
+ },
+ "polluted": {
+ 1.28: Sum(
+ (
+ Lognormal(
+ norm_factor=160 / si.cm**3, m_mode=29 * si.nm, s_geom=1.36
+ ),
+ Lognormal(
+ norm_factor=380 / si.cm**3, m_mode=71 * si.nm, s_geom=1.57
+ ),
+ )
+ )
+ },
+ }[aerosol]
+
+ const = self.formulae.constants
+ self.vertical_velocity = vertical_velocity
+ self.initial_pressure = initial_pressure
+ self.initial_temperature = initial_temperature
+ pv0 = (
+ initial_relative_humidity
+ * self.formulae.saturation_vapour_pressure.pvs_Celsius(
+ initial_temperature - const.T0
+ )
+ )
+ self.initial_vapour_mixing_ratio = const.eps * pv0 / (initial_pressure - pv0)
+ self.t_max = displacement / vertical_velocity
+ self.timestep = dt
+ self.output_interval = self.timestep
+
+ @property
+ def initial_air_density(self):
+ const = self.formulae.constants
+ dry_air_density = (
+ self.formulae.trivia.p_d(
+ self.initial_pressure, self.initial_vapour_mixing_ratio
+ )
+ / self.initial_temperature
+ / const.Rd
+ )
+ return dry_air_density * (1 + self.initial_vapour_mixing_ratio)
+
+ @property
+ def nt(self) -> int:
+ nt = self.t_max / self.timestep
+ nt_int = round(nt)
+ np.testing.assert_almost_equal(nt, nt_int)
+ return nt_int
+
+ @property
+ def steps_per_output_interval(self) -> int:
+ return int(self.output_interval / self.timestep)
+
+ @property
+ def output_steps(self) -> np.ndarray:
+ return np.arange(0, self.nt + 1, self.steps_per_output_interval)
+
+
+
+
+
+
+
+
+
+class Settings
+( *, aerosol: str, vertical_velocity: float, dt: float, n_sd: int, initial_temperature: float = 283.0, initial_pressure: float = 90000.0, initial_relative_humidity: float = 0.97, displacement: float = 1000.0, mass_accommodation_coefficient: float = 0.3)
+
+
+
+
+
+Expand source code
+
+@strict
+class Settings:
+ def __init__(
+ self,
+ *,
+ aerosol: str,
+ vertical_velocity: float,
+ dt: float,
+ n_sd: int,
+ initial_temperature: float = 283 * si.K,
+ initial_pressure: float = 900 * si.mbar,
+ initial_relative_humidity: float = 0.97,
+ displacement: float = 1000 * si.m,
+ mass_accommodation_coefficient: float = 0.3,
+ ):
+ self.formulae = Formulae(constants={"MAC": mass_accommodation_coefficient})
+ self.n_sd = n_sd
+ self.aerosol_modes_by_kappa = {
+ "pristine": {
+ 1.28: Sum(
+ (
+ Lognormal(
+ norm_factor=125 / si.cm**3, m_mode=11 * si.nm, s_geom=1.2
+ ),
+ Lognormal(
+ norm_factor=65 / si.cm**3, m_mode=60 * si.nm, s_geom=1.7
+ ),
+ )
+ )
+ },
+ "polluted": {
+ 1.28: Sum(
+ (
+ Lognormal(
+ norm_factor=160 / si.cm**3, m_mode=29 * si.nm, s_geom=1.36
+ ),
+ Lognormal(
+ norm_factor=380 / si.cm**3, m_mode=71 * si.nm, s_geom=1.57
+ ),
+ )
+ )
+ },
+ }[aerosol]
+
+ const = self.formulae.constants
+ self.vertical_velocity = vertical_velocity
+ self.initial_pressure = initial_pressure
+ self.initial_temperature = initial_temperature
+ pv0 = (
+ initial_relative_humidity
+ * self.formulae.saturation_vapour_pressure.pvs_Celsius(
+ initial_temperature - const.T0
+ )
+ )
+ self.initial_vapour_mixing_ratio = const.eps * pv0 / (initial_pressure - pv0)
+ self.t_max = displacement / vertical_velocity
+ self.timestep = dt
+ self.output_interval = self.timestep
+
+ @property
+ def initial_air_density(self):
+ const = self.formulae.constants
+ dry_air_density = (
+ self.formulae.trivia.p_d(
+ self.initial_pressure, self.initial_vapour_mixing_ratio
+ )
+ / self.initial_temperature
+ / const.Rd
+ )
+ return dry_air_density * (1 + self.initial_vapour_mixing_ratio)
+
+ @property
+ def nt(self) -> int:
+ nt = self.t_max / self.timestep
+ nt_int = round(nt)
+ np.testing.assert_almost_equal(nt, nt_int)
+ return nt_int
+
+ @property
+ def steps_per_output_interval(self) -> int:
+ return int(self.output_interval / self.timestep)
+
+ @property
+ def output_steps(self) -> np.ndarray:
+ return np.arange(0, self.nt + 1, self.steps_per_output_interval)
+
+Instance variables
+
+var initial_air_density
+
+
+
+
+Expand source code
+
+@property
+def initial_air_density(self):
+ const = self.formulae.constants
+ dry_air_density = (
+ self.formulae.trivia.p_d(
+ self.initial_pressure, self.initial_vapour_mixing_ratio
+ )
+ / self.initial_temperature
+ / const.Rd
+ )
+ return dry_air_density * (1 + self.initial_vapour_mixing_ratio)
+
+
+var nt : int
+
+
+
+
+Expand source code
+
+@property
+def nt(self) -> int:
+ nt = self.t_max / self.timestep
+ nt_int = round(nt)
+ np.testing.assert_almost_equal(nt, nt_int)
+ return nt_int
+
+
+var output_steps : numpy.ndarray
+
+
+
+
+Expand source code
+
+@property
+def output_steps(self) -> np.ndarray:
+ return np.arange(0, self.nt + 1, self.steps_per_output_interval)
+
+
+var steps_per_output_interval : int
+
+
+
+
+Expand source code
+
+@property
+def steps_per_output_interval(self) -> int:
+ return int(self.output_interval / self.timestep)
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.html b/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.html
new file mode 100644
index 000000000..8e6c7d620
--- /dev/null
+++ b/PySDM_examples/Grabowski_and_Pawlowska_2023/simulation.html
@@ -0,0 +1,323 @@
+
+
+
+
+
+
+PySDM_examples.Grabowski_and_Pawlowska_2023.simulation API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Module PySDM_examples.Grabowski_and_Pawlowska_2023.simulation
+
+
+
+
+Expand source code
+
+import numpy as np
+from PySDM_examples.utils import BasicSimulation
+
+from PySDM import Builder
+from PySDM.backends import CPU
+from PySDM.backends.impl_numba.test_helpers import scipy_ode_condensation_solver
+from PySDM.dynamics import AmbientThermodynamics, Condensation
+from PySDM.environments import Parcel
+from PySDM.initialisation import equilibrate_wet_radii
+from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity
+from PySDM.physics import si
+
+
+class Simulation(BasicSimulation):
+ def __init__(
+ self,
+ settings,
+ products=None,
+ scipy_solver=False,
+ sampling_class=ConstantMultiplicity,
+ ):
+ env = Parcel(
+ dt=settings.timestep,
+ p0=settings.initial_pressure,
+ q0=settings.initial_vapour_mixing_ratio,
+ T0=settings.initial_temperature,
+ w=settings.vertical_velocity,
+ mass_of_dry_air=44 * si.kg,
+ )
+ builder = Builder(n_sd=settings.n_sd, backend=CPU(formulae=settings.formulae))
+ builder.set_environment(env)
+ builder.add_dynamic(AmbientThermodynamics())
+ builder.add_dynamic(Condensation())
+ for attribute in (
+ "critical supersaturation",
+ "equilibrium supersaturation",
+ "critical volume",
+ ):
+ builder.request_attribute(attribute)
+
+ volume = env.mass_of_dry_air / settings.initial_air_density
+ attributes = {
+ k: np.empty(0)
+ for k in ("dry volume", "kappa times dry volume", "multiplicity", "kappa")
+ }
+
+ assert len(settings.aerosol_modes_by_kappa.keys()) == 1
+ kappa = tuple(settings.aerosol_modes_by_kappa.keys())[0]
+ spectrum = settings.aerosol_modes_by_kappa[kappa]
+
+ r_dry, n_per_volume = sampling_class(spectrum).sample(settings.n_sd)
+ v_dry = settings.formulae.trivia.volume(radius=r_dry)
+ attributes["multiplicity"] = np.append(
+ attributes["multiplicity"], n_per_volume * volume
+ )
+ attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
+ attributes["kappa times dry volume"] = np.append(
+ attributes["kappa times dry volume"], v_dry * kappa
+ )
+ attributes["kappa"] = np.append(
+ attributes["kappa"], np.full(settings.n_sd, kappa)
+ )
+ r_wet = equilibrate_wet_radii(
+ r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
+ environment=env,
+ kappa_times_dry_volume=attributes["kappa times dry volume"],
+ )
+ attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
+
+ super().__init__(
+ particulator=builder.build(attributes=attributes, products=products)
+ )
+ if scipy_solver:
+ scipy_ode_condensation_solver.patch_particulator(self.particulator)
+
+ self.output_attributes = {
+ "volume": tuple([] for _ in range(self.particulator.n_sd)),
+ "dry volume": tuple([] for _ in range(self.particulator.n_sd)),
+ "critical supersaturation": tuple(
+ [] for _ in range(self.particulator.n_sd)
+ ),
+ "equilibrium supersaturation": tuple(
+ [] for _ in range(self.particulator.n_sd)
+ ),
+ "critical volume": tuple([] for _ in range(self.particulator.n_sd)),
+ "multiplicity": tuple([] for _ in range(self.particulator.n_sd)),
+ }
+ self.settings = settings
+
+ self.__sanity_checks(attributes, volume)
+
+ def __sanity_checks(self, attributes, volume):
+ for attribute in attributes.values():
+ assert attribute.shape[0] == self.particulator.n_sd
+ np.testing.assert_approx_equal(
+ sum(attributes["multiplicity"]) / volume,
+ sum(
+ mode.norm_factor
+ for mode in self.settings.aerosol_modes_by_kappa.values()
+ ),
+ significant=4,
+ )
+
+ def _save(self, output):
+ for key, attr in self.output_attributes.items():
+ attr_data = self.particulator.attributes[key].to_ndarray()
+ for drop_id in range(self.particulator.n_sd):
+ attr[drop_id].append(attr_data[drop_id])
+ super()._save(output)
+
+ def run(self):
+ output_products = super()._run(
+ self.settings.nt, self.settings.steps_per_output_interval
+ )
+ return {"products": output_products, "attributes": self.output_attributes}
+
+
+
+
+
+
+
+
+
+class Simulation
+( settings, products=None, scipy_solver=False, sampling_class=PySDM.initialisation.sampling.spectral_sampling.ConstantMultiplicity)
+
+
+
+
+
+Expand source code
+
+class Simulation(BasicSimulation):
+ def __init__(
+ self,
+ settings,
+ products=None,
+ scipy_solver=False,
+ sampling_class=ConstantMultiplicity,
+ ):
+ env = Parcel(
+ dt=settings.timestep,
+ p0=settings.initial_pressure,
+ q0=settings.initial_vapour_mixing_ratio,
+ T0=settings.initial_temperature,
+ w=settings.vertical_velocity,
+ mass_of_dry_air=44 * si.kg,
+ )
+ builder = Builder(n_sd=settings.n_sd, backend=CPU(formulae=settings.formulae))
+ builder.set_environment(env)
+ builder.add_dynamic(AmbientThermodynamics())
+ builder.add_dynamic(Condensation())
+ for attribute in (
+ "critical supersaturation",
+ "equilibrium supersaturation",
+ "critical volume",
+ ):
+ builder.request_attribute(attribute)
+
+ volume = env.mass_of_dry_air / settings.initial_air_density
+ attributes = {
+ k: np.empty(0)
+ for k in ("dry volume", "kappa times dry volume", "multiplicity", "kappa")
+ }
+
+ assert len(settings.aerosol_modes_by_kappa.keys()) == 1
+ kappa = tuple(settings.aerosol_modes_by_kappa.keys())[0]
+ spectrum = settings.aerosol_modes_by_kappa[kappa]
+
+ r_dry, n_per_volume = sampling_class(spectrum).sample(settings.n_sd)
+ v_dry = settings.formulae.trivia.volume(radius=r_dry)
+ attributes["multiplicity"] = np.append(
+ attributes["multiplicity"], n_per_volume * volume
+ )
+ attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
+ attributes["kappa times dry volume"] = np.append(
+ attributes["kappa times dry volume"], v_dry * kappa
+ )
+ attributes["kappa"] = np.append(
+ attributes["kappa"], np.full(settings.n_sd, kappa)
+ )
+ r_wet = equilibrate_wet_radii(
+ r_dry=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
+ environment=env,
+ kappa_times_dry_volume=attributes["kappa times dry volume"],
+ )
+ attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
+
+ super().__init__(
+ particulator=builder.build(attributes=attributes, products=products)
+ )
+ if scipy_solver:
+ scipy_ode_condensation_solver.patch_particulator(self.particulator)
+
+ self.output_attributes = {
+ "volume": tuple([] for _ in range(self.particulator.n_sd)),
+ "dry volume": tuple([] for _ in range(self.particulator.n_sd)),
+ "critical supersaturation": tuple(
+ [] for _ in range(self.particulator.n_sd)
+ ),
+ "equilibrium supersaturation": tuple(
+ [] for _ in range(self.particulator.n_sd)
+ ),
+ "critical volume": tuple([] for _ in range(self.particulator.n_sd)),
+ "multiplicity": tuple([] for _ in range(self.particulator.n_sd)),
+ }
+ self.settings = settings
+
+ self.__sanity_checks(attributes, volume)
+
+ def __sanity_checks(self, attributes, volume):
+ for attribute in attributes.values():
+ assert attribute.shape[0] == self.particulator.n_sd
+ np.testing.assert_approx_equal(
+ sum(attributes["multiplicity"]) / volume,
+ sum(
+ mode.norm_factor
+ for mode in self.settings.aerosol_modes_by_kappa.values()
+ ),
+ significant=4,
+ )
+
+ def _save(self, output):
+ for key, attr in self.output_attributes.items():
+ attr_data = self.particulator.attributes[key].to_ndarray()
+ for drop_id in range(self.particulator.n_sd):
+ attr[drop_id].append(attr_data[drop_id])
+ super()._save(output)
+
+ def run(self):
+ output_products = super()._run(
+ self.settings.nt, self.settings.steps_per_output_interval
+ )
+ return {"products": output_products, "attributes": self.output_attributes}
+
+Ancestors
+
+Methods
+
+
+def run (self)
+
+
+
+
+
+Expand source code
+
+def run(self):
+ output_products = super()._run(
+ self.settings.nt, self.settings.steps_per_output_interval
+ )
+ return {"products": output_products, "attributes": self.output_attributes}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PySDM_examples/Yang_et_al_2018/simulation.html b/PySDM_examples/Yang_et_al_2018/simulation.html
index aa9b31e5f..16864f752 100644
--- a/PySDM_examples/Yang_et_al_2018/simulation.html
+++ b/PySDM_examples/Yang_et_al_2018/simulation.html
@@ -26,11 +26,14 @@ Module PySDM_examples.Yang_et_al_2018.simulation
<
Expand source code
-import PySDM.products as PySDM_products
+import numpy as np
+
+import PySDM.products as PySDM_products
from PySDM import Builder, Formulae
from PySDM.backends import CPU
from PySDM.dynamics import AmbientThermodynamics, Condensation
from PySDM.environments import Parcel
+from PySDM.physics import si
class Simulation:
@@ -77,6 +80,12 @@ Module PySDM_examples.Yang_et_al_2018.simulation
<
PySDM_products.CondensationTimestepMin(name="dt_cond_min"),
PySDM_products.CondensationTimestepMax(name="dt_cond_max"),
PySDM_products.RipeningRate(),
+ PySDM_products.MeanRadius(
+ name="r_mean_gt_1_um", radius_range=(1 * si.um, np.inf)
+ ),
+ PySDM_products.ActivatedMeanRadius(
+ name="r_act", count_activated=True, count_unactivated=False
+ ),
]
attributes = environment.init_attributes(
@@ -96,28 +105,34 @@ Module PySDM_examples.Yang_et_al_2018.simulation
<
volume = _sp.attributes["volume"].to_ndarray()
output["r"].append(self.formulae.trivia.radius(volume=volume))
output["S"].append(_sp.environment["RH"][cell_id] - 1)
- output["qv"].append(_sp.environment["qv"][cell_id])
- output["T"].append(_sp.environment["T"][cell_id])
- output["z"].append(_sp.environment["z"][cell_id])
- output["t"].append(_sp.environment["t"][cell_id])
- output["dt_cond_max"].append(_sp.products["dt_cond_max"].get()[cell_id].copy())
- output["dt_cond_min"].append(_sp.products["dt_cond_min"].get()[cell_id].copy())
- output["ripening rate"].append(
- _sp.products["ripening rate"].get()[cell_id].copy()
- )
+ for key in ("qv", "T", "z", "t"):
+ output[key].append(_sp.environment[key][cell_id])
+ for key in (
+ "dt_cond_max",
+ "dt_cond_min",
+ "ripening rate",
+ "r_mean_gt_1_um",
+ "r_act",
+ ):
+ output[key].append(_sp.products[key].get()[cell_id].copy())
def run(self):
output = {
- "r": [],
- "S": [],
- "z": [],
- "t": [],
- "qv": [],
- "T": [],
- "r_bins_values": [],
- "dt_cond_max": [],
- "dt_cond_min": [],
- "ripening rate": [],
+ key: []
+ for key in (
+ "r",
+ "S",
+ "z",
+ "t",
+ "qv",
+ "T",
+ "r_bins_values",
+ "dt_cond_max",
+ "dt_cond_min",
+ "ripening rate",
+ "r_mean_gt_1_um",
+ "r_act",
+ )
}
self.save(output)
@@ -190,6 +205,12 @@
PySDM_products.CondensationTimestepMin(name="dt_cond_min"),
PySDM_products.CondensationTimestepMax(name="dt_cond_max"),
PySDM_products.RipeningRate(),
+ PySDM_products.MeanRadius(
+ name="r_mean_gt_1_um", radius_range=(1 * si.um, np.inf)
+ ),
+ PySDM_products.ActivatedMeanRadius(
+ name="r_act", count_activated=True, count_unactivated=False
+ ),
]
attributes = environment.init_attributes(
@@ -209,28 +230,34 @@
volume = _sp.attributes["volume"].to_ndarray()
output["r"].append(self.formulae.trivia.radius(volume=volume))
output["S"].append(_sp.environment["RH"][cell_id] - 1)
- output["qv"].append(_sp.environment["qv"][cell_id])
- output["T"].append(_sp.environment["T"][cell_id])
- output["z"].append(_sp.environment["z"][cell_id])
- output["t"].append(_sp.environment["t"][cell_id])
- output["dt_cond_max"].append(_sp.products["dt_cond_max"].get()[cell_id].copy())
- output["dt_cond_min"].append(_sp.products["dt_cond_min"].get()[cell_id].copy())
- output["ripening rate"].append(
- _sp.products["ripening rate"].get()[cell_id].copy()
- )
+ for key in ("qv", "T", "z", "t"):
+ output[key].append(_sp.environment[key][cell_id])
+ for key in (
+ "dt_cond_max",
+ "dt_cond_min",
+ "ripening rate",
+ "r_mean_gt_1_um",
+ "r_act",
+ ):
+ output[key].append(_sp.products[key].get()[cell_id].copy())
def run(self):
output = {
- "r": [],
- "S": [],
- "z": [],
- "t": [],
- "qv": [],
- "T": [],
- "r_bins_values": [],
- "dt_cond_max": [],
- "dt_cond_min": [],
- "ripening rate": [],
+ key: []
+ for key in (
+ "r",
+ "S",
+ "z",
+ "t",
+ "qv",
+ "T",
+ "r_bins_values",
+ "dt_cond_max",
+ "dt_cond_min",
+ "ripening rate",
+ "r_mean_gt_1_um",
+ "r_act",
+ )
}
self.save(output)
@@ -252,16 +279,21 @@ Methods
def run(self):
output = {
- "r": [],
- "S": [],
- "z": [],
- "t": [],
- "qv": [],
- "T": [],
- "r_bins_values": [],
- "dt_cond_max": [],
- "dt_cond_min": [],
- "ripening rate": [],
+ key: []
+ for key in (
+ "r",
+ "S",
+ "z",
+ "t",
+ "qv",
+ "T",
+ "r_bins_values",
+ "dt_cond_max",
+ "dt_cond_min",
+ "ripening rate",
+ "r_mean_gt_1_um",
+ "r_act",
+ )
}
self.save(output)
@@ -289,15 +321,16 @@ Methods
volume = _sp.attributes["volume"].to_ndarray()
output["r"].append(self.formulae.trivia.radius(volume=volume))
output["S"].append(_sp.environment["RH"][cell_id] - 1)
- output["qv"].append(_sp.environment["qv"][cell_id])
- output["T"].append(_sp.environment["T"][cell_id])
- output["z"].append(_sp.environment["z"][cell_id])
- output["t"].append(_sp.environment["t"][cell_id])
- output["dt_cond_max"].append(_sp.products["dt_cond_max"].get()[cell_id].copy())
- output["dt_cond_min"].append(_sp.products["dt_cond_min"].get()[cell_id].copy())
- output["ripening rate"].append(
- _sp.products["ripening rate"].get()[cell_id].copy()
- )
+ for key in ("qv", "T", "z", "t"):
+ output[key].append(_sp.environment[key][cell_id])
+ for key in (
+ "dt_cond_max",
+ "dt_cond_min",
+ "ripening rate",
+ "r_mean_gt_1_um",
+ "r_act",
+ ):
+ output[key].append(_sp.products[key].get()[cell_id].copy())
diff --git a/PySDM_examples/index.html b/PySDM_examples/index.html
index 6496b6d83..c516d648f 100644
--- a/PySDM_examples/index.html
+++ b/PySDM_examples/index.html
@@ -83,6 +83,10 @@