Exemple #1
0
def sim_full():
    return Simulation(
        TimeGrid(200, 2, 20),
        MeshGrid(5, 51),
        particle_sources=[
            ParticleSource('a', Box()),
            ParticleSource('c', Cylinder()),
            ParticleSource('d', Tube(start=(0, 0, 0), end=(0, 0, 1)))
        ],
        inner_regions=[
            InnerRegion('1', Box(), 1),
            InnerRegion('2', Sphere(), -2),
            InnerRegion('3', Cylinder(), 0),
            InnerRegion('4', Tube(), 4)
        ],
        particle_interaction_model=Model.binary,
        electric_fields=[FieldUniform('x', 'electric', (-2, -2, 1))],
        magnetic_fields=[
            FieldExpression('y', 'magnetic', '0', '0', '3*x + sqrt(y) - z**2')
        ])
Exemple #2
0
    def test_init_rhs(self):
        mesh = SpatialMeshConf((4, 3, 3)).make(BoundaryConditionsConf())
        solver = FieldSolver(mesh, [])
        solver.init_rhs_vector_in_full_domain(mesh)
        assert_array_equal(solver.rhs, np.zeros(3 * 2 * 2))

        mesh = SpatialMeshConf((4, 3, 3)).make(BoundaryConditionsConf(-2))
        solver = FieldSolver(mesh, [])
        solver.init_rhs_vector_in_full_domain(mesh)
        assert_array_equal(solver.rhs,
                           [6, 4, 6, 6, 4, 6, 6, 4, 6, 6, 4, 6])  # what

        mesh = SpatialMeshConf((4, 4, 5)).make(BoundaryConditionsConf(-2))
        solver = FieldSolver(mesh, [])
        solver.init_rhs_vector_in_full_domain(mesh)
        assert_array_equal(solver.rhs, [
            6, 4, 6, 4, 2, 4, 6, 4, 6, 4, 2, 4, 2, 0, 2, 4, 2, 4, 4, 2, 4, 2,
            0, 2, 4, 2, 4, 6, 4, 6, 4, 2, 4, 6, 4, 6
        ])  # what

        mesh = SpatialMeshConf((8, 12, 5),
                               (2, 3, 1)).make(BoundaryConditionsConf(-1))
        solver = FieldSolver(mesh, [])
        solver.init_rhs_vector_in_full_domain(mesh)
        assert_array_equal(solver.rhs, [
            49, 40, 49, 45, 36, 45, 49, 40, 49, 13, 4, 13, 9, 0, 9, 13, 4, 13,
            13, 4, 13, 9, 0, 9, 13, 4, 13, 49, 40, 49, 45, 36, 45, 49, 40, 49
        ])

        mesh = SpatialMeshConf((4, 6, 9),
                               (1, 2, 3)).make(BoundaryConditionsConf())
        solver = FieldSolver(mesh, [])
        mesh.charge_density = np.array([[[0, 0, 0, 0], [0, 0, 0, 0],
                                         [0, 0, 0, 0], [0, 0, 0, 0]],
                                        [[0, 0, 0, 0], [0, 1, 2, 0],
                                         [0, -1, 0, 0], [0, 0, 0, 0]],
                                        [[0, 0, 0, 0], [0, 3, 4, 0],
                                         [0, 0, -1, 0], [0, 0, 0, 0]],
                                        [[0, 0, 0, 0], [0, 5, 6, 0],
                                         [0, -1, 0, 0], [0, 0, 0, 0]],
                                        [[0, 0, 0, 0], [0, 0, 0, 0],
                                         [0, 0, 0, 0], [0, 0, 0, 0]]])
        solver.init_rhs_vector_in_full_domain(mesh)
        assert_allclose(
            solver.rhs, -np.array([1, 3, 5, -1, 0, -1, 2, 4, 6, 0, -1, 0]) *
            np.pi * 4 * 36)

        mesh = SpatialMeshConf((4, 6, 9),
                               (1, 2, 3)).make(BoundaryConditionsConf())
        solver = FieldSolver(mesh, [])
        region = InnerRegion('test', Box((1, 2, 3), (1, 2, 3)), 3)
        solver.init_rhs_vector(mesh, [region])
        assert_array_equal(solver.rhs, [3, 3, 0, 3, 3, 0, 3, 3, 0, 3, 3, 0])
