Module PySDM.builder

The Builder class handling creation of Particulator instances

Expand source code
"""
The Builder class handling creation of  `PySDM.particulator.Particulator` instances
"""

import inspect
import warnings

import numpy as np

from PySDM.attributes.impl.mapper import get_class as attr_class
from PySDM.attributes.numerics.cell_id import CellID
from PySDM.attributes.physics import WaterMass
from PySDM.attributes.physics.multiplicities import Multiplicities
from PySDM.impl.particle_attributes_factory import ParticleAttributesFactory
from PySDM.impl.wall_timer import WallTimer
from PySDM.initialisation.discretise_multiplicities import (  # TODO #324
    discretise_multiplicities,
)
from PySDM.particulator import Particulator
from PySDM.physics.particle_shape_and_density import LiquidSpheres, MixedPhaseSpheres


def _warn_env_as_ctor_arg():
    warnings.warn(
        "PySDM > v2.31 Builder expects environment instance as argument",
        DeprecationWarning,
    )


class Builder:
    def __init__(self, n_sd, backend, environment=None):
        assert not inspect.isclass(backend)
        self.formulae = backend.formulae
        self.particulator = Particulator(n_sd, backend)
        self.req_attr = {
            "multiplicity": Multiplicities(self),
            "water mass": WaterMass(self),
            "cell id": CellID(self),
        }
        self.aerosol_radius_threshold = 0
        self.condensation_params = None

        if environment is None:
            _warn_env_as_ctor_arg()
        else:
            self._set_environment(environment)

    def _set_condensation_parameters(self, **kwargs):
        self.condensation_params = kwargs

    def set_environment(self, environment):
        _warn_env_as_ctor_arg()
        self._set_environment(environment)

    def _set_environment(self, environment):
        if self.particulator.environment is not None:
            raise AssertionError("environment has already been set")
        self.particulator.environment = environment
        self.particulator.environment.register(self)

    def add_dynamic(self, dynamic):
        assert self.particulator.environment is not None
        key = get_key(dynamic)
        assert key not in self.particulator.dynamics
        self.particulator.dynamics[key] = dynamic

    def replace_dynamic(self, dynamic):
        key = get_key(dynamic)
        assert key in self.particulator.dynamics
        self.particulator.dynamics.pop(key)
        self.add_dynamic(dynamic)

    def register_product(self, product, buffer):
        if product.name in self.particulator.products:
            raise ValueError(f'product name "{product.name}" already registered')
        product.set_buffer(buffer)
        product.register(self)
        self.particulator.products[product.name] = product

    def get_attribute(self, attribute_name):
        self.request_attribute(attribute_name)
        return self.req_attr[attribute_name]

    def request_attribute(self, attribute, variant=None):
        if attribute not in self.req_attr:
            self.req_attr[attribute] = attr_class(
                attribute, self.particulator.dynamics, self.formulae
            )(self)
        if variant is not None:
            assert variant == self.req_attr[attribute]

    def build(
        self,
        attributes: dict,
        products: tuple = (),
        int_caster=discretise_multiplicities,
    ):
        assert self.particulator.environment is not None

        if "n" in attributes and "multiplicity" not in attributes:
            attributes["multiplicity"] = attributes["n"]
            del attributes["n"]
            warnings.warn(
                'renaming attributes["n"] to attributes["multiplicity"]',
                DeprecationWarning,
            )

        if "volume" in attributes and "water mass" not in attributes:
            assert self.particulator.formulae.particle_shape_and_density.__name__ in (
                LiquidSpheres.__name__,
                MixedPhaseSpheres.__name__,
            ), "implied volume-to-mass conversion is only supported for spherical particles"
            attributes["water mass"] = (
                self.particulator.formulae.particle_shape_and_density.volume_to_mass(
                    attributes["volume"]
                )
            )
            del attributes["volume"]
            self.request_attribute("volume")

        for dynamic in self.particulator.dynamics.values():
            dynamic.register(self)

        single_buffer_for_all_products = np.empty(self.particulator.mesh.grid)
        for product in products:
            self.register_product(product, single_buffer_for_all_products)

        for attribute in attributes:
            self.request_attribute(attribute)
        if "Condensation" in self.particulator.dynamics:
            self.particulator.condensation_solver = (
                self.particulator.backend.make_condensation_solver(
                    self.particulator.dt,
                    self.particulator.mesh.n_cell,
                    **self.condensation_params,
                )
            )
        attributes["multiplicity"] = int_caster(attributes["multiplicity"])
        if self.particulator.mesh.dimension == 0:
            attributes["cell id"] = np.zeros_like(
                attributes["multiplicity"], dtype=np.int64
            )
        self.particulator.attributes = ParticleAttributesFactory.attributes(
            self.particulator, self.req_attr, attributes
        )
        self.particulator.recalculate_cell_id()

        for key in self.particulator.dynamics:
            self.particulator.timers[key] = WallTimer()

        return self.particulator


