PySDM_examples.utils.kinematic_2d.simulation

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