Exemple #3
0
 def import_from_h5(h5file: h5py.File) -> Simulation:
     fields = [Field.import_h5(g) for g in h5file['ExternalFields'].values()]
     sources = [ParticleSource.import_h5(g) for g in h5file['ParticleSources'].values()]
     particles = [ParticleArray.import_h5(g) for g in h5file['ParticleSources'].values()]
     # cupy max has no `initial` argument
     max_id = int(max([(p.ids.get() if hasattr(p.ids, 'get') else p.ids).max(initial=-1) for p in particles], default=-1))
     g = h5file['SpatialMesh']
     mesh = MeshGrid.import_h5(g)
     charge = Reader.array_class(mesh, (), np.reshape(g['charge_density'], mesh.n_nodes))
     potential = Reader.array_class(mesh, (), np.reshape(g['potential'], mesh.n_nodes))
     field = FieldOnGrid('spatial_mesh', 'electric', Reader.array_class(mesh, 3, np.moveaxis(
         np.array([np.reshape(g[f'electric_field_{c}'], mesh.n_nodes) for c in 'xyz']),
         0, -1)))
     return Simulation(
         time_grid=TimeGrid.import_h5(h5file['TimeGrid']),
         mesh=mesh, charge_density=charge, potential=potential, electric_field=field,
         inner_regions=[InnerRegion.import_h5(g) for g in h5file['InnerRegions'].values()],
         electric_fields=[f for f in fields if f.electric_or_magnetic == 'electric'],
         magnetic_fields=[f for f in fields if f.electric_or_magnetic == 'magnetic'],
         particle_interaction_model=Model[
             h5file['ParticleInteractionModel'].attrs['particle_interaction_model'].decode('utf8')
         ],
         particle_sources=sources, particle_arrays=particles, particle_tracker=ParticleTracker(max_id)
     )
 def test_absorb_charge_inverted(self):
     particles = ParticleArray([1], -2.0, 1.0, (0, 0, 0), np.zeros(3))
     ir = InnerRegion('test', Box(), inverted=True)
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     ir.collide_with_particles(particles)
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     assert_dataclass_eq(
         particles,
         ParticleArray([1], -2.0, 1.0, np.zeros((1, 3)), np.zeros((1, 3))))
     particles = ParticleArray([1], -2.0, 1.0, (10, 10, 10), np.zeros(3))
     ir = InnerRegion('test', Box(), inverted=True)
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     ir.collide_with_particles(particles)
     assert ir.total_absorbed_particles == 1
     assert ir.total_absorbed_charge == -2
     assert_dataclass_eq(
         particles,
         ParticleArray([], -2.0, 1.0, np.zeros((0, 3)), np.zeros((0, 3))))
     particles = ParticleArray([1, 2], -2.0, 1.0, [(0, 0, 0), (10, 10, 10)],
                               np.zeros((2, 3)))
     ir = InnerRegion('test', Box(), inverted=True)
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     ir.collide_with_particles(particles)
     assert ir.total_absorbed_particles == 1
     assert ir.total_absorbed_charge == -2
     assert_dataclass_eq(
         particles,
         ParticleArray([1], -2.0, 1.0, [(0, 0, 0)], np.zeros((1, 3))))
 def test_absorb_charge(self):
     particles = ParticleArray([1], -2.0, 1.0, (0, 0, 0), np.zeros(3))
     ir = InnerRegion('test', Box())
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     ir.collide_with_particles(particles)
     assert ir.total_absorbed_particles == 1
     assert ir.total_absorbed_charge == -2
     assert particles == ParticleArray([], -2.0, 1.0, np.zeros((0, 3)), np.zeros((0, 3)))
     particles = ParticleArray([1], -2.0, 1.0, (10, 10, 10), np.zeros(3))
     ir = InnerRegion('test', Box())
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     ir.collide_with_particles(particles)
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     assert particles == ParticleArray([1], -2.0, 1.0, [(10, 10, 10)], np.zeros((1, 3)))
     particles = ParticleArray([1, 2], -2.0, 1.0, [(0, 0, 0), (10, 10, 10)], np.zeros((2, 3)))
     ir = InnerRegion('test', Box())
     assert ir.total_absorbed_particles == 0
     assert ir.total_absorbed_charge == 0
     ir.collide_with_particles(particles)
     assert ir.total_absorbed_particles == 1
     assert ir.total_absorbed_charge == -2
     assert particles == ParticleArray([2], -2.0, 1.0, [(10, 10, 10)], np.zeros((1, 3)))
