# Pogona
# Copyright (C) 2020 Data Communications and Networking (TKN), TU Berlin
#
# This file is part of Pogona.
#
# Pogona is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pogona is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Pogona. If not, see <https://www.gnu.org/licenses/>.
import logging
import numpy as np
import pogona as pg
import pogona.properties as prop
LOG = logging.getLogger(__name__)
[docs]class SprayNozzle(pg.Component):
translation = prop.VectorProperty([0, 0, 0], required=True)
rotation = prop.VectorProperty([0, 0, 0], required=True)
injection_amount = prop.IntProperty(100, required=True)
velocity = prop.FloatProperty(1000, required=True)
velocity_sigma = prop.FloatProperty(100, required=True)
distribution_sigma = prop.FloatProperty(2.5, required=True)
seed = prop.StrProperty('', required=False)
"""
Seed for the pseudo-random number generator.
If empty, the random number generator of the simulation kernel
will be used.
If 'random', a random seed will be used for initialization.
Will be converted to int otherwise.
"""
"""
A spray nozzle is an injector that "sprays" particles at a given position
into a given direction.
Usually an injector is controlled by an instance of
ModulationOOK or a similar component, which determines when
to switch the injector on or off and which also controls the flow
speed in connected objects.
"""
[docs] def __init__(self):
super().__init__()
self._turned_on = False
self._burst_on = False
self._rng = np.random.RandomState(seed=None)
self._transformation = pg.Transformation()
"""Transformation of this sensor in the scene."""
[docs] def initialize(
self,
simulation_kernel: 'pg.SimulationKernel',
init_stage: 'pg.InitStages'
):
super().initialize(simulation_kernel, init_stage)
if init_stage == pg.InitStages.CHECK_ARGUMENTS:
if self.seed == 'random':
self._rng = np.random.RandomState(seed=None)
elif self.seed != '':
self._rng = np.random.RandomState(seed=int(self.seed))
else:
self._rng = simulation_kernel.get_random_number_generator()
self._transformation = pg.Transformation(
translation=np.array(self.translation),
rotation=np.array(self.rotation),
scaling=np.array([1, 1, 1])
)
elif init_stage == pg.InitStages.BUILD_SCENE:
pass
[docs] def turn_on(self):
"""
Turn on the injector and start spawning particles.
Usually called in the NotificationStages.MODULATION stage,
so before SPAWNING in the current time step.
:return:
"""
# Start injecting molecules every time step
LOG.debug("Injector turned on")
self._turned_on = True
[docs] def turn_off(self):
"""Stop injecting molecules"""
LOG.debug("Injector turned off")
self._turned_on = False
[docs] def inject_burst(self):
"""Inject molecules in the next time step only"""
LOG.debug("Injector bursts")
self._burst_on = True
[docs] def process_new_time_step(
self,
simulation_kernel: 'pg.SimulationKernel',
notification_stage: 'pg.NotificationStages',
):
if notification_stage != pg.NotificationStages.SPAWNING:
return
if self._turned_on or self._burst_on:
# Only inject bursts once
self._burst_on = False
LOG.debug(f"Injecting {self.injection_amount} new molecules")
base_delta_time = simulation_kernel.base_delta_time
step_delta_time = base_delta_time / self.injection_amount
delta_time = 0
while delta_time < base_delta_time:
# Spray one particle in positive y direction
# with some randomness.
used_velocity = (
self.velocity_sigma * self._rng.randn() + self.velocity
)
# Uniformly distributed angle between 0 and 2*pi by which
# the new velocity vector will be roated around the y-axis:
used_3d_angle = self._rng.random() * 2 * np.pi
# Normally distributed angle by which the new velocity
# vector will be rotated around the x- and z-axis:
used_distribution = (
self.distribution_sigma * self._rng.randn() * (np.pi/180)
)
velocity_x = (
used_velocity
* np.sin(used_distribution)
* np.sin(used_3d_angle)
)
velocity_y = used_velocity * np.cos(used_distribution)
velocity_z = (
used_velocity
* np.sin(used_distribution)
* np.cos(used_3d_angle)
)
velocity = self._transformation.apply_to_direction(
np.array((velocity_x, velocity_y, velocity_z))
)
position = self._transformation.apply_to_point(
np.array((0, 0, 0))
)
injected_molecule = pg.Molecule(
position + (velocity * delta_time),
velocity,
None,
)
simulation_kernel.get_molecule_manager().add_molecule(
injected_molecule
)
delta_time += step_delta_time