def get_key(dynamic):
    return inspect.getmro(type(dynamic))[-2].__name__

Functions

def get_key(dynamic)
Expand source code
def get_key(dynamic):
    return inspect.getmro(type(dynamic))[-2].__name__

Classes

class Builder (n_sd, backend, environment=None)
Expand source code
class Builder:
    def __init__(self, n_sd, backend, environment=None):
        assert not inspect.isclass(backend)
        self.formulae = backend.formulae
        self.particulator = Particulator(n_sd, backend)
        self.req_attr = {
            "multiplicity": Multiplicities(self),
            "water mass": WaterMass(self),
            "cell id": CellID(self),
        }
        self.aerosol_radius_threshold = 0
        self.condensation_params = None

        if environment is None:
            _warn_env_as_ctor_arg()
        else:
            self._set_environment(environment)

    def _set_condensation_parameters(self, **kwargs):
        self.condensation_params = kwargs

    def set_environment(self, environment):
        _warn_env_as_ctor_arg()
        self._set_environment(environment)

    def _set_environment(self, environment):
        if self.particulator.environment is not None:
            raise AssertionError("environment has already been set")
        self.particulator.environment = environment
        self.particulator.environment.register(self)

    def add_dynamic(self, dynamic):
        assert self.particulator.environment is not None
        key = get_key(dynamic)
        assert key not in self.particulator.dynamics
        self.particulator.dynamics[key] = dynamic

    def replace_dynamic(self, dynamic):
        key = get_key(dynamic)
        assert key in self.particulator.dynamics
        self.particulator.dynamics.pop(key)
        self.add_dynamic(dynamic)

    def register_product(self, product, buffer):
        if product.name in self.particulator.products:
            raise ValueError(f'product name "{product.name}" already registered')
        product.set_buffer(buffer)
        product.register(self)
        self.particulator.products[product.name] = product

    def get_attribute(self, attribute_name):
        self.request_attribute(attribute_name)
        return self.req_attr[attribute_name]

    def request_attribute(self, attribute, variant=None):
        if attribute not in self.req_attr:
            self.req_attr[attribute] = attr_class(
                attribute, self.particulator.dynamics, self.formulae
            )(self)
        if variant is not None:
            assert variant == self.req_attr[attribute]

    def build(
        self,
        attributes: dict,
        products: tuple = (),
        int_caster=discretise_multiplicities,
    ):
        assert self.particulator.environment is not None

        if "n" in attributes and "multiplicity" not in attributes:
            attributes["multiplicity"] = attributes["n"]
            del attributes["n"]
            warnings.warn(
                'renaming attributes["n"] to attributes["multiplicity"]',
                DeprecationWarning,
            )

        if "volume" in attributes and "water mass" not in attributes:
            assert self.particulator.formulae.particle_shape_and_density.__name__ in (
                LiquidSpheres.__name__,
                MixedPhaseSpheres.__name__,
            ), "implied volume-to-mass conversion is only supported for spherical particles"
            attributes["water mass"] = (
                self.particulator.formulae.particle_shape_and_density.volume_to_mass(
                    attributes["volume"]
                )
            )
            del attributes["volume"]
            self.request_attribute("volume")

        for dynamic in self.particulator.dynamics.values():
            dynamic.register(self)

        single_buffer_for_all_products = np.empty(self.particulator.mesh.grid)
        for product in products:
            self.register_product(product, single_buffer_for_all_products)

        for attribute in attributes:
            self.request_attribute(attribute)
        if "Condensation" in self.particulator.dynamics:
            self.particulator.condensation_solver = (
                self.particulator.backend.make_condensation_solver(
                    self.particulator.dt,
                    self.particulator.mesh.n_cell,
                    **self.condensation_params,
                )
            )
        attributes["multiplicity"] = int_caster(attributes["multiplicity"])
        if self.particulator.mesh.dimension == 0:
            attributes["cell id"] = np.zeros_like(
                attributes["multiplicity"], dtype=np.int64
            )
        self.particulator.attributes = ParticleAttributesFactory.attributes(
            self.particulator, self.req_attr, attributes
        )
        self.particulator.recalculate_cell_id()

        for key in self.particulator.dynamics:
            self.particulator.timers[key] = WallTimer()

        return self.particulator

