PySDM_examples.utils.kinematic_2d.simulation

  1import numpy as np
  2from PySDM_examples.utils.kinematic_2d.make_default_product_collection import (
  3    make_default_product_collection,
  4)
  5from PySDM_examples.utils.kinematic_2d.mpdata_2d import MPDATA_2D
  6from PySDM_examples.utils import DummyController
  7
  8from PySDM.backends import CPU
  9from PySDM.builder import Builder
 10from PySDM.dynamics import (
 11    AmbientThermodynamics,
 12    Coalescence,
 13    Collision,
 14    Condensation,
 15    Displacement,
 16    EulerianAdvection,
 17    Freezing,
 18)
 19from PySDM.environments import Kinematic2D
 20from PySDM.initialisation.sampling import spatial_sampling
 21
 22
 23_BACKEND_CACHE = {}
 24
 25
 26def backend_factory(backend_class, formulae):
 27    key = backend_class.__name__ + ":" + str(formulae)
 28    if key not in _BACKEND_CACHE:
 29        _BACKEND_CACHE[key] = backend_class(formulae=formulae)
 30    return _BACKEND_CACHE[key]
 31
 32
 33class Simulation:
 34    def __init__(self, settings, storage, SpinUp, backend_class=CPU):
 35        self.settings = settings
 36        self.storage = storage
 37        self.particulator = None
 38        self.backend_class = backend_class
 39        self.SpinUp = SpinUp
 40
 41    @property
 42    def products(self):
 43        return self.particulator.products
 44
 45    def reinit(self, products=None):
 46        formulae = self.settings.formulae
 47        backend = backend_factory(self.backend_class, formulae=formulae)
 48        environment = Kinematic2D(
 49            dt=self.settings.dt,
 50            grid=self.settings.grid,
 51            size=self.settings.size,
 52            rhod_of=self.settings.rhod_of_zZ,
 53            mixed_phase=self.settings.processes["freezing"],
 54        )
 55        builder = Builder(
 56            n_sd=self.settings.n_sd, backend=backend, environment=environment
 57        )
 58
 59        if products is not None:
 60            products = list(products)
 61        else:
 62            products = make_default_product_collection(self.settings)
 63
 64        if self.settings.processes["fluid advection"]:
 65            builder.add_dynamic(AmbientThermodynamics())
 66        if self.settings.processes["condensation"]:
 67            kwargs = {}
 68            if not self.settings.condensation_adaptive:
 69                kwargs["substeps"] = (self.settings.condensation_substeps,)
 70            condensation = Condensation(
 71                rtol_x=self.settings.condensation_rtol_x,
 72                rtol_thd=self.settings.condensation_rtol_thd,
 73                adaptive=self.settings.condensation_adaptive,
 74                dt_cond_range=self.settings.condensation_dt_cond_range,
 75                schedule=self.settings.condensation_schedule,
 76                **kwargs,
 77            )
 78            builder.add_dynamic(condensation)
 79        if self.settings.processes["fluid advection"]:
 80            initial_profiles = {
 81                "th": self.settings.initial_dry_potential_temperature_profile,
 82                "water_vapour_mixing_ratio": self.settings.initial_vapour_mixing_ratio_profile,
 83            }
 84            advectees = dict(
 85                (
 86                    key,
 87                    np.repeat(
 88                        profile.reshape(1, -1),
 89                        builder.particulator.environment.mesh.grid[0],
 90                        axis=0,
 91                    ),
 92                )
 93                for key, profile in initial_profiles.items()
 94            )
 95            solver = MPDATA_2D(
 96                advectees=advectees,
 97                stream_function=self.settings.stream_function,
 98                rhod_of_zZ=self.settings.rhod_of_zZ,
 99                dt=self.settings.dt,
100                grid=self.settings.grid,
101                size=self.settings.size,
102                n_iters=self.settings.mpdata_iters,
103                infinite_gauge=self.settings.mpdata_iga,
104                nonoscillatory=self.settings.mpdata_fct,
105                third_order_terms=self.settings.mpdata_tot,
106            )
107            builder.add_dynamic(EulerianAdvection(solver))
108        if self.settings.processes["particle advection"]:
109            builder.add_dynamic(
110                Displacement(
111                    enable_sedimentation=self.settings.processes["sedimentation"],
112                    adaptive=self.settings.displacement_adaptive,
113                    rtol=self.settings.displacement_rtol,
114                )
115            )
116        if (
117            self.settings.processes["coalescence"]
118            and self.settings.processes["breakup"]
119        ):
120            builder.add_dynamic(
121                Collision(
122                    collision_kernel=self.settings.kernel,
123                    enable_breakup=self.settings.processes["breakup"],
124                    coalescence_efficiency=self.settings.coalescence_efficiency,
125                    breakup_efficiency=self.settings.breakup_efficiency,
126                    fragmentation_function=self.settings.breakup_fragmentation,
127                    adaptive=self.settings.coalescence_adaptive,
128                    dt_coal_range=self.settings.coalescence_dt_coal_range,
129                    substeps=self.settings.coalescence_substeps,
130                    optimized_random=self.settings.coalescence_optimized_random,
131                )
132            )
133        elif (
134            self.settings.processes["coalescence"]
135            and not self.settings.processes["breakup"]
136        ):
137            builder.add_dynamic(
138                Coalescence(
139                    collision_kernel=self.settings.kernel,
140                    adaptive=self.settings.coalescence_adaptive,
141                    dt_coal_range=self.settings.coalescence_dt_coal_range,
142                    substeps=self.settings.coalescence_substeps,
143                    optimized_random=self.settings.coalescence_optimized_random,
144                )
145            )
146        assert not (
147            self.settings.processes["breakup"]
148            and not self.settings.processes["coalescence"]
149        )
150        if self.settings.processes["freezing"]:
151            builder.add_dynamic(
152                Freezing(
153                    immersion_freezing=self.settings.freezing_immersion,
154                    thaw=self.settings.freezing_thaw,
155                )
156            )
157
158        attributes = builder.particulator.environment.init_attributes(
159            spatial_discretisation=spatial_sampling.Pseudorandom(),
160            dry_radius_spectrum=self.settings.spectrum_per_mass_of_dry_air,
161            kappa=self.settings.kappa,
162            n_sd=self.settings.n_sd
163            // (2 if self.settings.freezing_inp_frac != 1 else 1),
164        )
165
166        if self.settings.processes["freezing"]:
167            attributes["signed water mass"] = attributes.pop("water mass")
168
169            if self.settings.freezing_inp_spec is None:
170                immersed_surface_area = formulae.trivia.sphere_surface(
171                    diameter=2 * formulae.trivia.radius(volume=attributes["dry volume"])
172                )
173            else:
174                immersed_surface_area = self.settings.freezing_inp_spec.percentiles(
175                    np.random.random(attributes["dry volume"].size),  # TODO #599: seed
176                )
177
178            if self.settings.freezing_immersion == "singular":
179                attributes["freezing temperature"] = (
180                    formulae.freezing_temperature_spectrum.invcdf(
181                        np.random.random(immersed_surface_area.size),  # TODO #599: seed
182                        immersed_surface_area,
183                    )
184                )
185            else:
186                attributes["immersed surface area"] = immersed_surface_area
187
188            if self.settings.freezing_inp_frac != 1:
189                assert self.settings.n_sd % 2 == 0
190                assert 0 < self.settings.freezing_inp_frac < 1
191                freezing_attribute = {
192                    "singular": "freezing temperature",
193                    "time-dependent": "immersed surface area",
194                }[self.settings.freezing_immersion]
195                for name, array in attributes.items():
196                    if array.shape[-1] != self.settings.n_sd // 2:
197                        raise AssertionError(f"attribute >>{name}<< has wrong size")
198                    array = array.copy()
199                    full_shape = list(array.shape)
200                    orig = slice(None, full_shape[-1])
201                    copy = slice(orig.stop, None)
202                    full_shape[-1] *= 2
203                    attributes[name] = np.empty(full_shape, dtype=array.dtype)
204                    if name == freezing_attribute:
205                        attributes[name][orig] = array
206                        attributes[name][copy] = 0
207                    elif name == "multiplicity":
208                        attributes[name][orig] = array * self.settings.freezing_inp_frac
209                        attributes[name][copy] = array * (
210                            1 - self.settings.freezing_inp_frac
211                        )
212                    elif len(array.shape) > 1:  # particle positions
213                        # TODO #599: seed
214                        for dim, _ in enumerate(array.shape):
215                            # only to make particles not shadow each other in visualisations
216                            attributes[name][dim, orig] = array[dim, :]
217                            attributes[name][dim, copy] = np.random.permutation(
218                                array[dim, :]
219                            )
220                    else:
221                        attributes[name][orig] = array
222                        attributes[name][copy] = array
223
224                non_zero_per_gridbox = np.count_nonzero(
225                    attributes[freezing_attribute]
226                ) / np.prod(self.settings.grid)
227                assert non_zero_per_gridbox == self.settings.n_sd_per_gridbox / 2
228
229        self.particulator = builder.build(attributes, tuple(products))
230
231        if self.SpinUp is not None:
232            self.SpinUp(self.particulator, self.settings.n_spin_up)
233        if self.storage is not None:
234            self.storage.init(self.settings)
235
236    def run(self, controller=DummyController(), vtk_exporter=None):
237        with controller:
238            for step in self.settings.output_steps:
239                if controller.panic:
240                    break
241
242                self.particulator.run(step - self.particulator.n_steps)
243
244                self.store(step)
245
246                if vtk_exporter is not None:
247                    vtk_exporter.export_attributes(self.particulator)
248                    vtk_exporter.export_products(self.particulator)
249
250                controller.set_percent(step / self.settings.output_steps[-1])
251
252    def store(self, step):
253        for name, product in self.particulator.products.items():
254            self.storage.save(product.get(), step, name)
def backend_factory(backend_class, formulae):
27def backend_factory(backend_class, formulae):
28    key = backend_class.__name__ + ":" + str(formulae)
29    if key not in _BACKEND_CACHE:
30        _BACKEND_CACHE[key] = backend_class(formulae=formulae)
31    return _BACKEND_CACHE[key]
class Simulation:
 34class Simulation:
 35    def __init__(self, settings, storage, SpinUp, backend_class=CPU):
 36        self.settings = settings
 37        self.storage = storage
 38        self.particulator = None
 39        self.backend_class = backend_class
 40        self.SpinUp = SpinUp
 41
 42    @property
 43    def products(self):
 44        return self.particulator.products
 45
 46    def reinit(self, products=None):
 47        formulae = self.settings.formulae
 48        backend = backend_factory(self.backend_class, formulae=formulae)
 49        environment = Kinematic2D(
 50            dt=self.settings.dt,
 51            grid=self.settings.grid,
 52            size=self.settings.size,
 53            rhod_of=self.settings.rhod_of_zZ,
 54            mixed_phase=self.settings.processes["freezing"],
 55        )
 56        builder = Builder(
 57            n_sd=self.settings.n_sd, backend=backend, environment=environment
 58        )
 59
 60        if products is not None:
 61            products = list(products)
 62        else:
 63            products = make_default_product_collection(self.settings)
 64
 65        if self.settings.processes["fluid advection"]:
 66            builder.add_dynamic(AmbientThermodynamics())
 67        if self.settings.processes["condensation"]:
 68            kwargs = {}
 69            if not self.settings.condensation_adaptive:
 70                kwargs["substeps"] = (self.settings.condensation_substeps,)
 71            condensation = Condensation(
 72                rtol_x=self.settings.condensation_rtol_x,
 73                rtol_thd=self.settings.condensation_rtol_thd,
 74                adaptive=self.settings.condensation_adaptive,
 75                dt_cond_range=self.settings.condensation_dt_cond_range,
 76                schedule=self.settings.condensation_schedule,
 77                **kwargs,
 78            )
 79            builder.add_dynamic(condensation)
 80        if self.settings.processes["fluid advection"]:
 81            initial_profiles = {
 82                "th": self.settings.initial_dry_potential_temperature_profile,
 83                "water_vapour_mixing_ratio": self.settings.initial_vapour_mixing_ratio_profile,
 84            }
 85            advectees = dict(
 86                (
 87                    key,
 88                    np.repeat(
 89                        profile.reshape(1, -1),
 90                        builder.particulator.environment.mesh.grid[0],
 91                        axis=0,
 92                    ),
 93                )
 94                for key, profile in initial_profiles.items()
 95            )
 96            solver = MPDATA_2D(
 97                advectees=advectees,
 98                stream_function=self.settings.stream_function,
 99                rhod_of_zZ=self.settings.rhod_of_zZ,
100                dt=self.settings.dt,
101                grid=self.settings.grid,
102                size=self.settings.size,
103                n_iters=self.settings.mpdata_iters,
104                infinite_gauge=self.settings.mpdata_iga,
105                nonoscillatory=self.settings.mpdata_fct,
106                third_order_terms=self.settings.mpdata_tot,
107            )
108            builder.add_dynamic(EulerianAdvection(solver))
109        if self.settings.processes["particle advection"]:
110            builder.add_dynamic(
111                Displacement(
112                    enable_sedimentation=self.settings.processes["sedimentation"],
113                    adaptive=self.settings.displacement_adaptive,
114                    rtol=self.settings.displacement_rtol,
115                )
116            )
117        if (
118            self.settings.processes["coalescence"]
119            and self.settings.processes["breakup"]
120        ):
121            builder.add_dynamic(
122                Collision(
123                    collision_kernel=self.settings.kernel,
124                    enable_breakup=self.settings.processes["breakup"],
125                    coalescence_efficiency=self.settings.coalescence_efficiency,
126                    breakup_efficiency=self.settings.breakup_efficiency,
127                    fragmentation_function=self.settings.breakup_fragmentation,
128                    adaptive=self.settings.coalescence_adaptive,
129                    dt_coal_range=self.settings.coalescence_dt_coal_range,
130                    substeps=self.settings.coalescence_substeps,
131                    optimized_random=self.settings.coalescence_optimized_random,
132                )
133            )
134        elif (
135            self.settings.processes["coalescence"]
136            and not self.settings.processes["breakup"]
137        ):
138            builder.add_dynamic(
139                Coalescence(
140                    collision_kernel=self.settings.kernel,
141                    adaptive=self.settings.coalescence_adaptive,
142                    dt_coal_range=self.settings.coalescence_dt_coal_range,
143                    substeps=self.settings.coalescence_substeps,
144                    optimized_random=self.settings.coalescence_optimized_random,
145                )
146            )
147        assert not (
148            self.settings.processes["breakup"]
149            and not self.settings.processes["coalescence"]
150        )
151        if self.settings.processes["freezing"]:
152            builder.add_dynamic(
153                Freezing(
154                    immersion_freezing=self.settings.freezing_immersion,
155                    thaw=self.settings.freezing_thaw,
156                )
157            )
158
159        attributes = builder.particulator.environment.init_attributes(
160            spatial_discretisation=spatial_sampling.Pseudorandom(),
161            dry_radius_spectrum=self.settings.spectrum_per_mass_of_dry_air,
162            kappa=self.settings.kappa,
163            n_sd=self.settings.n_sd
164            // (2 if self.settings.freezing_inp_frac != 1 else 1),
165        )
166
167        if self.settings.processes["freezing"]:
168            attributes["signed water mass"] = attributes.pop("water mass")
169
170            if self.settings.freezing_inp_spec is None:
171                immersed_surface_area = formulae.trivia.sphere_surface(
172                    diameter=2 * formulae.trivia.radius(volume=attributes["dry volume"])
173                )
174            else:
175                immersed_surface_area = self.settings.freezing_inp_spec.percentiles(
176                    np.random.random(attributes["dry volume"].size),  # TODO #599: seed
177                )
178
179            if self.settings.freezing_immersion == "singular":
180                attributes["freezing temperature"] = (
181                    formulae.freezing_temperature_spectrum.invcdf(
182                        np.random.random(immersed_surface_area.size),  # TODO #599: seed
183                        immersed_surface_area,
184                    )
185                )
186            else:
187                attributes["immersed surface area"] = immersed_surface_area
188
189            if self.settings.freezing_inp_frac != 1:
190                assert self.settings.n_sd % 2 == 0
191                assert 0 < self.settings.freezing_inp_frac < 1
192                freezing_attribute = {
193                    "singular": "freezing temperature",
194                    "time-dependent": "immersed surface area",
195                }[self.settings.freezing_immersion]
196                for name, array in attributes.items():
197                    if array.shape[-1] != self.settings.n_sd // 2:
198                        raise AssertionError(f"attribute >>{name}<< has wrong size")
199                    array = array.copy()
200                    full_shape = list(array.shape)
201                    orig = slice(None, full_shape[-1])
202                    copy = slice(orig.stop, None)
203                    full_shape[-1] *= 2
204                    attributes[name] = np.empty(full_shape, dtype=array.dtype)
205                    if name == freezing_attribute:
206                        attributes[name][orig] = array
207                        attributes[name][copy] = 0
208                    elif name == "multiplicity":
209                        attributes[name][orig] = array * self.settings.freezing_inp_frac
210                        attributes[name][copy] = array * (
211                            1 - self.settings.freezing_inp_frac
212                        )
213                    elif len(array.shape) > 1:  # particle positions
214                        # TODO #599: seed
215                        for dim, _ in enumerate(array.shape):
216                            # only to make particles not shadow each other in visualisations
217                            attributes[name][dim, orig] = array[dim, :]
218                            attributes[name][dim, copy] = np.random.permutation(
219                                array[dim, :]
220                            )
221                    else:
222                        attributes[name][orig] = array
223                        attributes[name][copy] = array
224
225                non_zero_per_gridbox = np.count_nonzero(
226                    attributes[freezing_attribute]
227                ) / np.prod(self.settings.grid)
228                assert non_zero_per_gridbox == self.settings.n_sd_per_gridbox / 2
229
230        self.particulator = builder.build(attributes, tuple(products))
231
232        if self.SpinUp is not None:
233            self.SpinUp(self.particulator, self.settings.n_spin_up)
234        if self.storage is not None:
235            self.storage.init(self.settings)
236
237    def run(self, controller=DummyController(), vtk_exporter=None):
238        with controller:
239            for step in self.settings.output_steps:
240                if controller.panic:
241                    break
242
243                self.particulator.run(step - self.particulator.n_steps)
244
245                self.store(step)
246
247                if vtk_exporter is not None:
248                    vtk_exporter.export_attributes(self.particulator)
249                    vtk_exporter.export_products(self.particulator)
250
251                controller.set_percent(step / self.settings.output_steps[-1])
252
253    def store(self, step):
254        for name, product in self.particulator.products.items():
255            self.storage.save(product.get(), step, name)
Simulation( settings, storage, SpinUp, backend_class=<class 'PySDM.backends.numba.Numba'>)
35    def __init__(self, settings, storage, SpinUp, backend_class=CPU):
36        self.settings = settings
37        self.storage = storage
38        self.particulator = None
39        self.backend_class = backend_class
40        self.SpinUp = SpinUp
settings
storage
particulator
backend_class
SpinUp
products
42    @property
43    def products(self):
44        return self.particulator.products
def reinit(self, products=None):
 46    def reinit(self, products=None):
 47        formulae = self.settings.formulae
 48        backend = backend_factory(self.backend_class, formulae=formulae)
 49        environment = Kinematic2D(
 50            dt=self.settings.dt,
 51            grid=self.settings.grid,
 52            size=self.settings.size,
 53            rhod_of=self.settings.rhod_of_zZ,
 54            mixed_phase=self.settings.processes["freezing"],
 55        )
 56        builder = Builder(
 57            n_sd=self.settings.n_sd, backend=backend, environment=environment
 58        )
 59
 60        if products is not None:
 61            products = list(products)
 62        else:
 63            products = make_default_product_collection(self.settings)
 64
 65        if self.settings.processes["fluid advection"]:
 66            builder.add_dynamic(AmbientThermodynamics())
 67        if self.settings.processes["condensation"]:
 68            kwargs = {}
 69            if not self.settings.condensation_adaptive:
 70                kwargs["substeps"] = (self.settings.condensation_substeps,)
 71            condensation = Condensation(
 72                rtol_x=self.settings.condensation_rtol_x,
 73                rtol_thd=self.settings.condensation_rtol_thd,
 74                adaptive=self.settings.condensation_adaptive,
 75                dt_cond_range=self.settings.condensation_dt_cond_range,
 76                schedule=self.settings.condensation_schedule,
 77                **kwargs,
 78            )
 79            builder.add_dynamic(condensation)
 80        if self.settings.processes["fluid advection"]:
 81            initial_profiles = {
 82                "th": self.settings.initial_dry_potential_temperature_profile,
 83                "water_vapour_mixing_ratio": self.settings.initial_vapour_mixing_ratio_profile,
 84            }
 85            advectees = dict(
 86                (
 87                    key,
 88                    np.repeat(
 89                        profile.reshape(1, -1),
 90                        builder.particulator.environment.mesh.grid[0],
 91                        axis=0,
 92                    ),
 93                )
 94                for key, profile in initial_profiles.items()
 95            )
 96            solver = MPDATA_2D(
 97                advectees=advectees,
 98                stream_function=self.settings.stream_function,
 99                rhod_of_zZ=self.settings.rhod_of_zZ,
100                dt=self.settings.dt,
101                grid=self.settings.grid,
102                size=self.settings.size,
103                n_iters=self.settings.mpdata_iters,
104                infinite_gauge=self.settings.mpdata_iga,
105                nonoscillatory=self.settings.mpdata_fct,
106                third_order_terms=self.settings.mpdata_tot,
107            )
108            builder.add_dynamic(EulerianAdvection(solver))
109        if self.settings.processes["particle advection"]:
110            builder.add_dynamic(
111                Displacement(
112                    enable_sedimentation=self.settings.processes["sedimentation"],
113                    adaptive=self.settings.displacement_adaptive,
114                    rtol=self.settings.displacement_rtol,
115                )
116            )
117        if (
118            self.settings.processes["coalescence"]
119            and self.settings.processes["breakup"]
120        ):
121            builder.add_dynamic(
122                Collision(
123                    collision_kernel=self.settings.kernel,
124                    enable_breakup=self.settings.processes["breakup"],
125                    coalescence_efficiency=self.settings.coalescence_efficiency,
126                    breakup_efficiency=self.settings.breakup_efficiency,
127                    fragmentation_function=self.settings.breakup_fragmentation,
128                    adaptive=self.settings.coalescence_adaptive,
129                    dt_coal_range=self.settings.coalescence_dt_coal_range,
130                    substeps=self.settings.coalescence_substeps,
131                    optimized_random=self.settings.coalescence_optimized_random,
132                )
133            )
134        elif (
135            self.settings.processes["coalescence"]
136            and not self.settings.processes["breakup"]
137        ):
138            builder.add_dynamic(
139                Coalescence(
140                    collision_kernel=self.settings.kernel,
141                    adaptive=self.settings.coalescence_adaptive,
142                    dt_coal_range=self.settings.coalescence_dt_coal_range,
143                    substeps=self.settings.coalescence_substeps,
144                    optimized_random=self.settings.coalescence_optimized_random,
145                )
146            )
147        assert not (
148            self.settings.processes["breakup"]
149            and not self.settings.processes["coalescence"]
150        )
151        if self.settings.processes["freezing"]:
152            builder.add_dynamic(
153                Freezing(
154                    immersion_freezing=self.settings.freezing_immersion,
155                    thaw=self.settings.freezing_thaw,
156                )
157            )
158
159        attributes = builder.particulator.environment.init_attributes(
160            spatial_discretisation=spatial_sampling.Pseudorandom(),
161            dry_radius_spectrum=self.settings.spectrum_per_mass_of_dry_air,
162            kappa=self.settings.kappa,
163            n_sd=self.settings.n_sd
164            // (2 if self.settings.freezing_inp_frac != 1 else 1),
165        )
166
167        if self.settings.processes["freezing"]:
168            attributes["signed water mass"] = attributes.pop("water mass")
169
170            if self.settings.freezing_inp_spec is None:
171                immersed_surface_area = formulae.trivia.sphere_surface(
172                    diameter=2 * formulae.trivia.radius(volume=attributes["dry volume"])
173                )
174            else:
175                immersed_surface_area = self.settings.freezing_inp_spec.percentiles(
176                    np.random.random(attributes["dry volume"].size),  # TODO #599: seed
177                )
178
179            if self.settings.freezing_immersion == "singular":
180                attributes["freezing temperature"] = (
181                    formulae.freezing_temperature_spectrum.invcdf(
182                        np.random.random(immersed_surface_area.size),  # TODO #599: seed
183                        immersed_surface_area,
184                    )
185                )
186            else:
187                attributes["immersed surface area"] = immersed_surface_area
188
189            if self.settings.freezing_inp_frac != 1:
190                assert self.settings.n_sd % 2 == 0
191                assert 0 < self.settings.freezing_inp_frac < 1
192                freezing_attribute = {
193                    "singular": "freezing temperature",
194                    "time-dependent": "immersed surface area",
195                }[self.settings.freezing_immersion]
196                for name, array in attributes.items():
197                    if array.shape[-1] != self.settings.n_sd // 2:
198                        raise AssertionError(f"attribute >>{name}<< has wrong size")
199                    array = array.copy()
200                    full_shape = list(array.shape)
201                    orig = slice(None, full_shape[-1])
202                    copy = slice(orig.stop, None)
203                    full_shape[-1] *= 2
204                    attributes[name] = np.empty(full_shape, dtype=array.dtype)
205                    if name == freezing_attribute:
206                        attributes[name][orig] = array
207                        attributes[name][copy] = 0
208                    elif name == "multiplicity":
209                        attributes[name][orig] = array * self.settings.freezing_inp_frac
210                        attributes[name][copy] = array * (
211                            1 - self.settings.freezing_inp_frac
212                        )
213                    elif len(array.shape) > 1:  # particle positions
214                        # TODO #599: seed
215                        for dim, _ in enumerate(array.shape):
216                            # only to make particles not shadow each other in visualisations
217                            attributes[name][dim, orig] = array[dim, :]
218                            attributes[name][dim, copy] = np.random.permutation(
219                                array[dim, :]
220                            )
221                    else:
222                        attributes[name][orig] = array
223                        attributes[name][copy] = array
224
225                non_zero_per_gridbox = np.count_nonzero(
226                    attributes[freezing_attribute]
227                ) / np.prod(self.settings.grid)
228                assert non_zero_per_gridbox == self.settings.n_sd_per_gridbox / 2
229
230        self.particulator = builder.build(attributes, tuple(products))
231
232        if self.SpinUp is not None:
233            self.SpinUp(self.particulator, self.settings.n_spin_up)
234        if self.storage is not None:
235            self.storage.init(self.settings)
def run( self, controller=<PySDM_examples.utils.dummy_controller.DummyController object>, vtk_exporter=None):
237    def run(self, controller=DummyController(), vtk_exporter=None):
238        with controller:
239            for step in self.settings.output_steps:
240                if controller.panic:
241                    break
242
243                self.particulator.run(step - self.particulator.n_steps)
244
245                self.store(step)
246
247                if vtk_exporter is not None:
248                    vtk_exporter.export_attributes(self.particulator)
249                    vtk_exporter.export_products(self.particulator)
250
251                controller.set_percent(step / self.settings.output_steps[-1])
def store(self, step):
253    def store(self, step):
254        for name, product in self.particulator.products.items():
255            self.storage.save(product.get(), step, name)