Exemple #6
0
    def test_zero_nondiag_inside_objects(self):
        mesh = SpatialMeshConf((4, 6, 9),
                               (1, 2, 3)).make(BoundaryConditionsConf())
        solver = FieldSolver(mesh, [])
        region = InnerRegion('test', Box((1, 2, 3), (1, 2, 3)), 3)

        a = csr_matrix(np.full((12, 12), 2))
        assert_array_equal(a.toarray(), [[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                                         [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]])
        result = solver.zero_nondiag_for_nodes_inside_objects(
            a, mesh, [region])
        assert_array_equal(result.toarray(),
                           [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                            [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
                            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                            [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
                            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
                            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]])

        # TODO: check algorithm if on-diagonal zeros should turn into ones
        a = csr_matrix(
            np.array([[4, 0, 3, 0, 0, 0, 0, 2, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 6, 0],
                      [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]))
        result = solver.zero_nondiag_for_nodes_inside_objects(
            a, mesh, [region])
        assert_array_equal(result.toarray(),
                           [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
                            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
Exemple #7
0
class Simulation(SerializableH5):
    array_class: Type[ArrayOnGrid] = inject.attr(ArrayOnGrid)

    def __init__(self,
                 time_grid: TimeGrid,
                 mesh: MeshGrid,
                 inner_regions: Sequence[InnerRegion] = (),
                 particle_sources: Sequence[ParticleSource] = (),
                 electric_fields: Sequence[Field] = (),
                 magnetic_fields: Sequence[Field] = (),
                 particle_interaction_model: Model = Model.PIC,
                 charge_density: Optional[ArrayOnGrid] = None,
                 potential: Optional[ArrayOnGrid] = None,
                 electric_field: Optional[FieldOnGrid] = None,
                 particle_tracker: Optional[ParticleTracker] = None,
                 particle_arrays: Sequence[ParticleArray] = ()):
        super().__init__()
        self.time_grid: TimeGrid = time_grid
        self.mesh: MeshGrid = mesh
        self.charge_density: ArrayOnGrid = self.array_class(
            mesh) if charge_density is None else charge_density
        self.potential: ArrayOnGrid = self.array_class(
            mesh) if potential is None else potential
        self.electric_field: FieldOnGrid = FieldOnGrid('spatial_mesh', 'electric', self.array_class(mesh, 3)) \
            if electric_field is None else electric_field
        self._domain = InnerRegion('simulation_domain',
                                   shapes.Box(0, mesh.size),
                                   inverted=True)
        self.inner_regions: List[InnerRegion] = list(inner_regions)
        self.particle_sources: List[ParticleSource] = list(particle_sources)
        self.electric_fields: Field = FieldSum.factory(electric_fields,
                                                       'electric')
        self.magnetic_fields: Field = FieldSum.factory(magnetic_fields,
                                                       'magnetic')
        self.particle_interaction_model: Model = particle_interaction_model
        self.particle_arrays: List[ParticleArray] = list(particle_arrays)
        self.consolidate_particle_arrays()

        if self.particle_interaction_model == Model.binary:
            self._dynamic_field = FieldParticles('binary_particle_field',
                                                 self.particle_arrays)
            if not is_trivial(self.potential, self.inner_regions):
                self._dynamic_field += self.electric_field
        elif self.particle_interaction_model == Model.noninteracting:
            if not is_trivial(self.potential, self.inner_regions):
                self._dynamic_field = self.electric_field
            else:
                self._dynamic_field = FieldZero('Uniform_potential_zero_field',
                                                'electric')
        else:
            self._dynamic_field = self.electric_field

        self.particle_tracker = ParticleTracker(
        ) if particle_tracker is None else particle_tracker

    @property
    def dict(self) -> dict:
        d = super().dict
        d['particle_interaction_model'] = d['particle_interaction_model'].name
        return d

    def advance_one_time_step(self, field_solver):
        self.push_particles()
        self.generate_and_prepare_particles(field_solver)
        self.time_grid.update_to_next_step()

    def eval_charge_density(self):
        self.charge_density.reset()
        for p in self.particle_arrays:
            self.charge_density.distribute_at_positions(p.charge, p.positions)

    def push_particles(self):
        self.boris_integration(self.time_grid.time_step_size)

    def generate_and_prepare_particles(self, field_solver, initial=False):
        self.generate_valid_particles(initial)
        if self.particle_interaction_model == Model.PIC:
            self.eval_charge_density()
            field_solver.eval_potential(self.charge_density, self.potential)
            self.potential.gradient(self.electric_field.array)
        self.shift_new_particles_velocities_half_time_step_back()
        self.consolidate_particle_arrays()

    def generate_valid_particles(self, initial=False):
        # First generate then remove.
        # This allows for overlap of source and inner region.
        self.generate_new_particles(initial)
        self.apply_domain_boundary_conditions()
        self.remove_particles_inside_inner_regions()

    def boris_integration(self, dt):
        for particles in self.particle_arrays:
            total_el_field, total_mgn_field = \
                self.compute_total_fields_at_positions(particles.positions)
            if self.magnetic_fields != 0 and total_mgn_field.any():
                particles.boris_update_momentums(dt, total_el_field,
                                                 total_mgn_field)
            else:
                particles.boris_update_momentum_no_mgn(dt, total_el_field)
            particles.update_positions(dt)

    def prepare_boris_integration(self, minus_half_dt):
        # todo: place newly generated particle_arrays into separate buffer
        for particles in self.particle_arrays:
            if not particles.momentum_is_half_time_step_shifted:
                total_el_field, total_mgn_field = \
                    self.compute_total_fields_at_positions(particles.positions)
                if self.magnetic_fields != 0 and total_mgn_field.any():
                    particles.boris_update_momentums(minus_half_dt,
                                                     total_el_field,
                                                     total_mgn_field)
                else:
                    particles.boris_update_momentum_no_mgn(
                        minus_half_dt, total_el_field)
                particles.momentum_is_half_time_step_shifted = True

    def compute_total_fields_at_positions(self, positions):
        total_el_field = self.electric_fields + self._dynamic_field
        return total_el_field.get_at_points(positions, self.time_grid.current_time), \
               self.magnetic_fields.get_at_points(positions, self.time_grid.current_time)

    def shift_new_particles_velocities_half_time_step_back(self):
        minus_half_dt = -1.0 * self.time_grid.time_step_size / 2.0
        self.prepare_boris_integration(minus_half_dt)

    def apply_domain_boundary_conditions(self):
        for arr in self.particle_arrays:
            self._domain.collide_with_particles(arr)
        self.particle_arrays = [
            a for a in self.particle_arrays if len(a.ids) > 0
        ]

    def remove_particles_inside_inner_regions(self):
        for region in self.inner_regions:
            for p in self.particle_arrays:
                region.collide_with_particles(p)
            self.particle_arrays = [
                a for a in self.particle_arrays if len(a.ids) > 0
            ]

    def generate_new_particles(self, initial=False):
        for src in self.particle_sources:
            particles = src.generate_initial_particles(
            ) if initial else src.generate_each_step()
            if len(particles.ids):
                particles.ids = particles.xp.asarray(
                    self.particle_tracker.generate_particle_ids(
                        len(particles.ids)))
                self.particle_arrays.append(particles)

    def consolidate_particle_arrays(self):
        particles_by_type = defaultdict(list)
        for p in self.particle_arrays:
            particles_by_type[(p.mass, p.charge,
                               p.momentum_is_half_time_step_shifted)].append(p)
        self.particle_arrays = []
        for k, v in particles_by_type.items():
            mass, charge, shifted = k
            ids = v[0].xp.concatenate([p.ids for p in v])
            positions = v[0].xp.concatenate([p.positions for p in v])
            momentums = v[0].xp.concatenate([p.momentums for p in v])
            if len(ids):
                self.particle_arrays.append(
                    ParticleArray(ids, charge, mass, positions, momentums,
                                  shifted))