Methods

def add_dynamic(self, dynamic)
Expand source code
def add_dynamic(self, dynamic):
    assert self.particulator.environment is not None
    key = get_key(dynamic)
    assert key not in self.particulator.dynamics
    self.particulator.dynamics[key] = dynamic
def build(self, attributes: dict, products: tuple = (), int_caster=<function discretise_multiplicities>)
Expand source code
def build(
    self,
    attributes: dict,
    products: tuple = (),
    int_caster=discretise_multiplicities,
):
    assert self.particulator.environment is not None

    if "n" in attributes and "multiplicity" not in attributes:
        attributes["multiplicity"] = attributes["n"]
        del attributes["n"]
        warnings.warn(
            'renaming attributes["n"] to attributes["multiplicity"]',
            DeprecationWarning,
        )

    if "volume" in attributes and "water mass" not in attributes:
        assert self.particulator.formulae.particle_shape_and_density.__name__ in (
            LiquidSpheres.__name__,
            MixedPhaseSpheres.__name__,
        ), "implied volume-to-mass conversion is only supported for spherical particles"
        attributes["water mass"] = (
            self.particulator.formulae.particle_shape_and_density.volume_to_mass(
                attributes["volume"]
            )
        )
        del attributes["volume"]
        self.request_attribute("volume")

    for dynamic in self.particulator.dynamics.values():
        dynamic.register(self)

    single_buffer_for_all_products = np.empty(self.particulator.mesh.grid)
    for product in products:
        self.register_product(product, single_buffer_for_all_products)

    for attribute in attributes:
        self.request_attribute(attribute)
    if "Condensation" in self.particulator.dynamics:
        self.particulator.condensation_solver = (
            self.particulator.backend.make_condensation_solver(
                self.particulator.dt,
                self.particulator.mesh.n_cell,
                **self.condensation_params,
            )
        )
    attributes["multiplicity"] = int_caster(attributes["multiplicity"])
    if self.particulator.mesh.dimension == 0:
        attributes["cell id"] = np.zeros_like(
            attributes["multiplicity"], dtype=np.int64
        )
    self.particulator.attributes = ParticleAttributesFactory.attributes(
        self.particulator, self.req_attr, attributes
    )
    self.particulator.recalculate_cell_id()

    for key in self.particulator.dynamics:
        self.particulator.timers[key] = WallTimer()

    return self.particulator
def get_attribute(self, attribute_name)
Expand source code
def get_attribute(self, attribute_name):
    self.request_attribute(attribute_name)
    return self.req_attr[attribute_name]
def register_product(self, product, buffer)
Expand source code
def register_product(self, product, buffer):
    if product.name in self.particulator.products:
        raise ValueError(f'product name "{product.name}" already registered')
    product.set_buffer(buffer)
    product.register(self)
    self.particulator.products[product.name] = product
def replace_dynamic(self, dynamic)
Expand source code
def replace_dynamic(self, dynamic):
    key = get_key(dynamic)
    assert key in self.particulator.dynamics
    self.particulator.dynamics.pop(key)
    self.add_dynamic(dynamic)
def request_attribute(self, attribute, variant=None)
Expand source code
def request_attribute(self, attribute, variant=None):
    if attribute not in self.req_attr:
        self.req_attr[attribute] = attr_class(
            attribute, self.particulator.dynamics, self.formulae
        )(self)
    if variant is not None:
        assert variant == self.req_attr[attribute]
def set_environment(self, environment)
Expand source code
def set_environment(self, environment):
    _warn_env_as_ctor_arg()
    self._set_environment(environment)