From 42a132757d995a82789459276bd572b7d4392557 Mon Sep 17 00:00:00 2001 From: noway Date: Thu, 15 Mar 2018 19:52:50 +0300 Subject: [PATCH 01/30] WIP on fixes for continue from h5 --- Domain.py | 2 +- ExternalFields.py | 12 +++++----- ExternalFieldsManager.py | 2 +- InnerRegion.py | 6 ++--- InnerRegionBox.py | 12 +++++----- InnerRegionCylinder.py | 14 +++++------ InnerRegionSphere.py | 8 +++---- InnerRegionTube.py | 16 ++++++------- InnerRegionsManager.py | 2 +- ParticleInteractionModel.py | 2 +- ParticleSource.py | 21 +++++++++-------- ParticleSourceBox.py | 20 ++++++++-------- ParticleSourceCylinder.py | 22 ++++++++--------- ParticleSourcesManager.py | 2 +- SpatialMesh.py | 47 +++++++++++++++++++++++++++---------- TimeGrid.py | 14 +++++------ main.py | 8 +++---- 17 files changed, 117 insertions(+), 93 deletions(-) diff --git a/Domain.py b/Domain.py index d1a1f22c..a14ee140 100644 --- a/Domain.py +++ b/Domain.py @@ -38,7 +38,7 @@ def init_from_config( cls, conf ): @classmethod - def init_from_h5( h5file ): + def init_from_h5( cls, h5file ): new_obj = cls() new_obj.time_grid = TimeGrid.init_from_h5( h5file["/Time_grid"] ) new_obj.spat_mesh = SpatialMesh.init_from_h5( h5file["/Spatial_mesh"] ) diff --git a/ExternalFields.py b/ExternalFields.py index fd25e936..fb5e20b7 100644 --- a/ExternalFields.py +++ b/ExternalFields.py @@ -55,9 +55,9 @@ def get_values_from_config( self, field_conf ): def init_from_h5( cls, h5_field_group ): new_obj = super().init_from_h5( h5_field_group ) new_obj.field_type = "magnetic_uniform" - Hx = h5_field_group.attrs["magnetic_uniform_field_x"][0] - Hy = h5_field_group.attrs["magnetic_uniform_field_y"][0] - Hz = h5_field_group.attrs["magnetic_uniform_field_z"][0] + Hx = h5_field_group.attrs["magnetic_uniform_field_x"] + Hy = h5_field_group.attrs["magnetic_uniform_field_y"] + Hz = h5_field_group.attrs["magnetic_uniform_field_z"] new_obj.magnetic_field = Vec3d( Hx, Hy, Hz ) return new_obj @@ -112,9 +112,9 @@ def get_values_from_config( self, field_conf ): def init_from_h5( cls, h5_field_group ): new_obj = super().init_from_h5( h5_field_group ) new_obj.field_type = "electric_uniform" - Ex = h5_field_group.attrs["electric_uniform_field_x"][0] - Ey = h5_field_group.attrs["electric_uniform_field_y"][0] - Ez = h5_field_group.attrs["electric_uniform_field_z"][0] + Ex = h5_field_group.attrs["electric_uniform_field_x"] + Ey = h5_field_group.attrs["electric_uniform_field_y"] + Ez = h5_field_group.attrs["electric_uniform_field_z"] new_obj.electric_field = Vec3d( Ex, Ey, Ez ) return new_obj diff --git a/ExternalFieldsManager.py b/ExternalFieldsManager.py index 42cc6d6d..37750e94 100644 --- a/ExternalFieldsManager.py +++ b/ExternalFieldsManager.py @@ -37,7 +37,7 @@ def init_from_h5( cls, h5_external_fields_group ): return new_obj def parse_hdf5_external_field( self, current_field_grpid ): - field_type = current_field_grpid.attrs["field_type"][0] + field_type = current_field_grpid.attrs["field_type"] if field_type == "magnetic_uniform": self.magnetic.append( ExternalFieldMagneticUniform.init_from_h5( current_field_grpid ) ) diff --git a/InnerRegion.py b/InnerRegion.py index ba172071..b4b9ebd2 100644 --- a/InnerRegion.py +++ b/InnerRegion.py @@ -47,11 +47,11 @@ def set_parameters_from_config( self, this_reg_config_part, sec_name ): def get_values_from_h5( self, h5_inner_region_group ): self.name = os.path.basename( h5_inner_region_group.name ) - self.potential = h5_inner_region_group.attrs["potential"][0] + self.potential = h5_inner_region_group.attrs["potential"] self.total_absorbed_particles = h5_inner_region_group.attrs[ - "total_absorbed_particles"][0] + "total_absorbed_particles"] self.total_absorbed_charge = h5_inner_region_group.attrs[ - "total_absorbed_charge"][0] + "total_absorbed_charge"] def check_if_particle_inside( self, p ): diff --git a/InnerRegionBox.py b/InnerRegionBox.py index c6567f65..2729f670 100644 --- a/InnerRegionBox.py +++ b/InnerRegionBox.py @@ -46,12 +46,12 @@ def get_box_values_from_config( self, inner_region_box_conf ): def get_box_values_from_h5( self, h5_inner_region_box_group ): - self.x_left = h5_inner_region_box_group.attrs["x_left"][0] - self.x_right = h5_inner_region_box_group.attrs["x_right"][0] - self.y_bottom = h5_inner_region_box_group.attrs["y_bottom"][0] - self.y_top = h5_inner_region_box_group.attrs["y_top"][0] - self.z_near = h5_inner_region_box_group.attrs["z_near"][0] - self.z_far = h5_inner_region_box_group.attrs["z_far"][0] + self.x_left = h5_inner_region_box_group.attrs["x_left"] + self.x_right = h5_inner_region_box_group.attrs["x_right"] + self.y_bottom = h5_inner_region_box_group.attrs["y_bottom"] + self.y_top = h5_inner_region_box_group.attrs["y_top"] + self.z_near = h5_inner_region_box_group.attrs["z_near"] + self.z_far = h5_inner_region_box_group.attrs["z_far"] def check_if_point_inside( self, x, y, z ): diff --git a/InnerRegionCylinder.py b/InnerRegionCylinder.py index 3bd392e0..64a7b2b9 100644 --- a/InnerRegionCylinder.py +++ b/InnerRegionCylinder.py @@ -50,15 +50,15 @@ def get_cylinder_values_from_config( self, inner_region_cylinder_conf ): def get_cylinder_values_from_h5( self, h5_inner_region_cylinder_group ): self.axis_start_x = h5_inner_region_cylinder_group.attrs[ - "cylinder_axis_start_x"][0] + "cylinder_axis_start_x"] self.axis_start_y = h5_inner_region_cylinder_group.attrs[ - "cylinder_axis_start_y"][0] + "cylinder_axis_start_y"] self.axis_start_z = h5_inner_region_cylinder_group.attrs[ - "cylinder_axis_start_z"][0] - self.axis_end_x = h5_inner_region_cylinder_group.attrs["cylinder_axis_end_x"][0] - self.axis_end_y = h5_inner_region_cylinder_group.attrs["cylinder_axis_end_y"][0] - self.axis_end_z = h5_inner_region_cylinder_group.attrs["cylinder_axis_end_z"][0] - self.radius = h5_inner_region_cylinder_group.attrs["cylinder_radius"][0] + "cylinder_axis_start_z"] + self.axis_end_x = h5_inner_region_cylinder_group.attrs["cylinder_axis_end_x"] + self.axis_end_y = h5_inner_region_cylinder_group.attrs["cylinder_axis_end_y"] + self.axis_end_z = h5_inner_region_cylinder_group.attrs["cylinder_axis_end_z"] + self.radius = h5_inner_region_cylinder_group.attrs["cylinder_radius"] def check_if_point_inside( self, x, y, z ): diff --git a/InnerRegionSphere.py b/InnerRegionSphere.py index 04d51227..8d1a053c 100644 --- a/InnerRegionSphere.py +++ b/InnerRegionSphere.py @@ -46,10 +46,10 @@ def get_sphere_values_from_config( self, inner_region_sphere_conf ): def get_sphere_values_from_h5( self, h5_inner_region_sphere_group ): - self.origin_x = h5_inner_region_sphere_group.attrs["sphere_origin_x"][0] - self.origin_y = h5_inner_region_sphere_group.attrs["sphere_origin_y"][0] - self.origin_z = h5_inner_region_sphere_group.attrs["sphere_origin_z"][0] - self.radius = h5_inner_region_sphere_group.attrs["sphere_radius"][0] + self.origin_x = h5_inner_region_sphere_group.attrs["sphere_origin_x"] + self.origin_y = h5_inner_region_sphere_group.attrs["sphere_origin_y"] + self.origin_z = h5_inner_region_sphere_group.attrs["sphere_origin_z"] + self.radius = h5_inner_region_sphere_group.attrs["sphere_radius"] def check_if_point_inside( self, x, y, z ): diff --git a/InnerRegionTube.py b/InnerRegionTube.py index 23f90b5a..37cd15ac 100644 --- a/InnerRegionTube.py +++ b/InnerRegionTube.py @@ -49,14 +49,14 @@ def get_tube_values_from_config( self, inner_region_tube_conf ): def get_tube_values_from_h5( self, h5_inner_region_tube_group ): - self.axis_start_x = h5_inner_region_tube_group.attrs["tube_axis_start_x"][0] - self.axis_start_y = h5_inner_region_tube_group.attrs["tube_axis_start_y"][0] - self.axis_start_z = h5_inner_region_tube_group.attrs["tube_axis_start_z"][0] - self.axis_end_x = h5_inner_region_tube_group.attrs["tube_axis_end_x"][0] - self.axis_end_y = h5_inner_region_tube_group.attrs["tube_axis_end_y"][0] - self.axis_end_z = h5_inner_region_tube_group.attrs["tube_axis_end_z"][0] - self.inner_radius = h5_inner_region_tube_group.attrs["tube_inner_radius"][0] - self.outer_radius = h5_inner_region_tube_group.attrs["tube_outer_radius"][0] + self.axis_start_x = h5_inner_region_tube_group.attrs["tube_axis_start_x"] + self.axis_start_y = h5_inner_region_tube_group.attrs["tube_axis_start_y"] + self.axis_start_z = h5_inner_region_tube_group.attrs["tube_axis_start_z"] + self.axis_end_x = h5_inner_region_tube_group.attrs["tube_axis_end_x"] + self.axis_end_y = h5_inner_region_tube_group.attrs["tube_axis_end_y"] + self.axis_end_z = h5_inner_region_tube_group.attrs["tube_axis_end_z"] + self.inner_radius = h5_inner_region_tube_group.attrs["tube_inner_radius"] + self.outer_radius = h5_inner_region_tube_group.attrs["tube_outer_radius"] def check_if_point_inside( self, x, y, z ): diff --git a/InnerRegionsManager.py b/InnerRegionsManager.py index 67ae42e0..d2dcec5a 100644 --- a/InnerRegionsManager.py +++ b/InnerRegionsManager.py @@ -50,7 +50,7 @@ def init_from_h5( cls, h5_reg_group, spat_mesh ): def parse_hdf5_inner_region( self, this_reg_h5_group, spat_mesh ): - geometry_type = this_reg_h5_group.attr["geometry_type"][0] + geometry_type = this_reg_h5_group.attrs["geometry_type"] if geometry_type == "box": self.regions.append( InnerRegionBox.init_from_h5( this_reg_h5_group, spat_mesh ) ) diff --git a/ParticleInteractionModel.py b/ParticleInteractionModel.py index af29f360..28ff5f9e 100644 --- a/ParticleInteractionModel.py +++ b/ParticleInteractionModel.py @@ -36,7 +36,7 @@ def get_values_from_config( self, conf ): def init_from_h5( cls, h5group ): new_obj = cls() new_obj.particle_interaction_model = \ - h5group.attrs["particle_interaction_model"][0] + h5group.attrs["particle_interaction_model"] if new_obj.particle_interaction_model == "noninteracting": new_obj.noninteracting = True elif new_obj.particle_interaction_model == "PIC": diff --git a/ParticleSource.py b/ParticleSource.py index 8b68d8f7..862cb2c2 100644 --- a/ParticleSource.py +++ b/ParticleSource.py @@ -70,17 +70,17 @@ def set_parameters_from_config( self, this_source_config_part, sec_name ): def read_hdf5_source_parameters( self, h5group ): self.name = os.path.basename( h5group.name ) - self.temperature = h5group.attrs["temperature"][0] - mean_momentum_x = h5group.attrs["mean_momentum_x"][0] - mean_momentum_y = h5group.attrs["mean_momentum_y"][0] - mean_momentum_z = h5group.attrs["mean_momentum_z"][0] + self.temperature = h5group.attrs["temperature"] + mean_momentum_x = h5group.attrs["mean_momentum_x"] + mean_momentum_y = h5group.attrs["mean_momentum_y"] + mean_momentum_z = h5group.attrs["mean_momentum_z"] self.mean_momentum = Vec3d( mean_momentum_x, mean_momentum_y, mean_momentum_z ) - self.charge = h5group.attrs["charge"][0] - self.mass = h5group.attrs["mass"][0] - self.initial_number_of_particles = h5group.attrs["initial_number_of_particles"][0] + self.charge = h5group.attrs["charge"] + self.mass = h5group.attrs["mass"] + self.initial_number_of_particles = h5group.attrs["initial_number_of_particles"] self.particles_to_generate_each_step = \ - h5group.attrs["particles_to_generate_each_step"][0] - self.max_id = h5group.attrs["max_id"][0] + h5group.attrs["particles_to_generate_each_step"] + self.max_id = h5group.attrs["max_id"] def read_hdf5_particles( self, h5group ): @@ -92,12 +92,13 @@ def read_hdf5_particles( self, h5group ): py_buf = h5group["./momentum_y"] pz_buf = h5group["./momentum_z"] # + self.particles = [] for (i, x, y, z, px, py, pz) in \ zip( id_buf, x_buf, y_buf, z_buf, px_buf, py_buf, pz_buf ): pos = Vec3d( x, y, z ) mom = Vec3d( px, py, pz ) self.particles.append( Particle( i, self.charge, self.mass, pos, mom ) ) - self.particles[-1].momentum_is_half_time_step_shifted = true + self.particles[-1].momentum_is_half_time_step_shifted = True def generate_initial_particles( self ): diff --git a/ParticleSourceBox.py b/ParticleSourceBox.py index 31699b7e..fe639871 100644 --- a/ParticleSourceBox.py +++ b/ParticleSourceBox.py @@ -20,10 +20,10 @@ def init_from_config( cls, conf, this_source_config_part, sec_name ): @classmethod - def init_from_h5_source_group( h5_source_group ): - new_obj = super().init_from_h5_source_group( h5_source_group ) + def init_from_h5( cls, h5_source_group ): + new_obj = super().init_from_h5( h5_source_group ) new_obj.geometry_type = "box" - new_obj.read_hdf5_source_parameters( h5_source_group ) + new_obj.read_hdf5_box_parameters( h5_source_group ) return new_obj @@ -48,13 +48,13 @@ def set_box_parameters_from_config( self, this_source_config_part ): self.zfar = this_source_config_part.getfloat("box_z_far") - def read_hdf5_source_parameters( self, this_source_h5_group ): - self.xleft = this_source_h5_group.attrs["box_x_left"][0] - self.xright = this_source_h5_group.attrs["box_x_xright"][0] - self.ytop = this_source_h5_group.attrs["box_y_top"][0] - self.ybottom = this_source_h5_group.attrs["box_y_bottom"][0] - self.zfar = this_source_h5_group.attrs["box_z_far"][0] - self.znear = this_source_h5_group.attrs["box_z_near"][0] + def read_hdf5_box_parameters( self, this_source_h5_group ): + self.xleft = this_source_h5_group.attrs["box_x_left"] + self.xright = this_source_h5_group.attrs["box_x_right"] + self.ytop = this_source_h5_group.attrs["box_y_top"] + self.ybottom = this_source_h5_group.attrs["box_y_bottom"] + self.zfar = this_source_h5_group.attrs["box_z_far"] + self.znear = this_source_h5_group.attrs["box_z_near"] def x_left_ge_zero( self, conf, this_source_config_part ): diff --git a/ParticleSourceCylinder.py b/ParticleSourceCylinder.py index 94ba8e64..e2fcb80c 100644 --- a/ParticleSourceCylinder.py +++ b/ParticleSourceCylinder.py @@ -19,10 +19,10 @@ def init_from_config( cls, conf, this_source_config_part, sec_name ): return new_obj @classmethod - def init_from_h5_source_group( h5_source_group ): - new_obj = super().init_from_h5_source_group( h5_source_group ) + def init_from_h5_source_group( cls, h5_source_group ): + new_obj = super().init_from_h5( h5_source_group ) new_obj.geometry_type = "cylinder" - new_obj.read_hdf5_source_parameters( h5_source_group ) + new_obj.read_hdf5_cylinder_parameters( h5_source_group ) return new_obj @@ -53,14 +53,14 @@ def set_cylinder_parameters_from_config( self, this_source_config_part ): self.radius = this_source_config_part.getfloat("cylinder_radius") - def read_hdf5_source_parameters( self, this_source_h5_group ): - self.axis_start_x = this_source_h5_group.attrs["cylinder_axis_start_x"][0] - self.axis_start_y = this_source_h5_group.attrs["cylinder_axis_start_y"][0] - self.axis_start_z = this_source_h5_group.attrs["cylinder_axis_start_z"][0] - self.axis_end_x = this_source_h5_group.attrs["cylinder_axis_end_x"][0] - self.axis_end_y = this_source_h5_group.attrs["cylinder_axis_end_y"][0] - self.axis_end_z = this_source_h5_group.attrs["cylinder_axis_end_z"][0] - self.radius = this_source_h5_group.attrs["cylinder_radius"][0] + def read_hdf5_cylinder_parameters( self, this_source_h5_group ): + self.axis_start_x = this_source_h5_group.attrs["cylinder_axis_start_x"] + self.axis_start_y = this_source_h5_group.attrs["cylinder_axis_start_y"] + self.axis_start_z = this_source_h5_group.attrs["cylinder_axis_start_z"] + self.axis_end_x = this_source_h5_group.attrs["cylinder_axis_end_x"] + self.axis_end_y = this_source_h5_group.attrs["cylinder_axis_end_y"] + self.axis_end_z = this_source_h5_group.attrs["cylinder_axis_end_z"] + self.radius = this_source_h5_group.attrs["cylinder_radius"] def radius_gt_zero( self, conf, this_source_config_part ): diff --git a/ParticleSourcesManager.py b/ParticleSourcesManager.py index b65e2af1..03210eaa 100644 --- a/ParticleSourcesManager.py +++ b/ParticleSourcesManager.py @@ -39,7 +39,7 @@ def init_from_h5( cls, h5_sources_group ): def parse_hdf5_particle_source( self, this_source_h5_group ): - geometry_type = this_source_h5_group.attr["geometry_type"][0] + geometry_type = this_source_h5_group.attrs["geometry_type"] if geometry_type == "box": self.sources.append( ParticleSourceBox.init_from_h5( this_source_h5_group ) ) diff --git a/SpatialMesh.py b/SpatialMesh.py index 03f74a18..43d457ec 100644 --- a/SpatialMesh.py +++ b/SpatialMesh.py @@ -26,15 +26,15 @@ def init_from_config( cls, conf ): @classmethod def init_from_h5( cls, h5group ): new_obj = cls() - new_obj.x_volume_size = h5group.attrs["x_volume_size"][0] - new_obj.y_volume_size = h5group.attrs["y_volume_size"][0] - new_obj.z_volume_size = h5group.attrs["z_volume_size"][0] - new_obj.x_cell_size = h5group.attrs["x_cell_size"][0] - new_obj.y_cell_size = h5group.attrs["y_cell_size"][0] - new_obj.z_cell_size = h5group.attrs["z_cell_size"][0] - new_obj.x_n_nodes = h5group.attrs["x_n_nodes"][0] - new_obj.y_n_nodes = h5group.attrs["y_n_nodes"][0] - new_obj.z_n_nodes = h5group.attrs["z_n_nodes"][0] + new_obj.x_volume_size = h5group.attrs["x_volume_size"] + new_obj.y_volume_size = h5group.attrs["y_volume_size"] + new_obj.z_volume_size = h5group.attrs["z_volume_size"] + new_obj.x_cell_size = h5group.attrs["x_cell_size"] + new_obj.y_cell_size = h5group.attrs["y_cell_size"] + new_obj.z_cell_size = h5group.attrs["z_cell_size"] + new_obj.x_n_nodes = h5group.attrs["x_n_nodes"] + new_obj.y_n_nodes = h5group.attrs["y_n_nodes"] + new_obj.z_n_nodes = h5group.attrs["z_n_nodes"] new_obj.allocate_ongrid_values() # dim = new_obj.node_coordinates.size @@ -45,17 +45,24 @@ def init_from_h5( cls, h5group ): tmp_x = h5group["./node_coordinates_x"] tmp_y = h5group["./node_coordinates_y"] tmp_z = h5group["./node_coordinates_z"] - for i, (vx, vy, vz) in enumerate( zip( tmp_x, tmp_y, tmp_z ) ): - new_obj.node_coordinates[i] = Vec3d( vx, vy, vz ) + for global_idx, (vx, vy, vz) in enumerate( zip( tmp_x, tmp_y, tmp_z ) ): + # todo: highly nonoptimal; use np.reshape? + i, j, k = new_obj.global_idx_to_node_ijk( global_idx ) + new_obj.node_coordinates[i][j][k] = Vec3d( vx, vy, vz ) # new_obj.charge_density = h5group["./charge_density"] + np.reshape( new_obj.charge_density, + ( new_obj.x_n_nodes, new_obj.y_n_nodes, new_obj.z_n_nodes ) ) new_obj.potential = h5group["./potential"] + np.reshape( new_obj.potential, + ( new_obj.x_n_nodes, new_obj.y_n_nodes, new_obj.z_n_nodes ) ) # tmp_x = h5group["./electric_field_x"] tmp_y = h5group["./electric_field_y"] tmp_z = h5group["./electric_field_z"] for i, (vx, vy, vz) in enumerate( zip( tmp_x, tmp_y, tmp_z ) ): - new_obj.electric_field[i] = Vec3d( vx, vy, vz ) + i, j, k = new_obj.global_idx_to_node_ijk( global_idx ) + new_obj.electric_field[i][j][k] = Vec3d( vx, vy, vz ) # return new_obj @@ -302,3 +309,19 @@ def node_number_to_coordinate_z( self, k ): print( "invalid node number k={:d} " "at node_number_to_coordinate_z".format( k ) ) sys.exit( -1 ) + + + def global_idx_to_node_ijk( self, global_idx ): + # In row-major order: (used to save on disk) + # global_index = i * nz * ny + + # j * nz + + # k + # + nx = self.x_n_nodes + ny = self.y_n_nodes + nz = self.z_n_nodes + i = global_idx // ( nz * ny ) + j_and_k_part = global_idx % ( nx * ny ) + j = j_and_k_part // nz + k = j_and_k_part % nz + return (i, j, k) diff --git a/TimeGrid.py b/TimeGrid.py index 81bcc6f8..fd7fa3e5 100644 --- a/TimeGrid.py +++ b/TimeGrid.py @@ -23,13 +23,13 @@ def init_from_config( cls, conf ): @classmethod def init_from_h5( cls, h5group ): new_obj = cls() - new_obj.total_time = h5group.attrs["total_time"][0] - new_obj.current_time = h5group.attrs["current_time"][0] - new_obj.time_step_size = h5group.attrs["time_step_size"][0] - new_obj.time_save_step = h5group.attrs["time_save_step"][0] - new_obj.total_nodes = h5group.attrs["total_nodes"][0] - new_obj.current_node = h5group.attrs["current_node"][0] - new_obj.node_to_save = h5group.attrs["node_to_save"][0] + new_obj.total_time = h5group.attrs["total_time"] + new_obj.current_time = h5group.attrs["current_time"] + new_obj.time_step_size = h5group.attrs["time_step_size"] + new_obj.time_save_step = h5group.attrs["time_save_step"] + new_obj.total_nodes = h5group.attrs["total_nodes"] + new_obj.current_node = h5group.attrs["current_node"] + new_obj.node_to_save = h5group.attrs["node_to_save"] return new_obj def check_correctness_of_related_config_fields( self, conf ): diff --git a/main.py b/main.py index a9413d2b..4a39a26b 100644 --- a/main.py +++ b/main.py @@ -55,12 +55,12 @@ def parse_cmd_line(): def extract_filename_prefix_and_suffix_from_h5filename( h5_file ): - rgx = "[0-9]{7}" + rgx = "[0-9]{7}" # search for timestep in filename (7 digits in a row) match = re.search( rgx, h5_file ) - if len( match.group ) == 1: + if match: prefix = h5_file[ 0:match.start() ] - suffix = h5_file.substr[ match.end(): ] - print( prefix, suffix ) + suffix = h5_file[ match.end(): ] + print( "Extracted h5 prefix and suffix:", prefix, suffix ) else: print( "Can't identify filename prefix and suffix in ", h5_file ) print( "Aborting." ) From e686d562e1e89a07028735c4075430d0988eef68 Mon Sep 17 00:00:00 2001 From: noway Date: Fri, 16 Mar 2018 20:45:15 +0300 Subject: [PATCH 02/30] Fixed restart from file --- SpatialMesh.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/SpatialMesh.py b/SpatialMesh.py index 43d457ec..790ed368 100644 --- a/SpatialMesh.py +++ b/SpatialMesh.py @@ -35,6 +35,8 @@ def init_from_h5( cls, h5group ): new_obj.x_n_nodes = h5group.attrs["x_n_nodes"] new_obj.y_n_nodes = h5group.attrs["y_n_nodes"] new_obj.z_n_nodes = h5group.attrs["z_n_nodes"] + # + # todo: don't allocate. read into flat arrays. then reshape new_obj.allocate_ongrid_values() # dim = new_obj.node_coordinates.size @@ -46,16 +48,16 @@ def init_from_h5( cls, h5group ): tmp_y = h5group["./node_coordinates_y"] tmp_z = h5group["./node_coordinates_z"] for global_idx, (vx, vy, vz) in enumerate( zip( tmp_x, tmp_y, tmp_z ) ): - # todo: highly nonoptimal; use np.reshape? - i, j, k = new_obj.global_idx_to_node_ijk( global_idx ) + # todo: highly nonoptimal; make view or reshape? + i, j, k = new_obj.global_idx_to_node_ijk( global_idx ) new_obj.node_coordinates[i][j][k] = Vec3d( vx, vy, vz ) # - new_obj.charge_density = h5group["./charge_density"] - np.reshape( new_obj.charge_density, - ( new_obj.x_n_nodes, new_obj.y_n_nodes, new_obj.z_n_nodes ) ) - new_obj.potential = h5group["./potential"] - np.reshape( new_obj.potential, - ( new_obj.x_n_nodes, new_obj.y_n_nodes, new_obj.z_n_nodes ) ) + tmp_rho = h5group["./charge_density"] + tmp_phi = h5group["./potential"] + for global_idx, (rho, phi) in enumerate( zip( tmp_rho, tmp_phi ) ): + i, j, k = new_obj.global_idx_to_node_ijk( global_idx ) + new_obj.charge_density[i][j][k] = rho + new_obj.potential[i][j][k] = phi # tmp_x = h5group["./electric_field_x"] tmp_y = h5group["./electric_field_y"] @@ -221,7 +223,9 @@ def write_hdf5_ongrid_values( self, h5group ): tmp_y = np.empty_like( tmp_x ) tmp_z = np.empty_like( tmp_x ) # todo: make view instead of copy - for i, v in enumerate( self.node_coordinates.flat ): + flat_node_coords = self.node_coordinates.ravel( order = 'C' ) + print( len( flat_node_coords ), dim ) + for i, v in enumerate( flat_node_coords ): tmp_x[i] = v.x tmp_y[i] = v.y tmp_z[i] = v.z @@ -235,7 +239,8 @@ def write_hdf5_ongrid_values( self, h5group ): flat_rho = self.charge_density.ravel( order = 'C' ) h5group.create_dataset( "./charge_density", data = flat_rho ) # - for i, v in enumerate( self.electric_field.flat ): + flat_field = self.electric_field.ravel( order = 'C' ) + for i, v in enumerate( flat_field ): tmp_x[i] = v.x tmp_y[i] = v.y tmp_z[i] = v.z @@ -321,7 +326,7 @@ def global_idx_to_node_ijk( self, global_idx ): ny = self.y_n_nodes nz = self.z_n_nodes i = global_idx // ( nz * ny ) - j_and_k_part = global_idx % ( nx * ny ) + j_and_k_part = global_idx % ( nz * ny ) j = j_and_k_part // nz k = j_and_k_part % nz return (i, j, k) From b1dd3d3881d64684fddf878989d12cdf06e28f0d Mon Sep 17 00:00:00 2001 From: noway Date: Sat, 24 Mar 2018 18:56:34 +0300 Subject: [PATCH 03/30] Fix restart from file for external fields --- ExternalFields.py | 2 ++ ExternalFieldsManager.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ExternalFields.py b/ExternalFields.py index fb5e20b7..c2fffbf4 100644 --- a/ExternalFields.py +++ b/ExternalFields.py @@ -1,3 +1,5 @@ +import os + from Vec3d import Vec3d import physical_constants diff --git a/ExternalFieldsManager.py b/ExternalFieldsManager.py index 37750e94..ade5054c 100644 --- a/ExternalFieldsManager.py +++ b/ExternalFieldsManager.py @@ -33,7 +33,7 @@ def init_from_h5( cls, h5_external_fields_group ): new_obj.magnetic = [] for field_name in h5_external_fields_group.keys(): current_field_grpid = h5_external_fields_group[ field_name ] - self.parse_hdf5_external_field( current_field_grpid ) + new_obj.parse_hdf5_external_field( current_field_grpid ) return new_obj def parse_hdf5_external_field( self, current_field_grpid ): From 909a3ac6ed6ee4d7602550c99ded3765c6d9494f Mon Sep 17 00:00:00 2001 From: noway Date: Mon, 26 Mar 2018 19:36:54 +0300 Subject: [PATCH 04/30] In SpatialMesh init_from_h5 fix index in for loop --- SpatialMesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpatialMesh.py b/SpatialMesh.py index 790ed368..ee3c459e 100644 --- a/SpatialMesh.py +++ b/SpatialMesh.py @@ -62,7 +62,7 @@ def init_from_h5( cls, h5group ): tmp_x = h5group["./electric_field_x"] tmp_y = h5group["./electric_field_y"] tmp_z = h5group["./electric_field_z"] - for i, (vx, vy, vz) in enumerate( zip( tmp_x, tmp_y, tmp_z ) ): + for global_idx, (vx, vy, vz) in enumerate( zip( tmp_x, tmp_y, tmp_z ) ): i, j, k = new_obj.global_idx_to_node_ijk( global_idx ) new_obj.electric_field[i][j][k] = Vec3d( vx, vy, vz ) # From 3403403fd75bc087f0cce370e51b1526c08ef695 Mon Sep 17 00:00:00 2001 From: noway Date: Thu, 5 Apr 2018 16:56:52 +0300 Subject: [PATCH 05/30] Initial implementation of tubular sources --- ParticleSourceTube.py | 221 ++++++++++++++++++++++++++++++++++++++ ParticleSourcesManager.py | 9 ++ 2 files changed, 230 insertions(+) create mode 100644 ParticleSourceTube.py diff --git a/ParticleSourceTube.py b/ParticleSourceTube.py new file mode 100644 index 00000000..e61ec093 --- /dev/null +++ b/ParticleSourceTube.py @@ -0,0 +1,221 @@ +from ParticleSource import * + +# Tube source + +class ParticleSourceTube( ParticleSource ): + + def __init__( self ): + super().__init__() + + + @classmethod + def init_from_config( cls, conf, this_source_config_part, sec_name ): + new_obj = super().init_from_config( conf, this_source_config_part, sec_name ) + new_obj.geometry_type = "tube" + new_obj.check_correctness_of_tube_config_fields( + conf, this_source_config_part ) + new_obj.set_tube_parameters_from_config( this_source_config_part ) + new_obj.generate_initial_particles() + return new_obj + + @classmethod + def init_from_h5_source_group( cls, h5_source_group ): + new_obj = super().init_from_h5( h5_source_group ) + new_obj.geometry_type = "tube" + new_obj.read_hdf5_tube_parameters( h5_source_group ) + return new_obj + + + def check_correctness_of_tube_config_fields( self, conf, this_source_config_part ): + # todo: + self.inner_radius_gt_zero( conf, this_source_config_part ) + self.outer_radius_gt_inner_radius( conf, this_source_config_part ) + self.axis_start_x_min_rad_ge_zero( conf, this_source_config_part ) + self.axis_start_x_plus_rad_le_grid_x_size( conf, this_source_config_part ) + self.axis_start_y_min_rad_ge_zero( conf, this_source_config_part ) + self.axis_start_y_plus_rad_le_grid_y_size( conf, this_source_config_part ) + self.axis_start_z_min_rad_ge_zero( conf, this_source_config_part ) + self.axis_start_z_plus_rad_le_grid_z_size( conf, this_source_config_part ) + self.axis_end_x_min_rad_ge_zero( conf, this_source_config_part ) + self.axis_end_x_plus_rad_le_grid_x_size( conf, this_source_config_part ) + self.axis_end_y_min_rad_ge_zero( conf, this_source_config_part ) + self.axis_end_y_plus_rad_le_grid_y_size( conf, this_source_config_part ) + self.axis_end_z_min_rad_ge_zero( conf, this_source_config_part ) + self.axis_end_z_plus_rad_le_grid_z_size( conf, this_source_config_part ) + + + def set_tube_parameters_from_config( self, this_source_config_part ): + self.axis_start_x = this_source_config_part.getfloat("tube_axis_start_x") + self.axis_start_y = this_source_config_part.getfloat("tube_axis_start_y") + self.axis_start_z = this_source_config_part.getfloat("tube_axis_start_z") + self.axis_end_x = this_source_config_part.getfloat("tube_axis_end_x") + self.axis_end_y = this_source_config_part.getfloat("tube_axis_end_y") + self.axis_end_z = this_source_config_part.getfloat("tube_axis_end_z") + self.inner_radius = this_source_config_part.getfloat("tube_inner_radius") + self.outer_radius = this_source_config_part.getfloat("tube_outer_radius") + + + def read_hdf5_tube_parameters( self, this_source_h5_group ): + self.axis_start_x = this_source_h5_group.attrs["tube_axis_start_x"] + self.axis_start_y = this_source_h5_group.attrs["tube_axis_start_y"] + self.axis_start_z = this_source_h5_group.attrs["tube_axis_start_z"] + self.axis_end_x = this_source_h5_group.attrs["tube_axis_end_x"] + self.axis_end_y = this_source_h5_group.attrs["tube_axis_end_y"] + self.axis_end_z = this_source_h5_group.attrs["tube_axis_end_z"] + self.inner_radius = this_source_h5_group.attrs["tube_inner_radius"] + self.outer_radius = this_source_h5_group.attrs["tube_outer_radius"] + + + def inner_radius_gt_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_inner_radius") <= 0: + raise ValueError( "inner_radius <= 0" ) + + + def outer_radius_gt_inner_radius( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_outer_radius") <= \ + this_source_config_part.getfloat("tube_inner_radius"): + raise ValueError( "outer_radius <= inner_radius" ) + + + def axis_start_x_min_rad_ge_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_start_x") - \ + this_source_config_part.getfloat("tube_outer_radius") < 0: + raise ValueError( "tube_axis_start_x - tube_outer_radius < 0" ) + + + def axis_start_x_plus_rad_le_grid_x_size( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_start_x") + \ + this_source_config_part.getfloat("tube_outer_radius") > \ + conf["Spatial mesh"].getfloat("grid_x_size"): + raise ValueError( "tube_axis_start_x + tube_outer_radius > grid_x_size" ) + + + def axis_start_y_min_rad_ge_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_start_y") - \ + this_source_config_part.getfloat("tube_outer_radius") < 0: + raise ValueError( "tube_axis_start_y - tube_outer_radius < 0" ) + + + def axis_start_y_plus_rad_le_grid_y_size( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_start_y") + \ + this_source_config_part.getfloat("tube_outer_radius") > \ + conf["Spatial mesh"].getfloat("grid_y_size"): + raise ValueError( "tube_axis_start_y + tube_outer_radius > grid_y_size" ) + + + def axis_start_z_min_rad_ge_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_start_z") - \ + this_source_config_part.getfloat("tube_outer_radius") < 0: + raise ValueError( "tube_axis_start_z - tube_outer_radius < 0" ) + + + def axis_start_z_plus_rad_le_grid_z_size( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_start_z") + \ + this_source_config_part.getfloat("tube_outer_radius") > \ + conf["Spatial mesh"].getfloat("grid_z_size"): + raise ValueError( "tube_axis_start_z + tube_outer_radius > grid_z_size" ) + + + def axis_end_x_min_rad_ge_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_end_x") - \ + this_source_config_part.getfloat("tube_outer_radius") < 0: + raise ValueError( "tube_axis_end_x - tube_outer_radius < 0" ) + + + def axis_end_x_plus_rad_le_grid_x_size( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_end_x") + \ + this_source_config_part.getfloat("tube_outer_radius") >\ + conf["Spatial mesh"].getfloat("grid_x_size"): + raise ValueError ("tube_axis_end_x + tube_outer_radius > grid_x_size" ) + + + def axis_end_y_min_rad_ge_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_end_y") - \ + this_source_config_part.getfloat("tube_outer_radius") < 0: + raise ValueError( "tube_axis_end_y - tube_outer_radius < 0" ) + + + def axis_end_y_plus_rad_le_grid_y_size( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_end_y") + \ + this_source_config_part.getfloat("tube_outer_radius") > \ + conf["Spatial mesh"].getfloat("grid_y_size"): + raise ValueError( "tube_axis_end_y + tube_outer_radius > grid_y_size" ) + + + def axis_end_z_min_rad_ge_zero( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_end_z") - \ + this_source_config_part.getfloat("tube_outer_radius") < 0: + raise ValueError( "tube_axis_end_z - tube_outer_radius < 0" ) + + + def axis_end_z_plus_rad_le_grid_z_size( self, conf, this_source_config_part ): + if this_source_config_part.getfloat("tube_axis_end_z") + \ + this_source_config_part.getfloat("tube_outer_radius") > \ + conf["Spatial mesh"].getfloat("grid_z_size"): + raise ValueError( "tube_axis_end_z + tube_outer_radius > grid_z_size" ) + + + def write_hdf5_source_parameters( self, this_source_h5_group ): + super().write_hdf5_source_parameters( this_source_h5_group ) + this_source_h5_group.attrs.create( "tube_axis_start_x", self.axis_start_x ) + this_source_h5_group.attrs.create( "tube_axis_start_y", self.axis_start_y ) + this_source_h5_group.attrs.create( "tube_axis_start_z", self.axis_start_z ) + this_source_h5_group.attrs.create( "tube_axis_end_x", self.axis_end_x ) + this_source_h5_group.attrs.create( "tube_axis_end_y", self.axis_end_y ) + this_source_h5_group.attrs.create( "tube_axis_end_z", self.axis_end_z ) + this_source_h5_group.attrs.create( "tube_inner_radius", self.inner_radius ) + this_source_h5_group.attrs.create( "tube_outer_radius", self.outer_radius ) + + + def uniform_position_in_source( self ): + return self.uniform_position_in_tube() + + + def uniform_position_in_tube( self ): + # random point in tube along z + cyl_axis = Vec3d( ( self.axis_end_x - self.axis_start_x ), + ( self.axis_end_y - self.axis_start_y ), + ( self.axis_end_z - self.axis_start_z ) ) + cyl_axis_length = cyl_axis.length() + r = sqrt( self.random_in_range( self.inner_radius / self.outer_radius, 1.0 ) ) \ + * self.outer_radius + phi = self.random_in_range( 0.0, 2.0 * np.pi ) + z = self.random_in_range( 0.0, cyl_axis_length ) + # + x = r * np.cos( phi ) + y = r * np.sin( phi ) + z = z + random_pnt_in_cyl_along_z = Vec3d( x, y, z ) + # rotate: + # see "https://en.wikipedia.org/wiki/Rodrigues'_rotation_formula" + # todo: Too complicated. Try rejection sampling. + unit_cyl_axis = cyl_axis.normalized() + unit_along_z = Vec3d( 0, 0, 1.0 ) + rotation_axis = unit_along_z.cross_product( unit_cyl_axis ) + rotation_axis_length = rotation_axis.length() + if rotation_axis_length == 0: + if copysign( 1.0, unit_cyl_axis.z ) >= 0: + random_pnt_in_rotated_cyl = random_pnt_in_cyl_along_z + else: + random_pnt_in_rotated_cyl = random_pnt_in_cyl_along_z.negate() + else: + unit_rotation_axis = rotation_axis.normalized() + rot_cos = unit_cyl_axis.dot_product( unit_along_z ) + rot_sin = rotation_axis_length + random_pnt_in_rotated_cyl = \ + random_pnt_in_cyl_along_z.times_scalar( rot_cos ) + \ + unit_rotation_axis.cross_product( random_pnt_in_cyl_along_z ) * rot_sin + \ + unit_rotation_axis.times_scalar( \ + ( 1 - rot_cos ) * \ + unit_rotation_axis.dot_product( random_pnt_in_cyl_along_z ) ) + # shift: + shifted = random_pnt_in_rotated_cyl.add( + Vec3d( self.axis_start_x, + self.axis_start_y, + self.axis_start_z ) ) + return shifted + + + @classmethod + def is_tube_source( cls, conf_sec_name ): + return 'Particle_source_tube' in conf_sec_name diff --git a/ParticleSourcesManager.py b/ParticleSourcesManager.py index 03210eaa..bae97867 100644 --- a/ParticleSourcesManager.py +++ b/ParticleSourcesManager.py @@ -4,6 +4,7 @@ from ParticleSource import * from ParticleSourceBox import * from ParticleSourceCylinder import * +from ParticleSourceTube import * class ParticleSourcesManager: @@ -26,6 +27,11 @@ def init_from_config( cls, conf ): ParticleSourceCylinder.init_from_config( conf, conf[sec_name], sec_name ) ) + elif ParticleSourceTube.is_tube_source( sec_name ): + new_obj.sources.append( + ParticleSourceTube.init_from_config( conf, + conf[sec_name], + sec_name ) ) return new_obj @@ -46,6 +52,9 @@ def parse_hdf5_particle_source( self, this_source_h5_group ): elif geometry_type == "cylinder": self.sources.append( ParticleSourceCylinder.init_from_h5( this_source_h5_group ) ) + elif geometry_type == "tube": + self.sources.append( + ParticleSourceTube.init_from_h5( this_source_h5_group ) ) else: print( "In Particle_source_manager constructor-from-h5: " "Unknown particle_source type. Aborting" ) From a8210b4a2c0fe174d4e1562304585a8f4564e237 Mon Sep 17 00:00:00 2001 From: noway Date: Thu, 12 Apr 2018 18:02:24 +0300 Subject: [PATCH 06/30] Tube source example config --- examples/tube_source_test/contour.conf | 59 ++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 examples/tube_source_test/contour.conf diff --git a/examples/tube_source_test/contour.conf b/examples/tube_source_test/contour.conf new file mode 100644 index 00000000..1f114aa8 --- /dev/null +++ b/examples/tube_source_test/contour.conf @@ -0,0 +1,59 @@ +# PIC simulation config. +# Do not change section and field names. + +# see estimates.py for parameter values +[Time grid] +total_time = 3.0e-9 +time_save_step = 3.0e-10 +time_step_size = 3.0e-11 + +[Spatial mesh] +grid_x_size = 5.0 +grid_x_step = 0.1 +grid_y_size = 5.0 +grid_y_step = 0.1 +grid_z_size = 10.0 +grid_z_step = 0.1 + +[Particle_source_tube.cathode_emitter] +initial_number_of_particles = 50000 +particles_to_generate_each_step = 50000 +tube_axis_start_x = 2.5 +tube_axis_start_y = 2.5 +tube_axis_start_z = 1.51 +tube_axis_end_x = 2.5 +tube_axis_end_y = 2.5 +tube_axis_end_z = 2.52 +tube_inner_radius = 0.2 +tube_outer_radius = 1.0 +mean_momentum_x = 0 +mean_momentum_y = 0 +mean_momentum_z = 6.641e-15 +temperature = 0.0 +charge = -1.799e-6 +mass = 3.672e-24 + +[Boundary conditions] +boundary_phi_left = 0.0 +boundary_phi_right = 0.0 +boundary_phi_bottom = 0.0 +boundary_phi_top = 0.0 +boundary_phi_near = 0.0 +boundary_phi_far = 0.0 + +[External_magnetic_field_uniform.mgn] +magnetic_field_x = 0.0 +magnetic_field_y = 0.0 +magnetic_field_z = 0.0 +speed_of_light = 3.0e10 + + +[Particle interaction model] +# 'noninteracting' or 'PIC'; without quotes +# particle_interaction_model = noninteracting +particle_interaction_model = PIC + +[Output filename] +# No quotes; no spaces till end of line +output_filename_prefix = contour_ +output_filename_suffix = .h5 From 6ec58d71d0eaa5e01bd37b3f95b903f9a78dd0e6 Mon Sep 17 00:00:00 2001 From: noway Date: Wed, 18 Apr 2018 10:28:15 +0300 Subject: [PATCH 07/30] Updated Readme --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d62f4817..c162c450 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,52 @@ -# ef_python -Ef, python version. +Ef is a software for simulation of charged particles dynamics. It's primary areas of application are accelerator science and plasma physics. Below are several examples of the simulations that can be done with this program: + +

+ + + +
+Single particle in uniform magnetic field; +      +Widening of a ribbon beam during the propagation +

+ +

+
+ +
+Ribbon beam in uniform magnetic field +

+ +

+
+ + +
+Potential of electron beam inside conducting tube; +      +Volt-Ampere characteristic of a planar diode +

+ +Ef focuses on nonrelativistic energies. +Particular emphasis is placed on low-energy beams, such that can be found in ion sources and electron guns. +A motivation behind the program, the scope and the general goals are discussed [here](https://github.com/epicf/ef/wiki/Motivation-and-Goals). + +Particles dynamics is traced under action of external electromagnetic fields. +Particle self-interaction is taken into account with [particle-in-cell](https://github.com/epicf/ef/wiki/What-It-Is-and-How-It-Works#intuitive-introduction-to-particle-in-cell-method) method. Detailed description of the mathematical model can be found [here](https://github.com/epicf/ef/wiki/What-It-Is-and-How-It-Works#mathematical-model-description). + +Attention is given to integration with CAD software to allow for simulation of complex real-life experimental setups. +An experimental plugin for FreeCAD [exists](https://github.com/epicf/ef/wiki/Freecad-and-Paraview). + + +Ef is a free software -- it's source code is open and avaible for +modification and redistribution. +Python (this one) and [C++](https://github.com/epicf/ef) versions are available. +Python version is easy to install and experiment with. +It's speed should be sufficient to perform example computations and small-scale +simulations but for large-scale simulations C++ version is the choice. + + +[Current features](https://github.com/epicf/ef/wiki/Current-Features-and-Development-Roadmap) +are described in detail in appropriate wiki sections, +as well as [installation procedure](https://github.com/epicf/ef/wiki/Installation). +Some usage [examples](https://github.com/epicf/ef/wiki/Examples) are also given. \ No newline at end of file From be1c404d5111e5a44a07e1113aeb7fada3d49bf9 Mon Sep 17 00:00:00 2001 From: noway Date: Sat, 12 May 2018 11:27:59 +0300 Subject: [PATCH 08/30] Several PEP8 formatting fixes in Domain.py --- Domain.py | 277 +++++++++++++++++++++++++++--------------------------- 1 file changed, 138 insertions(+), 139 deletions(-) diff --git a/Domain.py b/Domain.py index a14ee140..220ac0ec 100644 --- a/Domain.py +++ b/Domain.py @@ -1,4 +1,3 @@ -import sys import h5py from TimeGrid import TimeGrid @@ -15,49 +14,49 @@ class Domain(): - def __init__( self ): + def __init__(self): pass - + @classmethod - def init_from_config( cls, conf ): + def init_from_config(cls, conf): new_obj = cls() - new_obj.time_grid = TimeGrid.init_from_config( conf ) - new_obj.spat_mesh = SpatialMesh.init_from_config( conf ) + new_obj.time_grid = TimeGrid.init_from_config(conf) + new_obj.spat_mesh = SpatialMesh.init_from_config(conf) new_obj.inner_regions = InnerRegionsManager.init_from_config( - conf, new_obj.spat_mesh ) + conf, new_obj.spat_mesh) new_obj.particle_to_mesh_map = ParticleToMeshMap() - new_obj.field_solver = FieldSolver( new_obj.spat_mesh, new_obj.inner_regions ) - new_obj.particle_sources = ParticleSourcesManager.init_from_config( conf ) - new_obj.external_fields = ExternalFieldsManager.init_from_config( conf ) + new_obj.field_solver = FieldSolver(new_obj.spat_mesh, new_obj.inner_regions) + new_obj.particle_sources = ParticleSourcesManager.init_from_config(conf) + new_obj.external_fields = ExternalFieldsManager.init_from_config(conf) new_obj.particle_interaction_model = ParticleInteractionModel.init_from_config( - conf ) + conf) new_obj.output_filename_prefix = conf["Output filename"]["output_filename_prefix"] new_obj.output_filename_suffix = conf["Output filename"]["output_filename_suffix"] return new_obj - + @classmethod - def init_from_h5( cls, h5file ): + def init_from_h5(cls, h5file): new_obj = cls() - new_obj.time_grid = TimeGrid.init_from_h5( h5file["/Time_grid"] ) - new_obj.spat_mesh = SpatialMesh.init_from_h5( h5file["/Spatial_mesh"] ) + new_obj.time_grid = TimeGrid.init_from_h5(h5file["/Time_grid"]) + new_obj.spat_mesh = SpatialMesh.init_from_h5(h5file["/Spatial_mesh"]) new_obj.inner_regions = InnerRegionsManager.init_from_h5( - h5file["/Inner_regions"], new_obj.spat_mesh ) + h5file["/Inner_regions"], new_obj.spat_mesh) new_obj.particle_to_mesh_map = ParticleToMeshMap() - new_obj.field_solver = FieldSolver( new_obj.spat_mesh, new_obj.inner_regions ) + new_obj.field_solver = FieldSolver(new_obj.spat_mesh, new_obj.inner_regions) new_obj.particle_sources = ParticleSourcesManager.init_from_h5( - h5file["/Particle_sources"] ) + h5file["/Particle_sources"]) new_obj.external_fields = ExternalFieldsManager.init_from_h5( - h5file["/External_fields"] ) + h5file["/External_fields"]) new_obj.particle_interaction_model = ParticleInteractionModel.init_from_h5( - h5file["/Particle_interaction_model"] ) + h5file["/Particle_interaction_model"]) # todo: pass output filename prefix and suffix as arguments # and call a method to set them here. return new_obj - - def start_pic_simulation( self ): + + def start_pic_simulation(self): # fields in domain without any particles self.eval_and_write_fields_without_particles() # generate particles and write initial step @@ -66,23 +65,23 @@ def start_pic_simulation( self ): # run simulation self.run_pic() - - def continue_pic_simulation( self ): + + def continue_pic_simulation(self): self.run_pic() - def run_pic( self ): + def run_pic(self): total_time_iterations = self.time_grid.total_nodes - 1 current_node = self.time_grid.current_node - for i in range( current_node, total_time_iterations ): - print( "Time step from {:d} to {:d} of {:d}".format( - i, i+1, total_time_iterations ) ) + for i in range(current_node, total_time_iterations): + print("Time step from {:d} to {:d} of {:d}".format( + i, i+1, total_time_iterations)) self.advance_one_time_step() self.write_step_to_save() #self.particle_sources.print_num_of_particles() - - def prepare_boris_integration( self ): + + def prepare_boris_integration(self): if self.particle_interaction_model.noninteracting: self.shift_velocities_half_time_step_back() elif self.particle_interaction_model.pic: @@ -90,8 +89,8 @@ def prepare_boris_integration( self ): self.eval_potential_and_fields() self.shift_velocities_half_time_step_back() - - def advance_one_time_step( self ): + + def advance_one_time_step(self): if self.particle_interaction_model.noninteracting: self.push_particles() self.apply_domain_constrains() @@ -103,23 +102,23 @@ def advance_one_time_step( self ): self.eval_potential_and_fields() self.update_time_grid() - - def eval_charge_density( self ): + + def eval_charge_density(self): self.spat_mesh.clear_old_density_values() self.particle_to_mesh_map.weight_particles_charge_to_mesh( - self.spat_mesh, self.particle_sources ) + self.spat_mesh, self.particle_sources) + + def eval_potential_and_fields(self): + self.field_solver.eval_potential(self.spat_mesh, self.inner_regions) + self.field_solver.eval_fields_from_potential(self.spat_mesh) - def eval_potential_and_fields( self ): - self.field_solver.eval_potential( self.spat_mesh, self.inner_regions ) - self.field_solver.eval_fields_from_potential( self.spat_mesh ) - - def push_particles( self ): + def push_particles(self): self.boris_integration() - def apply_domain_constrains( self ): + def apply_domain_constrains(self): # First generate then remove. # This allows for overlap of source and inner region. self.generate_new_particles() @@ -130,127 +129,127 @@ def apply_domain_constrains( self ): # Push particles # - def boris_integration( self ): + def boris_integration(self): dt = self.time_grid.time_step_size - self.update_momentum( dt ) - self.update_position( dt ) + self.update_momentum(dt) + self.update_position(dt) - def shift_velocities_half_time_step_back( self ): + def shift_velocities_half_time_step_back(self): minus_half_dt = -self.time_grid.time_step_size / 2.0 - # + # for src in self.particle_sources.sources: for p in src.particles: if not p.momentum_is_half_time_step_shifted: total_el_field = Vec3d.zero() for f in self.external_fields.electric: el_field = f.field_at_particle_position( - p, self.time_grid.current_time ) - total_el_field = total_el_field.add( el_field ) + p, self.time_grid.current_time) + total_el_field = total_el_field.add(el_field) pic_el_field = self.particle_to_mesh_map.field_at_particle_position( - self.spat_mesh, p ) - total_el_field = total_el_field.add( pic_el_field ) - # + self.spat_mesh, p) + total_el_field = total_el_field.add(pic_el_field) + # total_mgn_field = Vec3d.zero() for f in self.external_fields.magnetic: mgn_field = f.field_at_particle_position( - p, self.time_grid.current_time ) - total_mgn_field = total_mgn_field.add( mgn_field ) + p, self.time_grid.current_time) + total_mgn_field = total_mgn_field.add(mgn_field) # - if len( self.external_fields.magnetic ) == 0: - dp = total_el_field.times_scalar( p.charge * minus_half_dt ) - p.momentum = p.momentum.add( dp ) + if len(self.external_fields.magnetic) == 0: + dp = total_el_field.times_scalar(p.charge * minus_half_dt) + p.momentum = p.momentum.add(dp) else: q_quote = minus_half_dt * p.charge / p.mass / 2.0 - half_el_force = total_el_field.times_scalar( q_quote ) - v_current = p.momentum.times_scalar( 1.0 / p.mass ) - u = v_current.add( half_el_force ) + half_el_force = total_el_field.times_scalar(q_quote) + v_current = p.momentum.times_scalar(1.0 / p.mass) + u = v_current.add(half_el_force) h = total_mgn_field.times_scalar( - q_quote / physical_constants.speed_of_light ) + q_quote / physical_constants.speed_of_light) s = h.times_scalar( - 2.0 / ( 1.0 + h.dot_product( h ) ) ) - tmp = u.add( u.cross_product( h ) ) - u_quote = u.add( tmp.cross_product( s ) ) - p.momentum = u_quote.add( half_el_force ).times_scalar( p.mass ) + 2.0 / (1.0 + h.dot_product(h))) + tmp = u.add(u.cross_product(h)) + u_quote = u.add(tmp.cross_product(s)) + p.momentum = u_quote.add(half_el_force).times_scalar(p.mass) p.momentum_is_half_time_step_shifted = True - def update_momentum( self, dt ): + def update_momentum(self, dt): for src in self.particle_sources.sources: for p in src.particles: total_el_field = Vec3d.zero() for f in self.external_fields.electric: el_field = f.field_at_particle_position( - p, self.time_grid.current_time ) - total_el_field = total_el_field.add( el_field ) + p, self.time_grid.current_time) + total_el_field = total_el_field.add(el_field) pic_el_field = self.particle_to_mesh_map.field_at_particle_position( - self.spat_mesh, p ) - total_el_field = total_el_field.add( pic_el_field ) - # + self.spat_mesh, p) + total_el_field = total_el_field.add(pic_el_field) + # total_mgn_field = Vec3d.zero() for f in self.external_fields.magnetic: mgn_field = f.field_at_particle_position( - p, self.time_grid.current_time ) - total_mgn_field = total_mgn_field.add( mgn_field ) + p, self.time_grid.current_time) + total_mgn_field = total_mgn_field.add(mgn_field) # - if len( self.external_fields.magnetic ) == 0: - dp = total_el_field.times_scalar( p.charge * dt ) - p.momentum = p.momentum.add( dp ) + if len(self.external_fields.magnetic) == 0: + dp = total_el_field.times_scalar(p.charge * dt) + p.momentum = p.momentum.add(dp) else: q_quote = dt * p.charge / p.mass / 2.0 - half_el_force = total_el_field.times_scalar( q_quote ) - v_current = p.momentum.times_scalar( 1.0 / p.mass ) - u = v_current.add( half_el_force ) + half_el_force = total_el_field.times_scalar(q_quote) + v_current = p.momentum.times_scalar(1.0 / p.mass) + u = v_current.add(half_el_force) h = total_mgn_field.times_scalar( - q_quote / physical_constants.speed_of_light ) + q_quote / physical_constants.speed_of_light) s = h.times_scalar( - 2.0 / ( 1.0 + h.dot_product( h ) ) ) - tmp = u.add( u.cross_product( h ) ) - u_quote = u.add( tmp.cross_product( s ) ) - p.momentum = u_quote.add( half_el_force ).times_scalar( p.mass ) - + 2.0 / (1.0 + h.dot_product(h))) + tmp = u.add(u.cross_product(h)) + u_quote = u.add(tmp.cross_product(s)) + p.momentum = u_quote.add(half_el_force).times_scalar(p.mass) - def update_position( self, dt ): - self.particle_sources.update_particles_position( dt ) + + def update_position(self, dt): + self.particle_sources.update_particles_position(dt) # # Apply domain constrains # - def apply_domain_boundary_conditions( self ): + def apply_domain_boundary_conditions(self): for src in self.particle_sources.sources: - src.particles[:] = [ p for p in src.particles if not self.out_of_bound( p ) ] + src.particles[:] = [p for p in src.particles if not self.out_of_bound(p)] - def remove_particles_inside_inner_regions( self ): + def remove_particles_inside_inner_regions(self): for src in self.particle_sources.sources: src.particles[:] = \ - [ p for p in src.particles \ - if not self.inner_regions.check_if_particle_inside_and_count_charge( p ) ] - + [p for p in src.particles \ + if not self.inner_regions.check_if_particle_inside_and_count_charge(p)] + - def out_of_bound( self, particle ): + def out_of_bound(self, particle): x = particle.position.x y = particle.position.y - z = particle.position.z - out = ( x >= self.spat_mesh.x_volume_size ) or ( x <= 0 ) \ + z = particle.position.z + out = (x >= self.spat_mesh.x_volume_size) or (x <= 0) \ or \ - ( y >= self.spat_mesh.y_volume_size ) or ( y <= 0 ) \ + (y >= self.spat_mesh.y_volume_size) or (y <= 0) \ or \ - ( z >= self.spat_mesh.z_volume_size ) or ( z <= 0 ) + (z >= self.spat_mesh.z_volume_size) or (z <= 0) return out - def generate_new_particles( self ): + def generate_new_particles(self): self.particle_sources.generate_each_step() self.shift_velocities_half_time_step_back() - + # # Update time grid # - def update_time_grid( self ): + def update_time_grid(self): self.time_grid.update_to_next_step() @@ -258,79 +257,79 @@ def update_time_grid( self ): # Write domain to file # - def write_step_to_save( self ): + def write_step_to_save(self): current_step = self.time_grid.current_node step_to_save = self.time_grid.node_to_save - if ( current_step % step_to_save ) == 0 : + if (current_step % step_to_save) == 0: self.write() - - def write( self ): + + def write(self): file_name_to_write = self.construct_output_filename( self.output_filename_prefix, self.time_grid.current_node, - self.output_filename_suffix ) - h5file = h5py.File( file_name_to_write, mode = "w" ) + self.output_filename_suffix) + h5file = h5py.File(file_name_to_write, mode="w") # todo: add exception # if not h5file: - # print( "Error: can't open file " + file_name_to_write + \ - # "to save results of initial field calculation!" ) - # print( "Recheck \'output_filename_prefix\' key in config file." ) - # print( "Make sure the directory you want to save to exists." ) - # print( "Writing initial fields to file " + file_name_to_write ) - print( "Writing step {} to file {}".format( - self.time_grid.current_node, file_name_to_write ) ) - self.time_grid.write_to_file( h5file ) - self.spat_mesh.write_to_file( h5file ) - self.particle_sources.write_to_file( h5file ) - self.inner_regions.write_to_file( h5file ) - self.external_fields.write_to_file( h5file ) - self.particle_interaction_model.write_to_file( h5file ) + # print("Error: can't open file " + file_name_to_write + \ + # "to save results of initial field calculation!") + # print("Recheck \'output_filename_prefix\' key in config file.") + # print("Make sure the directory you want to save to exists.") + # print("Writing initial fields to file " + file_name_to_write) + print("Writing step {} to file {}".format( + self.time_grid.current_node, file_name_to_write)) + self.time_grid.write_to_file(h5file) + self.spat_mesh.write_to_file(h5file) + self.particle_sources.write_to_file(h5file) + self.inner_regions.write_to_file(h5file) + self.external_fields.write_to_file(h5file) + self.particle_interaction_model.write_to_file(h5file) h5file.close() - - def construct_output_filename( self, output_filename_prefix, - current_time_step, output_filename_suffix ): + + def construct_output_filename(self, output_filename_prefix, + current_time_step, output_filename_suffix): filename = output_filename_prefix + \ - "{:07d}".format( current_time_step ) + \ + "{:07d}".format(current_time_step) + \ output_filename_suffix return filename - + # # Free domain # - def free( self ): - print( "TODO: free domain.\n" ) + def free(self): + print("TODO: free domain.\n") # # Various functions # - def set_output_filename_prefix_and_suffix( self, prefix, suffix ): + def set_output_filename_prefix_and_suffix(self, prefix, suffix): self.output_filename_prefix = prefix self.output_filename_suffix = suffix - def print_particles( self ): + def print_particles(self): self.particle_sources.print_particles() - def eval_and_write_fields_without_particles( self ): + def eval_and_write_fields_without_particles(self): self.spat_mesh.clear_old_density_values() - self.eval_potential_and_fields() + self.eval_potential_and_fields() file_name_to_write = self.output_filename_prefix + \ "fieldsWithoutParticles" + \ self.output_filename_suffix - h5file = h5py.File( file_name_to_write, mode = "w" ) + h5file = h5py.File(file_name_to_write, mode="w") # todo: add exception # if not h5file: - # print( "Error: can't open file " + file_name_to_write + \ - # "to save results of initial field calculation!" ) - # print( "Recheck \'output_filename_prefix\' key in config file." ) - # print( "Make sure the directory you want to save to exists." ) - # print( "Writing initial fields to file " + file_name_to_write ) - self.spat_mesh.write_to_file( h5file ) - self.external_fields.write_to_file( h5file ) - self.inner_regions.write_to_file( h5file ) + # print("Error: can't open file " + file_name_to_write + \ + # "to save results of initial field calculation!") + # print("Recheck \'output_filename_prefix\' key in config file.") + # print("Make sure the directory you want to save to exists.") + # print("Writing initial fields to file " + file_name_to_write) + self.spat_mesh.write_to_file(h5file) + self.external_fields.write_to_file(h5file) + self.inner_regions.write_to_file(h5file) h5file.close() From 0cda1674b7842d521948e4e1665853c5d5c2774d Mon Sep 17 00:00:00 2001 From: noway Date: Sat, 12 May 2018 11:51:46 +0300 Subject: [PATCH 09/30] PEP8 in main.py; echo_config --- Domain.py | 58 ++++++++++++++++++++++++++------------------------ main.py | 63 +++++++++++++++++++++++++------------------------------ 2 files changed, 59 insertions(+), 62 deletions(-) diff --git a/Domain.py b/Domain.py index 220ac0ec..ac1ea1c1 100644 --- a/Domain.py +++ b/Domain.py @@ -15,7 +15,16 @@ class Domain(): def __init__(self): - pass + self.time_grid = None + self.spat_mesh = None + self.inner_regions = None + self.particle_to_mesh_map = None + self.field_solver = None + self.particle_sources = None + self.external_fields = None + self.particle_interaction_model = None + self.output_filename_prefix = None + self.output_filename_suffix = None @classmethod @@ -37,7 +46,7 @@ def init_from_config(cls, conf): @classmethod - def init_from_h5(cls, h5file): + def init_from_h5(cls, h5file, filename_prefix, filename_suffix): new_obj = cls() new_obj.time_grid = TimeGrid.init_from_h5(h5file["/Time_grid"]) new_obj.spat_mesh = SpatialMesh.init_from_h5(h5file["/Spatial_mesh"]) @@ -51,8 +60,8 @@ def init_from_h5(cls, h5file): h5file["/External_fields"]) new_obj.particle_interaction_model = ParticleInteractionModel.init_from_h5( h5file["/Particle_interaction_model"]) - # todo: pass output filename prefix and suffix as arguments - # and call a method to set them here. + new_obj.output_filename_prefix = filename_prefix + new_obj.output_filename_suffix = filename_suffix return new_obj @@ -125,9 +134,9 @@ def apply_domain_constrains(self): self.apply_domain_boundary_conditions() self.remove_particles_inside_inner_regions() - # - # Push particles - # +# +# Push particles +# def boris_integration(self): dt = self.time_grid.time_step_size @@ -212,9 +221,9 @@ def update_momentum(self, dt): def update_position(self, dt): self.particle_sources.update_particles_position(dt) - # - # Apply domain constrains - # +# +# Apply domain constrains +# def apply_domain_boundary_conditions(self): for src in self.particle_sources.sources: @@ -245,17 +254,17 @@ def generate_new_particles(self): self.shift_velocities_half_time_step_back() - # - # Update time grid - # +# +# Update time grid +# def update_time_grid(self): self.time_grid.update_to_next_step() - # - # Write domain to file - # +# +# Write domain to file +# def write_step_to_save(self): current_step = self.time_grid.current_node @@ -295,21 +304,16 @@ def construct_output_filename(self, output_filename_prefix, return filename - # - # Free domain - # +# +# Free domain +# def free(self): print("TODO: free domain.\n") - # - # Various functions - # - - def set_output_filename_prefix_and_suffix(self, prefix, suffix): - self.output_filename_prefix = prefix - self.output_filename_suffix = suffix - +# +# Various functions +# def print_particles(self): self.particle_sources.print_particles() diff --git a/main.py b/main.py index 4a39a26b..d127b22b 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,10 @@ import re import sys -import h5py import argparse import configparser +import h5py + from Domain import Domain @@ -11,61 +12,53 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument("config_or_h5_file", help="Config or h5 file") args = parser.parse_args() - #print( args.config_or_h5_file ) config_or_h5_file = args.config_or_h5_file - #config_or_h5_file = parse_cmd_line() continue_from_h5 = False - dom, continue_from_h5 = construct_domain( config_or_h5_file ) - # + dom, continue_from_h5 = construct_domain(config_or_h5_file) if continue_from_h5: dom.continue_pic_simulation() else: dom.start_pic_simulation() - # return 0 -def construct_domain( config_or_h5_file ): - extension = config_or_h5_file[ config_or_h5_file.rfind(".") + 1 : ] +def construct_domain(config_or_h5_file): + extension = config_or_h5_file[config_or_h5_file.rfind(".") + 1:] if extension == "h5": - h5file = h5py.File( config_or_h5_file, 'r' ) - # if h5file_id < 0: - # print( "Can't open file: ", config_or_h5_file ) - # sys.exit( -1 ) - filename_prefix, filename_suffix = \ - extract_filename_prefix_and_suffix_from_h5filename( config_or_h5_file ) - dom = Domain.init_from_h5( h5file ) - dom.set_output_filename_prefix_and_suffix( filename_prefix, filename_suffix ) - continue_from_h5 = True + with h5py.File(config_or_h5_file, 'r') as h5file: + filename_prefix, filename_suffix = \ + extract_filename_prefix_and_suffix_from_h5filename(config_or_h5_file) + dom = Domain.init_from_h5(h5file, filename_prefix, filename_suffix) + continue_from_h5 = True else: conf = configparser.ConfigParser() - conf.read( config_or_h5_file ) - # print( config_or_h5_file ) - # for s in conf.sections(): - # print( s ) - # for k in conf[s]: - # print(" ", k, conf[s][k] ) - dom = Domain.init_from_config( conf ) + conf.read(config_or_h5_file) + echo_config(config_or_h5_file, conf) + dom = Domain.init_from_config(conf) continue_from_h5 = False return dom, continue_from_h5 -def parse_cmd_line(): - pass +def echo_config(config_or_h5_file, conf): + print("Config file is: ", config_or_h5_file) + for s in list(conf.sections()): + print("[", s, "]") + for k, v in list(conf[s]): + print("{} = {}".format(k, v)) + print() -def extract_filename_prefix_and_suffix_from_h5filename( h5_file ): +def extract_filename_prefix_and_suffix_from_h5filename(h5_file): rgx = "[0-9]{7}" # search for timestep in filename (7 digits in a row) - match = re.search( rgx, h5_file ) + match = re.search(rgx, h5_file) if match: - prefix = h5_file[ 0:match.start() ] - suffix = h5_file[ match.end(): ] - print( "Extracted h5 prefix and suffix:", prefix, suffix ) + prefix = h5_file[0:match.start()] + suffix = h5_file[match.end():] + print("Extracted h5 prefix and suffix:", prefix, suffix) else: - print( "Can't identify filename prefix and suffix in ", h5_file ) - print( "Aborting." ) - sys.exit( -1 ) - # + print("Can't identify filename prefix and suffix in ", h5_file) + print("Aborting.") + sys.exit(-1) return prefix, suffix From a97393af1ec53196af3da092b48e21b7a0d5afa6 Mon Sep 17 00:00:00 2001 From: noway Date: Sat, 12 May 2018 12:34:57 +0300 Subject: [PATCH 10/30] PEP8 and 'binary' option in ParticleInteractionMod --- ParticleInteractionModel.py | 82 +++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/ParticleInteractionModel.py b/ParticleInteractionModel.py index 28ff5f9e..4b087ae1 100644 --- a/ParticleInteractionModel.py +++ b/ParticleInteractionModel.py @@ -1,58 +1,68 @@ import sys -import h5py class ParticleInteractionModel(): - def __init__( self ): - self.noninteracting = self.pic = False - + def __init__(self): + self.noninteracting = self.binary = self.pic = False + self.particle_interaction_model = None + + @classmethod - def init_from_config( cls, conf ): + def init_from_config(cls, conf): new_obj = cls() - new_obj.check_correctness_of_related_config_fields( conf ) - new_obj.get_values_from_config( conf ) + new_obj.check_correctness_of_related_config_fields(conf) + new_obj.get_values_from_config(conf) return new_obj - - def check_correctness_of_related_config_fields( self, conf ): - conf_part = conf["Particle interaction model"] - model = conf_part["particle_interaction_model"] - # 'PIC' or 'noninteracting' - if model != "noninteracting" and model != "PIC": - print( "Error: wrong value of 'particle_interaction_model': {}".format( model )) - print( "Allowed values : 'noninteracting', 'PIC'" ) - print( "Aborting" ) - sys.exit( -1 ) - - def get_values_from_config( self, conf ): + + + def check_correctness_of_related_config_fields(self, conf): + conf_part = conf["Particle interaction model"] + model = conf_part["particle_interaction_model"] + # 'PIC' or 'noninteracting' or 'binary' + if model != "noninteracting" and model != "binary" and model != "PIC": + print("Error: wrong value of 'particle_interaction_model': {}".format(model)) + print("Allowed values : 'noninteracting', 'binary', 'PIC'") + print("Aborting") + sys.exit(-1) + + + def get_values_from_config(self, conf): conf_part = conf["Particle interaction model"] - self.particle_interaction_model = conf_part["particle_interaction_model"] + self.particle_interaction_model = conf_part["particle_interaction_model"] if self.particle_interaction_model == "noninteracting": self.noninteracting = True + elif self.particle_interaction_model == "binary": + self.binary = True elif self.particle_interaction_model == "PIC": self.pic = True - + @classmethod - def init_from_h5( cls, h5group ): + def init_from_h5(cls, h5group): new_obj = cls() - new_obj.particle_interaction_model = \ - h5group.attrs["particle_interaction_model"] + new_obj.particle_interaction_model = h5group.attrs["particle_interaction_model"] if new_obj.particle_interaction_model == "noninteracting": new_obj.noninteracting = True + elif new_obj.particle_interaction_model == "binary": + new_obj.binary = True elif new_obj.particle_interaction_model == "PIC": new_obj.pic = True return new_obj - - def __str__( self ): - return "Particle interaction model = {}".format( self.particle_interaction_model ) - - def print( self ): - print( "### Particle_interaction_model:" ) - print( self ) - print( "self.noninteracting = {}".format( self.noninteracting ) ) - print( "self.pic = {}".format( self.pic ) ) - - def write_to_file( self, h5file ): + + + def __str__(self): + return "Particle interaction model = {}".format(self.particle_interaction_model) + + + def print(self): + print("### Particle_interaction_model:") + print(self) + print("self.noninteracting = {}".format(self.noninteracting)) + print("self.binary = {}".format(self.binary)) + print("self.pic = {}".format(self.pic)) + + + def write_to_file(self, h5file): groupname = "/Particle_interaction_model" - h5group = h5file.create_group( groupname ) + h5group = h5file.create_group(groupname) h5group.attrs["particle_interaction_model"] = self.particle_interaction_model From 8e43dbe15798fdf068e192e8db8e6f3eb7adaeeb Mon Sep 17 00:00:00 2001 From: noway Date: Sat, 12 May 2018 14:56:54 +0300 Subject: [PATCH 11/30] WIP on binary interaction --- Domain.py | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Particle.py | 50 +++++++++++++++++++++++------------------ 2 files changed, 93 insertions(+), 21 deletions(-) diff --git a/Domain.py b/Domain.py index ac1ea1c1..d7428bb0 100644 --- a/Domain.py +++ b/Domain.py @@ -93,6 +93,8 @@ def run_pic(self): def prepare_boris_integration(self): if self.particle_interaction_model.noninteracting: self.shift_velocities_half_time_step_back() + elif self.particle_interaction_model.binary: + self.shift_velocities_half_time_step_back() elif self.particle_interaction_model.pic: self.eval_charge_density() self.eval_potential_and_fields() @@ -104,6 +106,10 @@ def advance_one_time_step(self): self.push_particles() self.apply_domain_constrains() self.update_time_grid() + elif self.particle_interaction_model.binary: + self.push_particles_binary_interaction() + self.apply_domain_constrains() + self.update_time_grid() elif self.particle_interaction_model.pic: self.push_particles() self.apply_domain_constrains() @@ -127,6 +133,10 @@ def push_particles(self): self.boris_integration() + def push_particles_binary_interaction(self): + self.boris_integration_with_binary_force() + + def apply_domain_constrains(self): # First generate then remove. # This allows for overlap of source and inner region. @@ -221,6 +231,60 @@ def update_momentum(self, dt): def update_position(self, dt): self.particle_sources.update_particles_position(dt) + + def boris_integration_with_binary_force(self): + dt = self.time_grid.time_step_size + self.update_momentum_and_position_with_bin_force(dt) + + + def update_momentum_and_position_with_bin_force(self, dt): + # todo: same code 3 times. get rid of duplication. + for src_idx, src in enumerate(self.particle_sources.sources): + for p_idx, p in enumerat(src.particles): + total_el_field = Vec3d.zero() + for f in self.external_fields.electric: + el_field = f.field_at_particle_position(p, self.time_grid.current_time) + total_el_field = total_el_field.add(el_field) + bin_el_field = self.binary_field_at_particle_position(p, src_idx, p_idx) + total_el_field = total_el_field.add(bin_el_field) + # + total_mgn_field = Vec3d.zero() + for f in self.external_fields.magnetic: + mgn_field = f.field_at_particle_position( + p, self.time_grid.current_time) + total_mgn_field = total_mgn_field.add(mgn_field) + # + if len(self.external_fields.magnetic) == 0: + dp = total_el_field.times_scalar(p.charge * dt) + p.momentum = p.momentum.add(dp) + else: + q_quote = dt * p.charge / p.mass / 2.0 + half_el_force = total_el_field.times_scalar(q_quote) + v_current = p.momentum.times_scalar(1.0 / p.mass) + u = v_current.add(half_el_force) + h = total_mgn_field.times_scalar( + q_quote / physical_constants.speed_of_light) + s = h.times_scalar( + 2.0 / (1.0 + h.dot_product(h))) + tmp = u.add(u.cross_product(h)) + u_quote = u.add(tmp.cross_product(s)) + p.momentum = u_quote.add(half_el_force).times_scalar(p.mass) + + + def binary_field_at_particle_position(self, particle, src_idx, p_idx): + bin_force = Vec3d.zero() + for iter_src_idx, src in enumerate(self.particle_sources.sources): + if iter_src_idx != src_idx: + for p in src.particles: + bin_force.add(p.field_at_point(particle.position)) + else: + tmp_p = src.particles[0] + src.particles[0] = particle + src.particles[p_idx] = tmp_p + for p in src.particles[1:]: + bin_force.add(p.field_at_point(particle.position)) + return bin_force + # # Apply domain constrains # diff --git a/Particle.py b/Particle.py index 9be3de38..08424a13 100644 --- a/Particle.py +++ b/Particle.py @@ -1,8 +1,8 @@ from Vec3d import * class Particle(): - - def __init__( self, id, charge, mass, position, momentum ): + + def __init__(self, id, charge, mass, position, momentum): self.id = id self.charge = charge self.mass = mass @@ -10,25 +10,33 @@ def __init__( self, id, charge, mass, position, momentum ): self.momentum = momentum self.momentum_is_half_time_step_shifted = False - def update_position( self, dt ): - pos_shift = self.momentum.times_scalar( dt / self.mass ) - self.position = self.position.add( pos_shift ) + def update_position(self, dt): + pos_shift = self.momentum.times_scalar(dt / self.mass) + self.position = self.position.add(pos_shift) + + def field_at_point(self, point): + dist = point.sub(self.position) + dist_len = dist.length() + if dist_len == 0: + return None + dist_len_cube = dist_len ** 3 + return dist.times_scalar(self.charge / dist_len_cube) - def print_long( self ): - print( "Particle: " ) - print( "id: {},".format( self.id ) ) - print( "charge = {:.3f}, mass = {:.3f}, ".format( self.charge, self.mass ) ) - print( "pos(x,y,z) = ( {:.2f}, {:.2f}, {:.2f} )".format( self.position.x, - self.position.y, - self.position.z ) ) - print( "momentum(px,py,pz) = ( {:.2f}, {:.2f}, {:.2f} )".format( self.momentum.x, - self.momentum.y, - self.momentum.z ) ) + def print_long(self): + print("Particle: ") + print("id: {},".format(self.id)) + print("charge = {:.3f}, mass = {:.3f}, ".format(self.charge, self.mass)) + print("pos(x,y,z) = ({:.2f}, {:.2f}, {:.2f})".format(self.position.x, + self.position.y, + self.position.z)) + print("momentum(px,py,pz) = ({:.2f}, {:.2f}, {:.2f})".format(self.momentum.x, + self.momentum.y, + self.momentum.z)) - def print_short( self ): - print( "id: {} x = {:.2f} y = {:.2f} z = {:.2f} " - "px = {:.2f} py = {:.2f} pz = {:.2f}".format( - self.id, - self.position.x, self.position.y, self.position.z, - self.momentum.x, self.momentum.y, self.momentum.z ) ) + def print_short(self): + print("id: {} x = {:.2f} y = {:.2f} z = {:.2f} " + "px = {:.2f} py = {:.2f} pz = {:.2f}".format( + self.id, + self.position.x, self.position.y, self.position.z, + self.momentum.x, self.momentum.y, self.momentum.z)) From 5a0734e5d562cc0123a4c4c423f3dd2d9debb178 Mon Sep 17 00:00:00 2001 From: noway Date: Sun, 13 May 2018 13:01:16 +0300 Subject: [PATCH 12/30] PEP8 in ExternalFieldsManager --- ExternalFieldsManager.py | 58 +++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/ExternalFieldsManager.py b/ExternalFieldsManager.py index ade5054c..0c242760 100644 --- a/ExternalFieldsManager.py +++ b/ExternalFieldsManager.py @@ -5,65 +5,63 @@ class ExternalFieldsManager(): - def __init__( self ): + def __init__(self): self.electric = [] self.magnetic = [] - pass - @classmethod - def init_from_config( cls, conf ): + + @classmethod + def init_from_config(cls, conf): new_obj = cls() new_obj.electric = [] new_obj.magnetic = [] for sec_name in conf: - if ExternalFieldMagneticUniform.is_magnetic_uniform_config_part( sec_name ): + if ExternalFieldMagneticUniform.is_magnetic_uniform_config_part(sec_name): new_obj.magnetic.append( - ExternalFieldMagneticUniform.init_from_config( - conf[sec_name], sec_name ) ) - elif ExternalFieldElectricUniform.is_electric_uniform_config_part( sec_name ): + ExternalFieldMagneticUniform.init_from_config(conf[sec_name], sec_name)) + elif ExternalFieldElectricUniform.is_electric_uniform_config_part(sec_name): new_obj.electric.append( - ExternalFieldElectricUniform.init_from_config( - conf[ sec_name ], sec_name ) ) + ExternalFieldElectricUniform.init_from_config(conf[sec_name], sec_name)) return new_obj @classmethod - def init_from_h5( cls, h5_external_fields_group ): + def init_from_h5(cls, h5_external_fields_group): new_obj = cls() new_obj.electric = [] new_obj.magnetic = [] for field_name in h5_external_fields_group.keys(): - current_field_grpid = h5_external_fields_group[ field_name ] - new_obj.parse_hdf5_external_field( current_field_grpid ) + current_field_grpid = h5_external_fields_group[field_name] + new_obj.parse_hdf5_external_field(current_field_grpid) return new_obj - def parse_hdf5_external_field( self, current_field_grpid ): + def parse_hdf5_external_field(self, current_field_grpid): field_type = current_field_grpid.attrs["field_type"] if field_type == "magnetic_uniform": self.magnetic.append( - ExternalFieldMagneticUniform.init_from_h5( current_field_grpid ) ) + ExternalFieldMagneticUniform.init_from_h5(current_field_grpid)) elif field_type == "electric_uniform": self.electric.append( - ExternalFieldElectricUniform.init_from_h5( current_field_grpid ) ) + ExternalFieldElectricUniform.init_from_h5(current_field_grpid)) else: - print( "In External_field_manager constructor-from-h5: " ) - print( "Unknown external_field type. Aborting" ) - sys.exit( -1 ) + print("In External_field_manager constructor-from-h5: ") + print("Unknown external_field type. Aborting") + sys.exit(-1) + - - def write_to_file( self, hdf5_file_id ): - hdf5_groupname = "/External_fields"; - n_of_electric_fields = len( self.electric ) - n_of_magnetic_fields = len( self.magnetic ) - fields_group = hdf5_file_id.create_group( hdf5_groupname ) - fields_group.attrs.create( "number_of_electric_fields", n_of_electric_fields ) - fields_group.attrs.create( "number_of_magnetic_fields", n_of_magnetic_fields ) + def write_to_file(self, hdf5_file_id): + hdf5_groupname = "/External_fields" + n_of_electric_fields = len(self.electric) + n_of_magnetic_fields = len(self.magnetic) + fields_group = hdf5_file_id.create_group(hdf5_groupname) + fields_group.attrs.create("number_of_electric_fields", n_of_electric_fields) + fields_group.attrs.create("number_of_magnetic_fields", n_of_magnetic_fields) for el_field in self.electric: - el_field.write_to_file( fields_group ) + el_field.write_to_file(fields_group) for mgn_field in self.magnetic: - mgn_field.write_to_file( fields_group ) + mgn_field.write_to_file(fields_group) - def print_fields( self ): + def print_fields(self): for el_field in self.electric: el_field.print() for mgn_field in self.magnetic: From 4371022cd994265f35b38c843302d36e57f4bce3 Mon Sep 17 00:00:00 2001 From: noway Date: Thu, 17 May 2018 17:40:50 +0300 Subject: [PATCH 13/30] WIP on binary interactions --- Domain.py | 187 +++++++------------------------------- ExternalFields.py | 6 +- ExternalFieldsManager.py | 18 ++++ Particle.py | 23 +++++ ParticleSourcesManager.py | 151 +++++++++++++++++++++--------- SpatialMesh.py | 8 ++ main.py | 4 +- 7 files changed, 200 insertions(+), 197 deletions(-) diff --git a/Domain.py b/Domain.py index d7428bb0..505a2335 100644 --- a/Domain.py +++ b/Domain.py @@ -66,12 +66,9 @@ def init_from_h5(cls, h5file, filename_prefix, filename_suffix): def start_pic_simulation(self): - # fields in domain without any particles self.eval_and_write_fields_without_particles() - # generate particles and write initial step - self.prepare_boris_integration() + self.prepare_recently_generated_particles_for_boris_integration() self.write_step_to_save() - # run simulation self.run_pic() @@ -87,35 +84,22 @@ def run_pic(self): i, i+1, total_time_iterations)) self.advance_one_time_step() self.write_step_to_save() - #self.particle_sources.print_num_of_particles() - def prepare_boris_integration(self): - if self.particle_interaction_model.noninteracting: - self.shift_velocities_half_time_step_back() - elif self.particle_interaction_model.binary: - self.shift_velocities_half_time_step_back() - elif self.particle_interaction_model.pic: + def prepare_recently_generated_particles_for_boris_integration(self): + if self.particle_interaction_model.pic: self.eval_charge_density() self.eval_potential_and_fields() - self.shift_velocities_half_time_step_back() + self.shift_new_particles_velocities_half_time_step_back() def advance_one_time_step(self): - if self.particle_interaction_model.noninteracting: - self.push_particles() - self.apply_domain_constrains() - self.update_time_grid() - elif self.particle_interaction_model.binary: - self.push_particles_binary_interaction() - self.apply_domain_constrains() - self.update_time_grid() - elif self.particle_interaction_model.pic: - self.push_particles() - self.apply_domain_constrains() + self.push_particles() + self.apply_domain_constrains() + if self.particle_interaction_model.pic: self.eval_charge_density() self.eval_potential_and_fields() - self.update_time_grid() + self.update_time_grid() def eval_charge_density(self): @@ -130,11 +114,12 @@ def eval_potential_and_fields(self): def push_particles(self): - self.boris_integration() - - - def push_particles_binary_interaction(self): - self.boris_integration_with_binary_force() + dt = self.time_grid.time_step_size + current_time = self.time_grid.current_time + self.particle_sources.boris_integration( + dt, current_time, + self.spat_mesh, self.external_fields, self.inner_regions, + self.particle_to_mesh_map, self.particle_interaction_model) def apply_domain_constrains(self): @@ -148,34 +133,25 @@ def apply_domain_constrains(self): # Push particles # - def boris_integration(self): - dt = self.time_grid.time_step_size - self.update_momentum(dt) - self.update_position(dt) - - def shift_velocities_half_time_step_back(self): + def shift_new_particles_velocities_half_time_step_back(self): minus_half_dt = -self.time_grid.time_step_size / 2.0 # for src in self.particle_sources.sources: for p in src.particles: if not p.momentum_is_half_time_step_shifted: - total_el_field = Vec3d.zero() - for f in self.external_fields.electric: - el_field = f.field_at_particle_position( - p, self.time_grid.current_time) - total_el_field = total_el_field.add(el_field) + total_el_field = \ + self.external_fields.total_electric_field_at_particle_position( + p, self.time_grid.current_time) pic_el_field = self.particle_to_mesh_map.field_at_particle_position( self.spat_mesh, p) total_el_field = total_el_field.add(pic_el_field) # - total_mgn_field = Vec3d.zero() - for f in self.external_fields.magnetic: - mgn_field = f.field_at_particle_position( - p, self.time_grid.current_time) - total_mgn_field = total_mgn_field.add(mgn_field) + total_mgn_field = \ + self.external_fields.total_magnetic_field_at_particle_position( + p, self.time_grid.current_time) # - if len(self.external_fields.magnetic) == 0: + if not self.external_fields.magnetic: dp = total_el_field.times_scalar(p.charge * minus_half_dt) p.momentum = p.momentum.add(dp) else: @@ -193,97 +169,6 @@ def shift_velocities_half_time_step_back(self): p.momentum_is_half_time_step_shifted = True - def update_momentum(self, dt): - for src in self.particle_sources.sources: - for p in src.particles: - total_el_field = Vec3d.zero() - for f in self.external_fields.electric: - el_field = f.field_at_particle_position( - p, self.time_grid.current_time) - total_el_field = total_el_field.add(el_field) - pic_el_field = self.particle_to_mesh_map.field_at_particle_position( - self.spat_mesh, p) - total_el_field = total_el_field.add(pic_el_field) - # - total_mgn_field = Vec3d.zero() - for f in self.external_fields.magnetic: - mgn_field = f.field_at_particle_position( - p, self.time_grid.current_time) - total_mgn_field = total_mgn_field.add(mgn_field) - # - if len(self.external_fields.magnetic) == 0: - dp = total_el_field.times_scalar(p.charge * dt) - p.momentum = p.momentum.add(dp) - else: - q_quote = dt * p.charge / p.mass / 2.0 - half_el_force = total_el_field.times_scalar(q_quote) - v_current = p.momentum.times_scalar(1.0 / p.mass) - u = v_current.add(half_el_force) - h = total_mgn_field.times_scalar( - q_quote / physical_constants.speed_of_light) - s = h.times_scalar( - 2.0 / (1.0 + h.dot_product(h))) - tmp = u.add(u.cross_product(h)) - u_quote = u.add(tmp.cross_product(s)) - p.momentum = u_quote.add(half_el_force).times_scalar(p.mass) - - - def update_position(self, dt): - self.particle_sources.update_particles_position(dt) - - - def boris_integration_with_binary_force(self): - dt = self.time_grid.time_step_size - self.update_momentum_and_position_with_bin_force(dt) - - - def update_momentum_and_position_with_bin_force(self, dt): - # todo: same code 3 times. get rid of duplication. - for src_idx, src in enumerate(self.particle_sources.sources): - for p_idx, p in enumerat(src.particles): - total_el_field = Vec3d.zero() - for f in self.external_fields.electric: - el_field = f.field_at_particle_position(p, self.time_grid.current_time) - total_el_field = total_el_field.add(el_field) - bin_el_field = self.binary_field_at_particle_position(p, src_idx, p_idx) - total_el_field = total_el_field.add(bin_el_field) - # - total_mgn_field = Vec3d.zero() - for f in self.external_fields.magnetic: - mgn_field = f.field_at_particle_position( - p, self.time_grid.current_time) - total_mgn_field = total_mgn_field.add(mgn_field) - # - if len(self.external_fields.magnetic) == 0: - dp = total_el_field.times_scalar(p.charge * dt) - p.momentum = p.momentum.add(dp) - else: - q_quote = dt * p.charge / p.mass / 2.0 - half_el_force = total_el_field.times_scalar(q_quote) - v_current = p.momentum.times_scalar(1.0 / p.mass) - u = v_current.add(half_el_force) - h = total_mgn_field.times_scalar( - q_quote / physical_constants.speed_of_light) - s = h.times_scalar( - 2.0 / (1.0 + h.dot_product(h))) - tmp = u.add(u.cross_product(h)) - u_quote = u.add(tmp.cross_product(s)) - p.momentum = u_quote.add(half_el_force).times_scalar(p.mass) - - - def binary_field_at_particle_position(self, particle, src_idx, p_idx): - bin_force = Vec3d.zero() - for iter_src_idx, src in enumerate(self.particle_sources.sources): - if iter_src_idx != src_idx: - for p in src.particles: - bin_force.add(p.field_at_point(particle.position)) - else: - tmp_p = src.particles[0] - src.particles[0] = particle - src.particles[p_idx] = tmp_p - for p in src.particles[1:]: - bin_force.add(p.field_at_point(particle.position)) - return bin_force # # Apply domain constrains @@ -315,7 +200,7 @@ def out_of_bound(self, particle): def generate_new_particles(self): self.particle_sources.generate_each_step() - self.shift_velocities_half_time_step_back() + self.shift_new_particles_velocities_half_time_step_back() # @@ -342,13 +227,12 @@ def write(self): self.output_filename_prefix, self.time_grid.current_node, self.output_filename_suffix) h5file = h5py.File(file_name_to_write, mode="w") - # todo: add exception - # if not h5file: - # print("Error: can't open file " + file_name_to_write + \ - # "to save results of initial field calculation!") - # print("Recheck \'output_filename_prefix\' key in config file.") - # print("Make sure the directory you want to save to exists.") - # print("Writing initial fields to file " + file_name_to_write) + if not h5file: + print("Error: can't open file " + file_name_to_write + \ + "to save results of initial field calculation!") + print("Recheck \'output_filename_prefix\' key in config file.") + print("Make sure the directory you want to save to exists.") + print("Writing initial fields to file " + file_name_to_write) print("Writing step {} to file {}".format( self.time_grid.current_node, file_name_to_write)) self.time_grid.write_to_file(h5file) @@ -390,13 +274,12 @@ def eval_and_write_fields_without_particles(self): "fieldsWithoutParticles" + \ self.output_filename_suffix h5file = h5py.File(file_name_to_write, mode="w") - # todo: add exception - # if not h5file: - # print("Error: can't open file " + file_name_to_write + \ - # "to save results of initial field calculation!") - # print("Recheck \'output_filename_prefix\' key in config file.") - # print("Make sure the directory you want to save to exists.") - # print("Writing initial fields to file " + file_name_to_write) + if not h5file: + print("Error: can't open file " + file_name_to_write + \ + "to save results of initial field calculation!") + print("Recheck \'output_filename_prefix\' key in config file.") + print("Make sure the directory you want to save to exists.") + print("Writing initial fields to file " + file_name_to_write) self.spat_mesh.write_to_file(h5file) self.external_fields.write_to_file(h5file) self.inner_regions.write_to_file(h5file) diff --git a/ExternalFields.py b/ExternalFields.py index c2fffbf4..2074c0b1 100644 --- a/ExternalFields.py +++ b/ExternalFields.py @@ -25,7 +25,11 @@ def write_to_file( self, h5_fields_group ): current_field_group = h5_fields_group.create_group( "./" + self.name ) self.write_hdf5_field_parameters( current_field_group ) - + + def field_at_particle_position(self, particle, current_time): + # virtual method + raise NotImplementedError() + # Uniform magnetic diff --git a/ExternalFieldsManager.py b/ExternalFieldsManager.py index 0c242760..cdad19a7 100644 --- a/ExternalFieldsManager.py +++ b/ExternalFieldsManager.py @@ -1,5 +1,6 @@ import sys +from Vec3d import Vec3d from ExternalFields import ExternalField from ExternalFields import ExternalFieldMagneticUniform, ExternalFieldElectricUniform @@ -24,6 +25,7 @@ def init_from_config(cls, conf): ExternalFieldElectricUniform.init_from_config(conf[sec_name], sec_name)) return new_obj + @classmethod def init_from_h5(cls, h5_external_fields_group): new_obj = cls() @@ -48,6 +50,22 @@ def parse_hdf5_external_field(self, current_field_grpid): sys.exit(-1) + def total_electric_field_at_particle_position(self, particle, current_time): + total_el_field = Vec3d.zero() + for f in self.electric: + el_field = f.field_at_particle_position(particle, current_time) + total_el_field = total_el_field.add(el_field) + return total_el_field + + + def total_magnetic_field_at_particle_position(self, particle, current_time): + total_mgn_field = Vec3d.zero() + for f in self.magnetic: + mgn_field = f.field_at_particle_position(particle, current_time) + total_mgn_field = total_mgn_field.add(mgn_field) + return total_mgn_field + + def write_to_file(self, hdf5_file_id): hdf5_groupname = "/External_fields" n_of_electric_fields = len(self.electric) diff --git a/Particle.py b/Particle.py index 08424a13..c039bc06 100644 --- a/Particle.py +++ b/Particle.py @@ -1,4 +1,5 @@ from Vec3d import * +import physical_constants class Particle(): @@ -10,10 +11,12 @@ def __init__(self, id, charge, mass, position, momentum): self.momentum = momentum self.momentum_is_half_time_step_shifted = False + def update_position(self, dt): pos_shift = self.momentum.times_scalar(dt / self.mass) self.position = self.position.add(pos_shift) + def field_at_point(self, point): dist = point.sub(self.position) dist_len = dist.length() @@ -22,6 +25,26 @@ def field_at_point(self, point): dist_len_cube = dist_len ** 3 return dist.times_scalar(self.charge / dist_len_cube) + + def boris_update_momentum(self, dt, total_el_field, total_mgn_field): + q_quote = dt * self.charge / self.mass / 2.0 + half_el_force = total_el_field.times_scalar(q_quote) + v_current = self.momentum.times_scalar(1.0 / self.mass) + u = v_current.add(half_el_force) + h = total_mgn_field.times_scalar( + q_quote / physical_constants.speed_of_light) + s = h.times_scalar( + 2.0 / (1.0 + h.dot_product(h))) + tmp = u.add(u.cross_product(h)) + u_quote = u.add(tmp.cross_product(s)) + self.momentum = u_quote.add(half_el_force).times_scalar(self.mass) + + + def boris_update_momentum_no_mgn(self, dt, total_el_field): + dp = total_el_field.times_scalar(self.charge * dt) + self.momentum = self.momentum.add(dp) + + def print_long(self): print("Particle: ") print("id: {},".format(self.id)) diff --git a/ParticleSourcesManager.py b/ParticleSourcesManager.py index bae97867..078e299f 100644 --- a/ParticleSourcesManager.py +++ b/ParticleSourcesManager.py @@ -1,6 +1,7 @@ import sys import h5py +from Vec3d import * from ParticleSource import * from ParticleSourceBox import * from ParticleSourceCylinder import * @@ -8,79 +9,145 @@ class ParticleSourcesManager: - def __init__( self ): - pass + def __init__(self): + self.sources = None + - @classmethod - def init_from_config( cls, conf ): + def init_from_config(cls, conf): new_obj = cls() new_obj.sources = [] for sec_name in conf.sections(): - if ParticleSourceBox.is_box_source( sec_name ): + if ParticleSourceBox.is_box_source(sec_name): new_obj.sources.append( - ParticleSourceBox.init_from_config( conf, - conf[sec_name], - sec_name ) ) - elif ParticleSourceCylinder.is_cylinder_source( sec_name ): + ParticleSourceBox.init_from_config(conf, + conf[sec_name], + sec_name)) + elif ParticleSourceCylinder.is_cylinder_source(sec_name): new_obj.sources.append( - ParticleSourceCylinder.init_from_config( conf, - conf[sec_name], - sec_name ) ) - elif ParticleSourceTube.is_tube_source( sec_name ): + ParticleSourceCylinder.init_from_config(conf, + conf[sec_name], + sec_name)) + elif ParticleSourceTube.is_tube_source(sec_name): new_obj.sources.append( - ParticleSourceTube.init_from_config( conf, - conf[sec_name], - sec_name ) ) + ParticleSourceTube.init_from_config(conf, + conf[sec_name], + sec_name)) return new_obj - + @classmethod - def init_from_h5( cls, h5_sources_group ): + def init_from_h5(cls, h5_sources_group): new_obj = cls() new_obj.sources = [] for src_group_name in h5_sources_group.keys(): - new_obj.parse_hdf5_particle_source( h5_sources_group[src_group_name] ) + new_obj.parse_hdf5_particle_source(h5_sources_group[src_group_name]) return new_obj - - def parse_hdf5_particle_source( self, this_source_h5_group ): + + def parse_hdf5_particle_source(self, this_source_h5_group): geometry_type = this_source_h5_group.attrs["geometry_type"] if geometry_type == "box": self.sources.append( - ParticleSourceBox.init_from_h5( this_source_h5_group ) ) + ParticleSourceBox.init_from_h5(this_source_h5_group)) elif geometry_type == "cylinder": self.sources.append( - ParticleSourceCylinder.init_from_h5( this_source_h5_group ) ) + ParticleSourceCylinder.init_from_h5(this_source_h5_group)) elif geometry_type == "tube": self.sources.append( - ParticleSourceTube.init_from_h5( this_source_h5_group ) ) + ParticleSourceTube.init_from_h5(this_source_h5_group)) else: - print( "In Particle_source_manager constructor-from-h5: " - "Unknown particle_source type. Aborting" ) - sys.exit( -1 ) - - - def write_to_file( self, h5file ): - h5group = h5file.create_group( "/Particle_sources" ) + print("In Particle_source_manager constructor-from-h5: " + "Unknown particle_source type. Aborting") + sys.exit(-1) + + + def write_to_file(self, h5file): + h5group = h5file.create_group("/Particle_sources") for src in self.sources: - src.write_to_file( h5group ) + src.write_to_file(h5group) - - def generate_each_step( self ): + + def generate_each_step(self): for src in self.sources: src.generate_each_step() - - def print_particles( self ): + + def print_particles(self): for src in self.sources: src.print_particles() - def print_num_of_particles( self ): + + def print_num_of_particles(self): for src in self.sources: - src.print_num_of_particles() - - - def update_particles_position( self, dt ): + src.print_num_of_particles() + + + def update_particles_position(self, dt): for src in self.sources: - src.update_particles_position( dt ) + src.update_particles_position(dt) + + + def boris_integration(self, dt, current_time, + spat_mesh, external_fields, inner_regions, + particle_to_mesh_map, particle_interaction_model): + # todo: too many arguments + for src_idx, src in enumerate(self.sources): + for p_idx, particle in enumerate(src.particles): + total_el_field, total_mgn_field = \ + self.compute_total_fields_at_particle_position( + particle, src_idx, p_idx, + current_time, spat_mesh, external_fields, inner_regions, + particle_to_mesh_map, particle_interaction_model) + if total_mgn_field: + particle.boris_update_momentum(dt, total_el_field, total_mgn_field) + else: + particle.boris_update_momentum_no_mgn(dt, total_el_field) + particle.update_position(dt) + + + def compute_total_fields_at_particle_position( + self, particle, src_idx, p_idx, + current_time, spat_mesh, external_fields, inner_regions, + particle_to_mesh_map, particle_interaction_model): + total_el_field = external_fields.total_electric_field_at_particle_position( + particle, current_time) + if particle_interaction_model.noninteracting: + if inner_regions.regions or not spat_mesh.is_potential_equal_on_boundaries(): + innerreg_el_field = particle_to_mesh_map.field_at_particle_position( + spat_mesh, particle) + total_el_field = total_el_field.add(innerreg_el_field) + elif particle_interaction_model.binary: + bin_el_field = self.binary_field_at_particle_position( + particle, src_idx, p_idx) + total_el_field = total_el_field.add(bin_el_field) + if inner_regions.regions or not spat_mesh.is_potential_equal_on_boundaries(): + innerreg_el_field = particle_to_mesh_map.field_at_particle_position( + spat_mesh, particle) + total_el_field = total_el_field.add(innerreg_el_field) + elif particle_interaction_model.pic: + innerreg_and_pic_el_field = \ + particle_to_mesh_map.field_at_particle_position(spat_mesh, particle) + total_el_field = total_el_field.add(innerreg_and_pic_el_field) + # + total_mgn_field = None + if external_fields.magnetic: + total_mgn_field = external_fields.total_magnetic_field_at_particle_position( + particle, current_time) + # + return (total_el_field, total_mgn_field) + + + def binary_field_at_particle_position(self, particle, src_idx, p_idx): + bin_force = Vec3d.zero() + for iter_src_idx, src in enumerate(self.sources): + if iter_src_idx != src_idx: + for p in src.particles: + bin_force = bin_force.add(p.field_at_point(particle.position)) + else: + tmp_p = src.particles[0] + src.particles[0] = particle + src.particles[p_idx] = tmp_p + for p in src.particles[1:]: + bin_force = bin_force.add(p.field_at_point(particle.position)) + return bin_force diff --git a/SpatialMesh.py b/SpatialMesh.py index ee3c459e..1bf987f3 100644 --- a/SpatialMesh.py +++ b/SpatialMesh.py @@ -160,7 +160,14 @@ def set_boundary_conditions( self, conf ): self.potential[i][j][0] = phi_near self.potential[i][j][nz-1] = phi_far + + def is_potential_equal_on_boundaries( self ): + return \ + ( self.potential[0][2][2] == self.potential[nx-1][2][2] == \ + self.potential[2][0][2] == self.potential[2][ny-1][2] == \ + self.potential[2][2][0] == self.potential[2][2][nz-1] ) + def print( self ): self.print_grid() self.print_ongrid_values() @@ -330,3 +337,4 @@ def global_idx_to_node_ijk( self, global_idx ): j = j_and_k_part // nz k = j_and_k_part % nz return (i, j, k) + diff --git a/main.py b/main.py index d127b22b..43ffd4bc 100644 --- a/main.py +++ b/main.py @@ -41,9 +41,9 @@ def construct_domain(config_or_h5_file): def echo_config(config_or_h5_file, conf): print("Config file is: ", config_or_h5_file) - for s in list(conf.sections()): + for s in conf.sections(): print("[", s, "]") - for k, v in list(conf[s]): + for k, v in conf[s].items(): print("{} = {}".format(k, v)) print() From df2667e33097f47bf9710c9538c4785305fc67e1 Mon Sep 17 00:00:00 2001 From: noway Date: Sat, 19 May 2018 09:50:20 +0300 Subject: [PATCH 14/30] Tests of binary interaction --- Domain.py | 1 + SpatialMesh.py | 3 + .../beam_contour_bin.py | 69 +++++++++++++++++++ .../contour.conf | 3 +- .../contour_bin.conf | 59 ++++++++++++++++ .../estimates.py | 6 +- .../run_example.sh | 3 + .../run_example.sh | 8 +-- .../single_particle_in_magnetic_field.conf | 3 +- main.py | 1 - 10 files changed, 147 insertions(+), 9 deletions(-) create mode 100644 examples/axially_symmetric_beam_contour/beam_contour_bin.py create mode 100644 examples/axially_symmetric_beam_contour/contour_bin.conf diff --git a/Domain.py b/Domain.py index 505a2335..0609cf81 100644 --- a/Domain.py +++ b/Domain.py @@ -129,6 +129,7 @@ def apply_domain_constrains(self): self.apply_domain_boundary_conditions() self.remove_particles_inside_inner_regions() + # # Push particles # diff --git a/SpatialMesh.py b/SpatialMesh.py index 1bf987f3..c7ef6f61 100644 --- a/SpatialMesh.py +++ b/SpatialMesh.py @@ -162,6 +162,9 @@ def set_boundary_conditions( self, conf ): def is_potential_equal_on_boundaries( self ): + nx = self.x_n_nodes + ny = self.y_n_nodes + nz = self.z_n_nodes return \ ( self.potential[0][2][2] == self.potential[nx-1][2][2] == \ self.potential[2][0][2] == self.potential[2][ny-1][2] == \ diff --git a/examples/axially_symmetric_beam_contour/beam_contour_bin.py b/examples/axially_symmetric_beam_contour/beam_contour_bin.py new file mode 100644 index 00000000..7b543fbf --- /dev/null +++ b/examples/axially_symmetric_beam_contour/beam_contour_bin.py @@ -0,0 +1,69 @@ +import h5py +import numpy as np +import matplotlib.pyplot as plt +import scipy.integrate + +filename = 'contour_bin_0000030.h5' +h5file = h5py.File( filename, mode = "r" ) + +def get_source_current( h5file ): + time_step = h5file["/Time_grid"].attrs["time_step_size"] + charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] + particles_per_step = h5file[ + "/Particle_sources/cathode_emitter"].attrs["particles_to_generate_each_step"] + current = particles_per_step * charge / time_step + return current + +def get_source_geometry( h5file ): + axis_start_x = \ + h5file["/Particle_sources/cathode_emitter"].attrs["cylinder_axis_start_x"] + axis_start_z = \ + h5file["/Particle_sources/cathode_emitter"].attrs["cylinder_axis_start_z"] + radius = h5file["/Particle_sources/cathode_emitter"].attrs["cylinder_radius"] + return ( axis_start_x, axis_start_z, radius ) + +def get_source_particle_parameters( h5file ): + mass = h5file["/Particle_sources/cathode_emitter"].attrs["mass"] + charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] + momentum_z = h5file["/Particle_sources/cathode_emitter"].attrs["mean_momentum_z"] + return ( mass, charge, momentum_z ) + +def beam_radius( u, r_0 ): + return r_0 * np.exp( u ** 2 ) + +def beam_z( u, m, v, q, I, r_0 ): + coeff = np.sqrt( m * v**3 / q / I ) * r_0 + subint = lambda t: np.exp( t * t ) + low_lim = 0 + up_lim = u + integral_value = scipy.integrate.quad( subint, low_lim, up_lim )[0] + return coeff * integral_value + +beam_axis_x_pos, emitter_z_pos, r_0 = get_source_geometry( h5file ) +I = get_source_current( h5file ) +m, q, p = get_source_particle_parameters( h5file ) +v = p / m + +u_min = 0; u_max = 2; num_u_points = 100 # for u = 1, r = r(0) * 2.71812 +u = np.linspace( u_min, u_max, num_u_points ) +r_an = [ beam_radius( x, r_0 ) for x in u ] +r_an_upper = r_an + beam_axis_x_pos +r_an_lower = beam_axis_x_pos - r_an +z_an = [ beam_z( x, m = m, v = v, q = q, I = I, r_0 = r_0 ) for x in u ] +z_an = z_an + emitter_z_pos + +r_num = h5file["/Particle_sources/cathode_emitter/position_x"] +z_num = h5file["/Particle_sources/cathode_emitter/position_z"] + +z_volume_size = h5file["/Spatial_mesh"].attrs["z_volume_size"] +x_volume_size = h5file["/Spatial_mesh"].attrs["x_volume_size"] +plt.xlabel( "Z [cm]" ) +plt.ylabel( "X [cm]" ) +plt.ylim( 0, x_volume_size ) +plt.xlim( 0, z_volume_size ) +plt.plot( z_num, r_num, '.', label = "num" ) +plt.plot( z_an, r_an_upper, label = "theory", color = "g" ) +plt.plot( z_an, r_an_lower, color = "g" ) +plt.legend() +plt.savefig( "beam_contour_bin.png" ) +h5file.close() diff --git a/examples/axially_symmetric_beam_contour/contour.conf b/examples/axially_symmetric_beam_contour/contour.conf index dd42e538..dd272d0f 100644 --- a/examples/axially_symmetric_beam_contour/contour.conf +++ b/examples/axially_symmetric_beam_contour/contour.conf @@ -48,8 +48,9 @@ speed_of_light = 3.0e10 [Particle interaction model] -# 'noninteracting' or 'PIC'; without quotes +# 'noninteracting', 'binary' or 'PIC'; without quotes # particle_interaction_model = noninteracting +# particle_interaction_model = binary particle_interaction_model = PIC [Output filename] diff --git a/examples/axially_symmetric_beam_contour/contour_bin.conf b/examples/axially_symmetric_beam_contour/contour_bin.conf new file mode 100644 index 00000000..002037ac --- /dev/null +++ b/examples/axially_symmetric_beam_contour/contour_bin.conf @@ -0,0 +1,59 @@ +# PIC simulation config. +# Do not change section and field names. + +# see estimates.py for parameter values +[Time grid] +total_time = 3.0e-9 +time_save_step = 3.0e-10 +time_step_size = 1.0e-10 + +[Spatial mesh] +grid_x_size = 5.0 +grid_x_step = 0.1 +grid_y_size = 5.0 +grid_y_step = 0.1 +grid_z_size = 10.0 +grid_z_step = 0.1 + +[Particle_source_cylinder.cathode_emitter] +initial_number_of_particles = 100 +particles_to_generate_each_step = 100 +cylinder_axis_start_x = 2.5 +cylinder_axis_start_y = 2.5 +cylinder_axis_start_z = 0.51 +cylinder_axis_end_x = 2.5 +cylinder_axis_end_y = 2.5 +cylinder_axis_end_z = 0.52 +cylinder_radius = 0.5 +mean_momentum_x = 0 +mean_momentum_y = 0 +mean_momentum_z = 1.107e-12 +temperature = 0.0 +charge = -2.998e-4 +mass = 6.121e-22 + +[Boundary conditions] +boundary_phi_left = 0.0 +boundary_phi_right = 0.0 +boundary_phi_bottom = 0.0 +boundary_phi_top = 0.0 +boundary_phi_near = 0.0 +boundary_phi_far = 0.0 + +[External_magnetic_field_uniform.mgn] +magnetic_field_x = 0.0 +magnetic_field_y = 0.0 +magnetic_field_z = 0.0 +speed_of_light = 3.0e10 + + +[Particle interaction model] +# 'noninteracting', 'binary' or 'PIC'; without quotes +# particle_interaction_model = noninteracting +particle_interaction_model = binary +#particle_interaction_model = PIC + +[Output filename] +# No quotes; no spaces till end of line +output_filename_prefix = contour_bin_ +output_filename_suffix = .h5 diff --git a/examples/axially_symmetric_beam_contour/estimates.py b/examples/axially_symmetric_beam_contour/estimates.py index e826c6b2..584d145b 100644 --- a/examples/axially_symmetric_beam_contour/estimates.py +++ b/examples/axially_symmetric_beam_contour/estimates.py @@ -26,7 +26,8 @@ print( "z_e_times_wider = {:.3e} [cm]".format( z_e_times_wider ) ) sim_time = 3.0e-9 -n_of_steps = 100 +#n_of_steps = 100 +n_of_steps = 30 # bin dt = sim_time / n_of_steps print( "simulation_time = {:.3e} [s]".format( sim_time ) ) print( "number_of_time_steps = {:d}".format( n_of_steps ) ) @@ -35,7 +36,8 @@ num_of_real_particles = I * dt / q print( "num_of_real_particles = {:.3e}".format( num_of_real_particles ) ) -num_of_macro_particles = 5000 +#num_of_macro_particles = 5000 +num_of_macro_particles = 100 # bin macro_q = I * dt / num_of_macro_particles macro_m = macro_q / q * m macro_mean_momentum = macro_m * v diff --git a/examples/axially_symmetric_beam_contour/run_example.sh b/examples/axially_symmetric_beam_contour/run_example.sh index 7b254b69..3a4c2427 100644 --- a/examples/axially_symmetric_beam_contour/run_example.sh +++ b/examples/axially_symmetric_beam_contour/run_example.sh @@ -1,2 +1,5 @@ python3 ../../main.py contour.conf python3 beam_contour.py + +#python3 ../../main.py contour_bin.conf +#python3 beam_contour_bin.py diff --git a/examples/single_particle_in_magnetic_field/run_example.sh b/examples/single_particle_in_magnetic_field/run_example.sh index b10fe5be..490b3840 100644 --- a/examples/single_particle_in_magnetic_field/run_example.sh +++ b/examples/single_particle_in_magnetic_field/run_example.sh @@ -1,6 +1,6 @@ -#python3 ../../main.py single_particle_in_magnetic_field.conf -#python3 plot.py +python3 ../../main.py single_particle_in_magnetic_field.conf +python3 plot.py #python3 ../../main.py large_time_step.conf #python3 plot_large_time_step.py -python3 ../../main.py long_simulation_time.conf -python3 plot_long_simulation_time.py +#python3 ../../main.py long_simulation_time.conf +#python3 plot_long_simulation_time.py diff --git a/examples/single_particle_in_magnetic_field/single_particle_in_magnetic_field.conf b/examples/single_particle_in_magnetic_field/single_particle_in_magnetic_field.conf index c92a6628..1a0d746e 100644 --- a/examples/single_particle_in_magnetic_field/single_particle_in_magnetic_field.conf +++ b/examples/single_particle_in_magnetic_field/single_particle_in_magnetic_field.conf @@ -45,8 +45,9 @@ magnetic_field_z = 1000 speed_of_light = 3.0e10 [Particle interaction model] -# 'noninteracting' or 'PIC'; without quotes +# 'noninteracting', 'binary' or 'PIC'; without quotes particle_interaction_model = noninteracting +# particle_interaction_model = binary # particle_interaction_model = PIC [Output filename] diff --git a/main.py b/main.py index 43ffd4bc..73b599d0 100644 --- a/main.py +++ b/main.py @@ -45,7 +45,6 @@ def echo_config(config_or_h5_file, conf): print("[", s, "]") for k, v in conf[s].items(): print("{} = {}".format(k, v)) - print() def extract_filename_prefix_and_suffix_from_h5filename(h5_file): From 1811df900794eb2bda8647db63c74218cc233223 Mon Sep 17 00:00:00 2001 From: noway Date: Sun, 20 May 2018 08:33:41 +0300 Subject: [PATCH 15/30] Test binary interactions with current examples --- .../ribbon_beam_contour/contour_beam_bin.py | 100 ++++++++++++++++++ examples/ribbon_beam_contour/contour_bin.conf | 50 +++++++++ examples/ribbon_beam_contour/estimates_bin.py | 63 +++++++++++ examples/ribbon_beam_contour/run_example.sh | 3 + 4 files changed, 216 insertions(+) create mode 100644 examples/ribbon_beam_contour/contour_beam_bin.py create mode 100644 examples/ribbon_beam_contour/contour_bin.conf create mode 100644 examples/ribbon_beam_contour/estimates_bin.py diff --git a/examples/ribbon_beam_contour/contour_beam_bin.py b/examples/ribbon_beam_contour/contour_beam_bin.py new file mode 100644 index 00000000..154f41e5 --- /dev/null +++ b/examples/ribbon_beam_contour/contour_beam_bin.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Mar 9 15:19:56 2017 +Example for contour of ribbon electron beam +@author: Boytsov +""" + +import numpy as np +import matplotlib.pyplot as plt +import h5py + +SGSE_conv_unit_current_to_A = 3e10 * 0.1; #from current units SGSE to A +SI_conv_cm_to_m = 0.01; +SI_conv_g_to_kg = 0.001 +SI_conv_Fr_to_C = 3.3356409519815207e-10 + +def get_source_current( h5file ): + time_step = h5file["/Time_grid"].attrs["time_step_size"] + charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] + particles_per_step = h5file[ + "/Particle_sources/cathode_emitter"].attrs["particles_to_generate_each_step"] + current = np.abs(particles_per_step * charge / time_step) + return current / SGSE_conv_unit_current_to_A + +def get_source_particle_parameters( h5file ): + mass = h5file["/Particle_sources/cathode_emitter"].attrs["mass"] + charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] + momentum_z = h5file["/Particle_sources/cathode_emitter"].attrs["mean_momentum_z"] + return ( mass * SI_conv_g_to_kg, + charge * SI_conv_Fr_to_C, + momentum_z * SI_conv_g_to_kg * SI_conv_cm_to_m ) + +def get_source_geometry( h5file ): + start_y = h5file["/Particle_sources/cathode_emitter"].attrs["box_y_top"] + end_y = h5file["/Particle_sources/cathode_emitter"].attrs["box_y_bottom"] + start_x = h5file["/Particle_sources/cathode_emitter"].attrs["box_x_left"] + end_x = h5file["/Particle_sources/cathode_emitter"].attrs["box_x_right"] + length_of_cathode = start_y - end_y + half_width_of_cathode = ( end_x - start_x ) / 2 + center_of_beam = ( start_x + end_x ) / 2 + return ( length_of_cathode * SI_conv_cm_to_m, + half_width_of_cathode * SI_conv_cm_to_m, + center_of_beam * SI_conv_cm_to_m ) + +def get_zlim( h5file ): + start_z = h5file["/Particle_sources/cathode_emitter"].attrs["box_z_near"] + end_z = h5file["/Spatial_mesh/"].attrs["z_volume_size"] + return( start_z * SI_conv_cm_to_m, + end_z * SI_conv_cm_to_m) + +def get_voltage( momentum_z, mass, charge ): + energy = (momentum_z * momentum_z) / (2 * mass) + voltage = energy / np.abs(charge) + return voltage + +def get_current_dens(current,length_of_cathode): + current_dens = current / length_of_cathode + return current_dens + +def p_const( linear_current_density , voltage, charge , mass): + eps = 8.85e-12 + p_const = 1 / (4*eps*(np.abs(2*charge/mass))**0.5) * linear_current_density / voltage**1.5 ; + return p_const + + +def contour( z_position , half_width , angle , p_const): + contour = half_width + np.tan(angle) * z_position + p_const / 2 * (z_position * z_position) + return contour + +filename = 'contour_bin_0000030.h5' +h5 = h5py.File( filename, mode="r") + +current = get_source_current( h5 ) +mass, charge, momentum_z = get_source_particle_parameters( h5 ) +length_of_cathode, half_width, center_of_beam = get_source_geometry( h5 ) +start_z, end_z = get_zlim( h5 ) +voltage = get_voltage( momentum_z, mass, charge ) +current_dens = get_current_dens(current,length_of_cathode) + +conv_grad_to_rad = np.pi/180 +angle = 0 * conv_grad_to_rad #angle of beam +steps_z = 100000 +position_z = np.arange(start_z,end_z,(end_z-start_z)/steps_z) # points in z direction, from 0 to 0.01 m with step 0,00001 m + +p_cons = p_const(current_dens,voltage,charge,mass) # constant from equation of motion calculation +contour = contour( position_z , half_width , angle , p_cons) # countour calculation, m + +h5 = h5py.File( filename , mode="r") # read h5 file +plt.figure(figsize=(10,10), dpi = (100)) +plt.xlabel("Z position, [mm]") +plt.ylabel("X position, [mm]") +plt.plot(h5["/Particle_sources/cathode_emitter/position_z"][:]*SI_conv_cm_to_m*1000, + ((h5["/Particle_sources/cathode_emitter/position_x"][:]*SI_conv_cm_to_m - center_of_beam)*1000), + 'o',label="calculated_points") #plot particles + +plt.plot(position_z*1000,contour*1000, color = 'g', lw = 3, label="analytic_curve") # plot countour in cm and move to left z of beam and top x of beam neat cathode +plt.plot(position_z*1000,-1 * contour*1000, color = 'g', lw = 3) +plt.legend(bbox_to_anchor=(0.32, 1), loc=1, borderaxespad=0.) +plt.savefig('countour_beam_bin.png') # save png picture +h5.close() #close h5 file diff --git a/examples/ribbon_beam_contour/contour_bin.conf b/examples/ribbon_beam_contour/contour_bin.conf new file mode 100644 index 00000000..61a2a244 --- /dev/null +++ b/examples/ribbon_beam_contour/contour_bin.conf @@ -0,0 +1,50 @@ +# PIC simulation config. +# Do not change section and field names. + +[Time grid] +total_time = 3.0e-9 +time_save_step = 3.0e-10 +time_step_size = 1.0e-10 + +[Spatial mesh] +grid_x_size = 0.5 +grid_x_step = 0.02 +grid_y_size = 2.0 +grid_y_step = 0.04 +grid_z_size = 8.0 +grid_z_step = 0.2 + +[Particle_source_box.cathode_emitter] +initial_number_of_particles = 100 +particles_to_generate_each_step = 100 +box_x_left = 0.225 +box_x_right = 0.275 +box_y_bottom = 0.6 +box_y_top = 1.4 +box_z_near = 0.11 +box_z_far = 0.12 +mean_momentum_x = 0 +mean_momentum_y = 0 +mean_momentum_z = 1.107e-12 +temperature = 0.0 +charge = -2.998e-04 +mass = 6.125e-22 + +[Boundary conditions] +boundary_phi_left = 0.0 +boundary_phi_right = 0.0 +boundary_phi_bottom = 0.0 +boundary_phi_top = 0.0 +boundary_phi_near = 0.0 +boundary_phi_far = 0.0 + +[Particle interaction model] +# 'noninteracting', 'binary' or 'PIC'; without quotes +# particle_interaction_model = noninteracting +#particle_interaction_model = binary +particle_interaction_model = PIC + +[Output filename] +# No quotes; no spaces till end of line +output_filename_prefix = contour_bin_ +output_filename_suffix = .h5 diff --git a/examples/ribbon_beam_contour/estimates_bin.py b/examples/ribbon_beam_contour/estimates_bin.py new file mode 100644 index 00000000..d50a0d3b --- /dev/null +++ b/examples/ribbon_beam_contour/estimates_bin.py @@ -0,0 +1,63 @@ +from math import * + +# estimates are done in SI units; +# to compose config file, conversion to CGS is provided + +kg_to_g = 1000 +coulomb_to_statcoulomb = 2997924580 +m = 9.8e-31 +q = 1.6e-19 +print( "q = {:.3e} [C] = {:.3e} [statC]".format( q, q * coulomb_to_statcoulomb ) ) +print( "m = {:.3e} [kg] = {:.3e} [g]".format( m, m * kg_to_g ) ) + +m_to_cm = 100 +x_0 = 0.0005 +y_width = 0.008 +print( "beam_width = {:.4f} [m] = {:.3f} [cm]".format( x_0, x_0 * m_to_cm ) ) +print( "y_size = {:.3f} [m] = {:.3f} [cm]".format( y_width, y_width * m_to_cm ) ) + +I = 0.1 +linear_current_density = I / y_width +print( "I = {:.3f} [A]".format( I ) ) +print( "linear_current_density = {:.3f} [A/m]".format( linear_current_density ) ) + +voltage = 1000 +q_electron = 1.60218e-19 +ev_to_joule = 1.60218e-19 +E = voltage * q / q_electron * ev_to_joule +v = sqrt( 2 * E / m ) +print( "U = {:.3f} [V]".format( voltage ) ) +print( "E = {:.3f} [eV]".format( E / ev_to_joule ) ) +print( "v = {:.3e} [m/s]".format( v ) ) + +eps_0 = 8.85e-12 +p = linear_current_density / ( 4 * eps_0 * sqrt( 2 * q / m ) * voltage**1.5 ) +x0_2_times_wider = 2 * x_0 +z_2_times_wider = sqrt( 2 * x_0 / p ) +t_2_times_wider = z_2_times_wider / v +print( "x0_2_times_wider = {:.3f} [m] = {:.3f} [cm]".format( + x0_2_times_wider, x0_2_times_wider * m_to_cm ) ) +print( "t_2_times_wider = {:.3e} [s]".format( t_2_times_wider ) ) +print( "z_2_times_wider = {:.3e} [m] = {:.3e} [cm]".format( + z_2_times_wider, z_2_times_wider * m_to_cm ) ) + +sim_time = 3.0e-9 +n_of_steps = 30 +dt = sim_time / n_of_steps +print( "simulation_time = {:.3e} [s]".format( sim_time ) ) +print( "number_of_time_steps = {:d}".format( n_of_steps ) ) +print( "time_step_size = {:.3e} [s]".format( dt ) ) + +num_of_real_particles = I * dt / q +print( "num_of_real_particles = {:.3e}".format( num_of_real_particles ) ) + +num_of_macro_particles = 100 +macro_q = I * dt / num_of_macro_particles +macro_m = macro_q / q * m +macro_mean_momentum = macro_m * v +print( "num_of_macro_particles = {:d}".format( num_of_macro_particles ) ) +print( "macro_q = {:.3e} [C] = {:.3e} [statC]".format( + macro_q, macro_q * coulomb_to_statcoulomb ) ) +print( "macro_m = {:.3e} [kg] = {:.3e} [g]".format( macro_m, macro_m * kg_to_g ) ) +print( "macro_mean_momentum = {:.3e} [kg * m / s] = {:.3e} [g * cm / s]".format( + macro_mean_momentum, macro_mean_momentum * m_to_cm * kg_to_g ) ) diff --git a/examples/ribbon_beam_contour/run_example.sh b/examples/ribbon_beam_contour/run_example.sh index 030aca52..7f752fd5 100644 --- a/examples/ribbon_beam_contour/run_example.sh +++ b/examples/ribbon_beam_contour/run_example.sh @@ -1,2 +1,5 @@ python3 ../../main.py contour.conf python3 contour_beam.py + +#python3 ../../main.py contour_bin.conf +#python3 contour_beam_bin.py From 1e5514ce546a1d8c6087ae4c844d348514d675c9 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 21 May 2018 16:05:58 +0300 Subject: [PATCH 16/30] expand .gitignore and add requirements.txt --- .gitignore | 4 ++++ requirements.txt | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 3effe65e..0d11c06f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ __pycache__ \#*\# *.h5 *.png +.idea/ +venv/ +.ipynb_checkpoints/ + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..938c0a23 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +h5py +matplotlib +sympy \ No newline at end of file From 8397bb3868fb266ed980b0aa0a3e200bc3755eed Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Mon, 9 Jul 2018 16:49:16 +0300 Subject: [PATCH 17/30] plot_maxwell Plotting of the particles distribution with a 20 time step resolution --- examples/maxwell_check/plot_maxwell.py | 137 +++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 examples/maxwell_check/plot_maxwell.py diff --git a/examples/maxwell_check/plot_maxwell.py b/examples/maxwell_check/plot_maxwell.py new file mode 100644 index 00000000..a1ab804f --- /dev/null +++ b/examples/maxwell_check/plot_maxwell.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Mar 9 15:19:56 2017 +Example for contour of ribbon electron beam +@author: Boytsov +""" + +import numpy as np +import matplotlib.pyplot as plt +import h5py + +SGSE_conv_unit_current_to_A = 3e10 * 0.1; # from current units SGSE to A +SI_conv_cm_to_m = 0.01; +SI_conv_g_to_kg = 0.001 +SI_conv_Fr_to_C = 3.3356409519815207e-10 + + +def get_source_current(h5file): + time_step = h5file["/Time_grid"].attrs["time_step_size"] + charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] + particles_per_step = h5file[ + "/Particle_sources/cathode_emitter"].attrs["particles_to_generate_each_step"] + current = np.abs(particles_per_step * charge / time_step) + return current / SGSE_conv_unit_current_to_A + + +def get_source_particle_parameters(h5file): + mass = h5file["/Particle_sources/cathode_emitter"].attrs["mass"] + charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] + momentum_z = h5file["/Particle_sources/cathode_emitter"].attrs["mean_momentum_z"] + return (mass * SI_conv_g_to_kg, + charge * SI_conv_Fr_to_C, + momentum_z * SI_conv_g_to_kg * SI_conv_cm_to_m) + + +def get_source_geometry(h5file): + start_y = h5file["/Particle_sources/cathode_emitter"].attrs["box_y_top"] + end_y = h5file["/Particle_sources/cathode_emitter"].attrs["box_y_bottom"] + start_x = h5file["/Particle_sources/cathode_emitter"].attrs["box_x_left"] + end_x = h5file["/Particle_sources/cathode_emitter"].attrs["box_x_right"] + length_of_cathode = start_y - end_y + half_width_of_cathode = (end_x - start_x) / 2 + center_of_beam = (start_x + end_x) / 2 + return (length_of_cathode * SI_conv_cm_to_m, + half_width_of_cathode * SI_conv_cm_to_m, + center_of_beam * SI_conv_cm_to_m) + + +def get_zlim(h5file): + start_z = h5file["/Particle_sources/cathode_emitter"].attrs["box_z_near"] + end_z = h5file["/Spatial_mesh/"].attrs["z_volume_size"] + return (start_z * SI_conv_cm_to_m, + end_z * SI_conv_cm_to_m) + + +def get_voltage(momentum_z, mass, charge): + energy = (momentum_z * momentum_z) / (2 * mass) + voltage = energy / np.abs(charge) + return voltage + + +def get_current_dens(current, length_of_cathode): + current_dens = current / length_of_cathode + return current_dens + + +def p_const(linear_current_density, voltage, charge, mass): + eps = 8.85e-12 + p_const = 1 / (4 * eps * (np.abs(2 * charge / mass)) ** 0.5) * linear_current_density / voltage ** 1.5; + return p_const + + +def contour(z_position, half_width, angle, p_const): + contour = half_width + np.tan(angle) * z_position + p_const / 2 * (z_position * z_position) + return contour + + +filename = 'task_maxwell0000000.h5' +h5 = h5py.File(filename, mode="r") + +filename_1 = 'task_maxwell0000020.h5' +h5_1 = h5py.File(filename_1, mode="r") + +filename_2 = 'task_maxwell0000060.h5' +h5_2 = h5py.File(filename_2, mode="r") + +filename_3 = 'task_maxwell0000080.h5' +h5_3 = h5py.File(filename_3, mode="r") + +current = get_source_current(h5) +mass, charge, momentum_z = get_source_particle_parameters(h5) +length_of_cathode, half_width, center_of_beam = get_source_geometry(h5) +start_z, end_z = get_zlim(h5) +voltage = get_voltage(momentum_z, mass, charge) +current_dens = get_current_dens(current, length_of_cathode) + +current = get_source_current(h5_1) +mass, charge, momentum_z = get_source_particle_parameters(h5_1) +length_of_cathode, half_width, center_of_beam = get_source_geometry(h5_1) +start_z, end_z = get_zlim(h5) +voltage = get_voltage(momentum_z, mass, charge) +current_dens = get_current_dens(current, length_of_cathode) + +conv_grad_to_rad = np.pi / 180 +angle = 0 * conv_grad_to_rad # angle of beam +steps_z = 100000 +position_z = np.arange(start_z, end_z, + (end_z - start_z) / steps_z) # points in z direction, from 0 to 0.01 m with step 0,00001 m + +p_cons = p_const(current_dens, voltage, charge, mass) # constant from equation of motion calculation +contour = contour(position_z, half_width, angle, p_cons) # countour calculation, m + +h5 = h5py.File(filename, mode="r") # read h5 file +plt.figure(figsize=(10, 10), dpi=(100)) +plt.xlim(0.000, 0.0008) +plt.ylim(-0.0002, 0.0008) +plt.xlabel("Z position, [mm]") +plt.ylabel("X position, [mm]") +plt.plot(h5["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, + ((h5["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), + 'o', label="calculated_points") # plot particles +plt.plot(h5_1["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, + ((h5_1["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), + 'o', label="calculated_points2") +plt.plot(h5_2["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, + ((h5_2["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), + 'o', label="calculated_points3") +plt.plot(h5_3["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, + ((h5_3["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), + 'o', label="calculated_points4") + +plt.plot(position_z * 1000, contour * 1000, color='g', lw=3, + label="analytic_curve") # plot countour in cm and move to left z of beam and top x of beam neat cathode +plt.plot(position_z * 1000, -1 * contour * 1000, color='g', lw=3) +plt.legend(bbox_to_anchor=(0.32, 1), loc=1, borderaxespad=0.) +plt.savefig('plt_maxwell10.png') # save png picture +h5.close() # close h5 file From c7f81ff253233131c2d675c2e6fd751a8765fd7c Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Mon, 9 Jul 2018 16:50:08 +0300 Subject: [PATCH 18/30] maxwell config --- examples/maxwell_check/task_maxwell.conf | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 examples/maxwell_check/task_maxwell.conf diff --git a/examples/maxwell_check/task_maxwell.conf b/examples/maxwell_check/task_maxwell.conf new file mode 100644 index 00000000..cba39fe6 --- /dev/null +++ b/examples/maxwell_check/task_maxwell.conf @@ -0,0 +1,49 @@ +# see estimates.py for parameter values + +[Time grid] +total_time = 1.168e-13 +time_save_step = 1.168e-15 +time_step_size = 1.168e-15 + +[Spatial mesh] +grid_x_size = 0.001 +grid_x_step = 0.0001 +grid_y_size = 0.001 +grid_y_step = 0.0001 +grid_z_size = 0.001 +grid_z_step = 0.0001 + +[Particle_source_box.cathode_emitter] +initial_number_of_particles = 90 +particles_to_generate_each_step = 0 +box_x_left = 0.0000 +box_x_right = 0.00004 +box_y_bottom = 0.0000 +box_y_top = 0.00004 +box_z_near = 0.00000 +box_z_far = 0.00004 +mean_momentum_x = 3e-16 +mean_momentum_y = 3e-16 +mean_momentum_z = 3e-16 +temperature = 0.0 +charge = 4.8e-10 +mass = 1.67e-24 + +[Boundary conditions] +boundary_phi_left = 0.0 +boundary_phi_right = 0.0 +boundary_phi_bottom = 0.0 +boundary_phi_top = 0.0 +boundary_phi_near = 0.0 +boundary_phi_far = 0.0 + + +[Particle interaction model] +# 'noninteracting' or 'PIC'; without quotes +# particle_interaction_model = noninteracting +particle_interaction_model = binary + +[Output filename] +# No quotes; no spaces till end of line +output_filename_prefix = /home/igor/your-ef_python-dir/venv/task_maxwell +output_filename_suffix = .h5 From 4b15219fc4de8caf713b5bce135d0326a90736d4 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Mon, 9 Jul 2018 16:51:24 +0300 Subject: [PATCH 19/30] box boundary was added --- Domain.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/Domain.py b/Domain.py index 0609cf81..25a502d0 100644 --- a/Domain.py +++ b/Domain.py @@ -1,5 +1,6 @@ import h5py +from ParticleSourceBox import ParticleSourceBox from TimeGrid import TimeGrid from SpatialMesh import SpatialMesh from InnerRegionsManager import InnerRegionsManager @@ -10,7 +11,7 @@ from ParticleSourcesManager import ParticleSourcesManager from Vec3d import Vec3d import physical_constants - +#from Solid_boundary import Solid_boundary class Domain(): @@ -126,9 +127,71 @@ def apply_domain_constrains(self): # First generate then remove. # This allows for overlap of source and inner region. self.generate_new_particles() - self.apply_domain_boundary_conditions() - self.remove_particles_inside_inner_regions() +# self.apply_domain_boundary_conditions() + if self.particle_interaction_model.binary: + self.check_position_of_the_particle_outside() +# self.remove_particles_inside_inner_regions() + + def check_position_of_the_particle_outside(self): + for src in self.particle_sources.sources: + for p in src.particles: + iter = p.id + dt = self.time_grid.time_step_size + pos = p.position # Extraction of each particle Cartesian coordinate vector + mom = p.momentum # Extraction of each particle momentum vector + m = p.mass + pos_est = self.future_step_particle_evolution(dt, pos, mom, m) + pos_new, mom_new = self.gradient_reflection(pos_est, mom) + p.position = pos_new + p.momentum = mom_new + + def future_step_particle_evolution(self, dt, pos, mom, mass): + pos_step_change = mom.times_scalar(dt / mass) # Change of the particle position for a dt time interval + pos_step_evol = pos.add(pos_step_change) # The coordinate vector of the particle for a dt time step + return pos_step_evol + + def gradient_reflection(self, pos_step_evol, mom): +# self.particle_sources.sources[0].() + outside_xleft = pos_step_evol.x <= self.particle_sources.sources[0].xleft + outside_xright = pos_step_evol.x >= self.particle_sources.sources[0].xright + if outside_xleft == 1: + x_grad = self.particle_sources.sources[0].xleft - pos_step_evol.x + mom_ch_x = -1 + elif outside_xright == 1: + x_grad = self.particle_sources.sources[0].xright - pos_step_evol.x + mom_ch_x = -1 + else: + x_grad = 0 + mom_ch_x = 0 + + outside_ybottom = pos_step_evol.y <= self.particle_sources.sources[0].ybottom + outside_ytop = pos_step_evol.y >= self.particle_sources.sources[0].ytop + if outside_ybottom == 1: + y_grad = self.particle_sources.sources[0].ybottom - pos_step_evol.y + mom_ch_y = -1 + elif outside_ytop == 1: + y_grad = self.particle_sources.sources[0].ytop - pos_step_evol.y + mom_ch_y = -1 + else: + y_grad = 0 + mom_ch_y = 0 + + outside_zfar = pos_step_evol.z <= self.particle_sources.sources[0].znear + outside_znear = pos_step_evol.z >= self.particle_sources.sources[0].zfar + if outside_zfar == 1: + z_grad = self.particle_sources.sources[0].zfar - pos_step_evol.z + mom_ch_z = -1 + elif outside_znear == 1: + z_grad = self.particle_sources.sources[0].znear - pos_step_evol.z + mom_ch_z = -1 + else: + z_grad = 0 + mom_ch_z = 0 + + pos_new = Vec3d(pos_step_evol.x + 2*x_grad, pos_step_evol.y + 2*y_grad, pos_step_evol.z + 2*z_grad) + mom_new = Vec3d(mom.x + 2*mom_ch_x*mom.x, mom.y + 2*mom_ch_y*mom.y, mom.z + 2*mom_ch_z) + return pos_new, mom_new # # Push particles @@ -187,6 +250,8 @@ def remove_particles_inside_inner_regions(self): if not self.inner_regions.check_if_particle_inside_and_count_charge(p)] + + def out_of_bound(self, particle): x = particle.position.x y = particle.position.y From a6af66bf563b538837fd3cd1aac000e8e12f7536 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Mon, 9 Jul 2018 16:52:10 +0300 Subject: [PATCH 20/30] Update Domain.py --- Domain.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Domain.py b/Domain.py index 25a502d0..052bc680 100644 --- a/Domain.py +++ b/Domain.py @@ -127,10 +127,9 @@ def apply_domain_constrains(self): # First generate then remove. # This allows for overlap of source and inner region. self.generate_new_particles() -# self.apply_domain_boundary_conditions() + self.apply_domain_boundary_conditions() if self.particle_interaction_model.binary: - self.check_position_of_the_particle_outside() -# self.remove_particles_inside_inner_regions() + self.check_position_of_the_particle_outside() self.remove_particles_inside_inner_regions() def check_position_of_the_particle_outside(self): From fd043b3537b6fa3727d34f2ab8a5328b3a2fd260 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 11 Jul 2018 13:05:49 +0300 Subject: [PATCH 21/30] Estimate script has been added --- .../estimates_maxwell_parameters.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 examples/maxwell_check/estimates_maxwell_parameters.py diff --git a/examples/maxwell_check/estimates_maxwell_parameters.py b/examples/maxwell_check/estimates_maxwell_parameters.py new file mode 100644 index 00000000..cb6a5c3d --- /dev/null +++ b/examples/maxwell_check/estimates_maxwell_parameters.py @@ -0,0 +1,30 @@ +import math + +m = 1.67e-24 # Ion mass +q = 4.8e-10 # Ion charge +kB = 1.38e-16 # The Boltzmann constant +print( "q = {:.3e} [cgs]".format( q ) ) +print( "m = {:.3e} [g]".format( m ) ) +print( "k_B = {:.3e} [erg/K]".format( kB ) ) + +ev_to_erg = 1.60218e-12 # Transfer from eV to erg units +E = 1 * ev_to_erg # The mean energy of ions +v = math.sqrt( 2 * E / m ) # The mean velocity of ions +T = 2/3*(E / kB) # The temperature of the ion gas +print( "E = {:.3e} [eV] = {:.3e} [erg]".format( E / ev_to_erg, E ) ) +print( "v = {:.3e} [cm/s]".format( v ) ) +print( "T = {:.3e} [K]".format( T ) ) + +sim_time = 0.67e-6 +#n_of_steps = 100 +n_of_steps = 1000 # bin +dt = sim_time / n_of_steps +print( "simulation_time = {:.3e} [s]".format( sim_time ) ) +print( "number_of_time_steps = {:d}".format( n_of_steps ) ) +print( "time_step_size = {:.3e} [s]".format( dt ) ) + +#num_of_macro_particles = 5000 +num_of_macro_particles = 1 # bin +macro_mean_momentum = m * v +print( "num_of_macro_particles = {:d}".format( num_of_macro_particles ) ) +print( "macro_mean_momentum = {:.3e} [g * cm / s]".format( macro_mean_momentum ) ) From 814e7237b56596c14f480ce4d52894422b770372 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 11 Jul 2018 13:08:52 +0300 Subject: [PATCH 22/30] Correct parameters has been added The parameters of the box-like boundary has been selected correctly --- examples/maxwell_check/task_maxwell.conf | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/maxwell_check/task_maxwell.conf b/examples/maxwell_check/task_maxwell.conf index cba39fe6..5a3e5d97 100644 --- a/examples/maxwell_check/task_maxwell.conf +++ b/examples/maxwell_check/task_maxwell.conf @@ -1,31 +1,31 @@ # see estimates.py for parameter values [Time grid] -total_time = 1.168e-13 -time_save_step = 1.168e-15 -time_step_size = 1.168e-15 +total_time = 0.67e-5 +time_save_step = 0.67e-9 +time_step_size = 0.67e-9 [Spatial mesh] -grid_x_size = 0.001 +grid_x_size = 0.004 grid_x_step = 0.0001 -grid_y_size = 0.001 +grid_y_size = 0.004 grid_y_step = 0.0001 -grid_z_size = 0.001 +grid_z_size = 0.004 grid_z_step = 0.0001 [Particle_source_box.cathode_emitter] -initial_number_of_particles = 90 +initial_number_of_particles = 100 particles_to_generate_each_step = 0 -box_x_left = 0.0000 -box_x_right = 0.00004 -box_y_bottom = 0.0000 -box_y_top = 0.00004 -box_z_near = 0.00000 -box_z_far = 0.00004 -mean_momentum_x = 3e-16 -mean_momentum_y = 3e-16 -mean_momentum_z = 3e-16 -temperature = 0.0 +box_x_left = 0.000 +box_x_right = 0.004 +box_y_bottom = 0.000 +box_y_top = 0.004 +box_z_near = 0.000 +box_z_far = 0.004 +mean_momentum_x = 0 +mean_momentum_y = 0 +mean_momentum_z = 2.313e-18 +temperature = 0 charge = 4.8e-10 mass = 1.67e-24 @@ -40,10 +40,10 @@ boundary_phi_far = 0.0 [Particle interaction model] # 'noninteracting' or 'PIC'; without quotes -# particle_interaction_model = noninteracting +#particle_interaction_model = noninteracting particle_interaction_model = binary [Output filename] # No quotes; no spaces till end of line -output_filename_prefix = /home/igor/your-ef_python-dir/venv/task_maxwell +output_filename_prefix = /home/igor/your-ef_python-dir/examples/maxwell_check/task_maxwell output_filename_suffix = .h5 From 7cc53d8e8a5e55d608a45e4dc4c9bd2c458c167a Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 11 Jul 2018 13:10:29 +0300 Subject: [PATCH 23/30] The analytical maxwell distribution function has been added --- examples/maxwell_check/plot_maxwell.py | 196 ++++++++++++------------- 1 file changed, 97 insertions(+), 99 deletions(-) diff --git a/examples/maxwell_check/plot_maxwell.py b/examples/maxwell_check/plot_maxwell.py index a1ab804f..c9ecf767 100644 --- a/examples/maxwell_check/plot_maxwell.py +++ b/examples/maxwell_check/plot_maxwell.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- """ -Created on Thu Mar 9 15:19:56 2017 -Example for contour of ribbon electron beam -@author: Boytsov +Created on Wed July 11 13:00:00 2018 +Estimation of establishing Maxwell distribution of the Hydrogen ion gas inside the box +@author: Getmanov """ import numpy as np +import math import matplotlib.pyplot as plt import h5py @@ -15,123 +16,120 @@ SI_conv_Fr_to_C = 3.3356409519815207e-10 -def get_source_current(h5file): - time_step = h5file["/Time_grid"].attrs["time_step_size"] - charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] - particles_per_step = h5file[ - "/Particle_sources/cathode_emitter"].attrs["particles_to_generate_each_step"] - current = np.abs(particles_per_step * charge / time_step) - return current / SGSE_conv_unit_current_to_A - - -def get_source_particle_parameters(h5file): - mass = h5file["/Particle_sources/cathode_emitter"].attrs["mass"] - charge = h5file["/Particle_sources/cathode_emitter"].attrs["charge"] - momentum_z = h5file["/Particle_sources/cathode_emitter"].attrs["mean_momentum_z"] - return (mass * SI_conv_g_to_kg, - charge * SI_conv_Fr_to_C, - momentum_z * SI_conv_g_to_kg * SI_conv_cm_to_m) - - def get_source_geometry(h5file): start_y = h5file["/Particle_sources/cathode_emitter"].attrs["box_y_top"] end_y = h5file["/Particle_sources/cathode_emitter"].attrs["box_y_bottom"] start_x = h5file["/Particle_sources/cathode_emitter"].attrs["box_x_left"] end_x = h5file["/Particle_sources/cathode_emitter"].attrs["box_x_right"] - length_of_cathode = start_y - end_y - half_width_of_cathode = (end_x - start_x) / 2 - center_of_beam = (start_x + end_x) / 2 - return (length_of_cathode * SI_conv_cm_to_m, - half_width_of_cathode * SI_conv_cm_to_m, - center_of_beam * SI_conv_cm_to_m) - - -def get_zlim(h5file): start_z = h5file["/Particle_sources/cathode_emitter"].attrs["box_z_near"] - end_z = h5file["/Spatial_mesh/"].attrs["z_volume_size"] - return (start_z * SI_conv_cm_to_m, - end_z * SI_conv_cm_to_m) - - -def get_voltage(momentum_z, mass, charge): - energy = (momentum_z * momentum_z) / (2 * mass) - voltage = energy / np.abs(charge) - return voltage - - -def get_current_dens(current, length_of_cathode): - current_dens = current / length_of_cathode - return current_dens - + end_z = h5file["/Particle_sources/cathode_emitter"].attrs["box_z_far"] + return start_x, end_x, start_y, end_y, start_z, end_z + +def draw_2dbox_boundary(start_x, end_x, start_z, end_z): + x_box = np.arange(start_x, end_x, (end_x - start_x) / 100) + z_box = start_z*np.ones(x_box.shape) + np.append(x_box, x_box) + np.append(z_box, end_z*np.ones(z_box.shape)) + z1_box = np.arange(start_z, end_z, (end_z - start_z) / 100) + x1_box = start_x*np.ones(z1_box.shape) + np.append(x_box, x1_box) + np.append(z_box, z1_box) + np.append(x_box, end_x*np.ones(z1_box.shape)) + np.append(z_box, z1_box) + return x_box, z_box + +def analyt_maxwell_distrib(num_particles, temperature, h5file): + p_xend = h5file["/Particle_sources/cathode_emitter/momentum_x"][:] + p_yend = h5file["/Particle_sources/cathode_emitter/momentum_y"][:] + p_zend = h5file["/Particle_sources/cathode_emitter/momentum_z"][:] + mass = h5file["/Particle_sources/cathode_emitter"].attrs["mass"] + kB = 1.38e-16 -def p_const(linear_current_density, voltage, charge, mass): - eps = 8.85e-12 - p_const = 1 / (4 * eps * (np.abs(2 * charge / mass)) ** 0.5) * linear_current_density / voltage ** 1.5; - return p_const + p = (p_xend ** 2 + p_yend ** 2 + p_zend ** 2) ** (1/2) + p_grid = np.arange(0.0, p.max(), (p.max() - p.min())/150) + p_grid_1 = p_grid[1:] + dp = p_grid_1 - p_grid[0:len(p_grid)-1] -def contour(z_position, half_width, angle, p_const): - contour = half_width + np.tan(angle) * z_position + p_const / 2 * (z_position * z_position) - return contour + distr = 4*math.pi* (1/(2*math.pi*mass*kB*temperature)) ** (3/2) * (p_grid[1:] ** 2) * np.exp(-1* (p_grid[1:] ** 2) /(2*kB*mass*temperature))*dp + dN = num_particles * distr + return dN, p_grid[1:] -filename = 'task_maxwell0000000.h5' +filename = 'task_maxwell0000001.h5' h5 = h5py.File(filename, mode="r") -filename_1 = 'task_maxwell0000020.h5' +filename_1 = 'task_maxwell0000200.h5' h5_1 = h5py.File(filename_1, mode="r") -filename_2 = 'task_maxwell0000060.h5' +filename_2 = 'task_maxwell0000400.h5' h5_2 = h5py.File(filename_2, mode="r") -filename_3 = 'task_maxwell0000080.h5' +filename_3 = 'task_maxwell0000900.h5' h5_3 = h5py.File(filename_3, mode="r") -current = get_source_current(h5) -mass, charge, momentum_z = get_source_particle_parameters(h5) -length_of_cathode, half_width, center_of_beam = get_source_geometry(h5) -start_z, end_z = get_zlim(h5) -voltage = get_voltage(momentum_z, mass, charge) -current_dens = get_current_dens(current, length_of_cathode) - -current = get_source_current(h5_1) -mass, charge, momentum_z = get_source_particle_parameters(h5_1) -length_of_cathode, half_width, center_of_beam = get_source_geometry(h5_1) -start_z, end_z = get_zlim(h5) -voltage = get_voltage(momentum_z, mass, charge) -current_dens = get_current_dens(current, length_of_cathode) - -conv_grad_to_rad = np.pi / 180 -angle = 0 * conv_grad_to_rad # angle of beam -steps_z = 100000 -position_z = np.arange(start_z, end_z, - (end_z - start_z) / steps_z) # points in z direction, from 0 to 0.01 m with step 0,00001 m - -p_cons = p_const(current_dens, voltage, charge, mass) # constant from equation of motion calculation -contour = contour(position_z, half_width, angle, p_cons) # countour calculation, m +start_x, end_x, start_y, end_y, start_z, end_z = get_source_geometry(h5) +x_box, z_box = draw_2dbox_boundary(start_x, end_x, start_z, end_z) +dN, p_grid = analyt_maxwell_distrib(100, 7.740e+03, h5_3) h5 = h5py.File(filename, mode="r") # read h5 file plt.figure(figsize=(10, 10), dpi=(100)) -plt.xlim(0.000, 0.0008) -plt.ylim(-0.0002, 0.0008) -plt.xlabel("Z position, [mm]") -plt.ylabel("X position, [mm]") -plt.plot(h5["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, - ((h5["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), - 'o', label="calculated_points") # plot particles -plt.plot(h5_1["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, - ((h5_1["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), - 'o', label="calculated_points2") -plt.plot(h5_2["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, - ((h5_2["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), - 'o', label="calculated_points3") -plt.plot(h5_3["/Particle_sources/cathode_emitter/position_z"][:] * SI_conv_cm_to_m * 1000, - ((h5_3["/Particle_sources/cathode_emitter/position_x"][:] * SI_conv_cm_to_m - center_of_beam) * 1000), - 'o', label="calculated_points4") - -plt.plot(position_z * 1000, contour * 1000, color='g', lw=3, - label="analytic_curve") # plot countour in cm and move to left z of beam and top x of beam neat cathode -plt.plot(position_z * 1000, -1 * contour * 1000, color='g', lw=3) +#plt.xlim(2*start_z , 2*end_z) +#plt.ylim(-0.0002, 0.0002) +plt.xlabel("Z position, [cm]") +plt.ylabel("X position, [cm]") +plt.plot(h5["/Particle_sources/cathode_emitter/position_z"][:], + (h5["/Particle_sources/cathode_emitter/position_x"][:]), + 'o', label="elapsed time: 0.67e-9 s") # plot particles +#plt.plot(h5_1["/Particle_sources/cathode_emitter/position_z"][:], +# (h5_1["/Particle_sources/cathode_emitter/position_x"][:]), +# 'o', label="calculated_points2") +#plt.plot(h5_2["/Particle_sources/cathode_emitter/position_z"][:], +# (h5_2["/Particle_sources/cathode_emitter/position_x"][:]), +# 'o', label="calculated_points3") +plt.plot(h5_3["/Particle_sources/cathode_emitter/position_z"][:], + (h5_3["/Particle_sources/cathode_emitter/position_x"][:]), + 'o', label="elapsed time: 0.67e-6 s") +plt.plot(z_box,x_box,'o', label="box") plt.legend(bbox_to_anchor=(0.32, 1), loc=1, borderaxespad=0.) -plt.savefig('plt_maxwell10.png') # save png picture +plt.savefig('plt_maxwell_distr.png') +plt.close() + +p_x0 = h5["/Particle_sources/cathode_emitter/momentum_x"][:] +p_y0 = h5["/Particle_sources/cathode_emitter/momentum_y"][:] +p_z0 = h5["/Particle_sources/cathode_emitter/momentum_z"][:] + +p0 = (p_x0 ** 2 + p_y0 ** 2 + p_z0 ** 2) ** (1/2) + +p_xend = h5_3["/Particle_sources/cathode_emitter/momentum_x"][:] +p_yend = h5_3["/Particle_sources/cathode_emitter/momentum_y"][:] +p_zend = h5_3["/Particle_sources/cathode_emitter/momentum_z"][:] + +p_end = (p_xend ** 2 + p_yend ** 2 + p_zend ** 2) ** (1/2) + +plt.figure() +plt.hist(h5["/Particle_sources/cathode_emitter/momentum_z"][:],15) +plt.hist(h5_1["/Particle_sources/cathode_emitter/momentum_z"][:],15) +plt.hist(h5_2["/Particle_sources/cathode_emitter/momentum_z"][:],15) +plt.hist(h5_3["/Particle_sources/cathode_emitter/momentum_z"][:],15) + +#plt.plot(position_z * 1000, -1 * contour * 1000, color='g', lw=3) +plt.legend(bbox_to_anchor=(0.32, 1), loc=1, borderaxespad=0.) +plt.savefig('plt_maxwell_hist.png') # save png picture + +plt.figure() +plt.hist(p0,20) +plt.plot(p_grid, dN) +plt.xlim(1.5e-18 , 4e-18) +plt.savefig('plt_maxwell_hist_p0.png') + +plt.figure() +plt.hist(p_end,20) +plt.plot(p_grid, dN) +plt.xlim(1.5e-18 , 4e-18) +plt.savefig('plt_maxwell_hist_pend.png') + h5.close() # close h5 file +h5_1.close() +h5_2.close() +h5_3.close() From defad55efd954711a482be60463651b350f4d09e Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 11 Jul 2018 13:24:15 +0300 Subject: [PATCH 24/30] Update ParticleSource.py The momentum distribution has been changed from maxwellian to uniform --- ParticleSource.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/ParticleSource.py b/ParticleSource.py index 862cb2c2..7d95672b 100644 --- a/ParticleSource.py +++ b/ParticleSource.py @@ -2,7 +2,8 @@ import sys import h5py import random -from math import sqrt, copysign +#from math import sqrt, copysign +import math import numpy as np from common import production_assert @@ -148,18 +149,30 @@ def maxwell_momentum_distr( self, mean_momentum, temperature, mass ): maxwell_gauss_std_mean_x = mean_momentum.x maxwell_gauss_std_mean_y = mean_momentum.y maxwell_gauss_std_mean_z = mean_momentum.z - maxwell_gauss_std_dev = sqrt( mass * temperature ) + # Initialization of the Spherical Coordinate parameters + polar_angle_phi = np.random.uniform(0, 2*math.pi) + spheric_angle_theta = np.random.uniform(0, math.pi) + p_abs = mean_momentum.length() + #polat_trig_phi = np.random.uniform(0, 2*math.pi) + # The momentum vector in the Cartesian coordinates + px = p_abs*math.sin(spheric_angle_theta)*math.cos(polar_angle_phi) + py = p_abs*math.sin(spheric_angle_theta)*math.sin(polar_angle_phi) + pz = p_abs*math.cos(spheric_angle_theta) + + mom = Vec3d( px, py, pz ) + mom = mom.times_scalar( 1.0 ) +# maxwell_gauss_std_dev = sqrt( mass * temperature ) # - tmp = random.getstate() - random.setstate( self.rnd_state ) - px = random.gauss( maxwell_gauss_std_mean_x, maxwell_gauss_std_dev ) - py = random.gauss( maxwell_gauss_std_mean_y, maxwell_gauss_std_dev ) - pz = random.gauss( maxwell_gauss_std_mean_z, maxwell_gauss_std_dev ) - self.rnd_state = random.getstate() - random.setstate( tmp ) +# tmp = random.getstate() +# random.setstate( self.rnd_state ) +# px = random.gauss( maxwell_gauss_std_mean_x, maxwell_gauss_std_dev ) +# py = random.gauss( maxwell_gauss_std_mean_y, maxwell_gauss_std_dev ) +# pz = random.gauss( maxwell_gauss_std_mean_z, maxwell_gauss_std_dev ) +# self.rnd_state = random.getstate() +# random.setstate( tmp ) # - mom = Vec3d( px, py, pz ) - mom = mom.times_scalar( 1.0 ) # recheck +# mom = Vec3d( px, py, pz ) +# mom = mom.times_scalar( 1.0 ) # recheck return mom @@ -245,3 +258,4 @@ def mass_gt_zero( self, conf, this_source_config_part ): if this_source_config_part.getfloat("mass") < 0: raise ValueError( "mass < 0" ) + From 7419fc79a39d2a71e1a3ac0d6a7f80585fd387d7 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 11 Jul 2018 13:25:31 +0300 Subject: [PATCH 25/30] Update Domain.py --- Domain.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Domain.py b/Domain.py index 052bc680..4c1bf53b 100644 --- a/Domain.py +++ b/Domain.py @@ -127,9 +127,12 @@ def apply_domain_constrains(self): # First generate then remove. # This allows for overlap of source and inner region. self.generate_new_particles() - self.apply_domain_boundary_conditions() if self.particle_interaction_model.binary: - self.check_position_of_the_particle_outside() self.remove_particles_inside_inner_regions() + self.check_position_of_the_particle_outside() + self.apply_domain_boundary_conditions() +# if self.particle_interaction_model.binary: + +# self.remove_particles_inside_inner_regions() def check_position_of_the_particle_outside(self): @@ -154,6 +157,7 @@ def gradient_reflection(self, pos_step_evol, mom): # self.particle_sources.sources[0].() outside_xleft = pos_step_evol.x <= self.particle_sources.sources[0].xleft outside_xright = pos_step_evol.x >= self.particle_sources.sources[0].xright + if outside_xleft == 1: x_grad = self.particle_sources.sources[0].xleft - pos_step_evol.x mom_ch_x = -1 @@ -176,20 +180,20 @@ def gradient_reflection(self, pos_step_evol, mom): y_grad = 0 mom_ch_y = 0 - outside_zfar = pos_step_evol.z <= self.particle_sources.sources[0].znear - outside_znear = pos_step_evol.z >= self.particle_sources.sources[0].zfar - if outside_zfar == 1: - z_grad = self.particle_sources.sources[0].zfar - pos_step_evol.z - mom_ch_z = -1 - elif outside_znear == 1: + outside_znear = pos_step_evol.z <= self.particle_sources.sources[0].znear + outside_zfar = pos_step_evol.z >= self.particle_sources.sources[0].zfar + if outside_znear == 1: z_grad = self.particle_sources.sources[0].znear - pos_step_evol.z mom_ch_z = -1 + elif outside_zfar == 1: + z_grad = self.particle_sources.sources[0].zfar - pos_step_evol.z + mom_ch_z = -1 else: z_grad = 0 mom_ch_z = 0 pos_new = Vec3d(pos_step_evol.x + 2*x_grad, pos_step_evol.y + 2*y_grad, pos_step_evol.z + 2*z_grad) - mom_new = Vec3d(mom.x + 2*mom_ch_x*mom.x, mom.y + 2*mom_ch_y*mom.y, mom.z + 2*mom_ch_z) + mom_new = Vec3d(mom.x + 2*mom_ch_x*mom.x, mom.y + 2*mom_ch_y*mom.y, mom.z + 2*mom_ch_z*mom.z) return pos_new, mom_new # From a8c7cd12fbaa226c0b0672ba006ec626669796fb Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 18 Jul 2018 13:33:36 +0300 Subject: [PATCH 26/30] ef and jupyter has been added --- jupyter/visualize_examples.ipynb | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 jupyter/visualize_examples.ipynb diff --git a/jupyter/visualize_examples.ipynb b/jupyter/visualize_examples.ipynb new file mode 100644 index 00000000..298d9071 --- /dev/null +++ b/jupyter/visualize_examples.ipynb @@ -0,0 +1,43 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "from glob import glob\n", + "import sys\n", + "sys.path.append('..')\n", + "from ef.config.visualizer import Visualizer3d\n", + "from ef.config.efconf import EfConf\n", + "example_configs = glob('../examples/*/*.conf')\n", + "for fname in example_configs:\n", + " print(fname)\n", + " EfConf.from_fname(fname).visualize_all(Visualizer3d())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 3c1ea0ba254f8850220e180b6a4e0454ce7b43cf Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Wed, 18 Jul 2018 13:39:00 +0300 Subject: [PATCH 27/30] ef and jupyter has been added --- ef/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 103 bytes ef/config/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 110 bytes .../__pycache__/component.cpython-35.pyc | Bin 0 -> 478 bytes .../__pycache__/data_class.cpython-35.pyc | Bin 0 -> 1093 bytes ef/config/__pycache__/efconf.cpython-35.pyc | Bin 0 -> 4849 bytes ef/config/__pycache__/section.cpython-35.pyc | Bin 0 -> 3955 bytes .../__pycache__/visualizer.cpython-35.pyc | Bin 0 -> 8055 bytes ef/config/component.py | 6 + ef/config/components/__init__.py | 14 ++ .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 809 bytes .../boundary_conditions.cpython-35.pyc | Bin 0 -> 2090 bytes .../__pycache__/inner_region.cpython-35.pyc | Bin 0 -> 4680 bytes .../__pycache__/output_file.cpython-35.pyc | Bin 0 -> 1312 bytes .../particle_interaction_model.cpython-35.pyc | Bin 0 -> 1517 bytes .../particle_source.cpython-35.pyc | Bin 0 -> 5525 bytes .../__pycache__/shapes.cpython-35.pyc | Bin 0 -> 2732 bytes .../__pycache__/spatial_mesh.cpython-35.pyc | Bin 0 -> 1817 bytes .../__pycache__/time_grid.cpython-35.pyc | Bin 0 -> 1473 bytes ef/config/components/boundary_conditions.py | 34 +++++ ef/config/components/fields/__init__.py | 3 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 280 bytes .../fields/__pycache__/field.cpython-35.pyc | Bin 0 -> 368 bytes .../components/fields/electric/__init__.py | 2 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 253 bytes .../electric/__pycache__/h5.cpython-35.pyc | Bin 0 -> 799 bytes .../__pycache__/uniform.cpython-35.pyc | Bin 0 -> 1540 bytes ef/config/components/fields/electric/h5.py | 16 ++ .../components/fields/electric/uniform.py | 28 ++++ ef/config/components/fields/field.py | 8 + .../components/fields/magnetic/__init__.py | 1 + .../__pycache__/__init__.cpython-35.pyc | Bin 0 -> 200 bytes .../__pycache__/uniform.cpython-35.pyc | Bin 0 -> 1540 bytes .../components/fields/magnetic/uniform.py | 28 ++++ ef/config/components/inner_region.py | 93 +++++++++++ ef/config/components/output_file.py | 25 +++ .../components/particle_interaction_model.py | 26 ++++ ef/config/components/particle_source.py | 106 +++++++++++++ ef/config/components/shapes.py | 48 ++++++ ef/config/components/spatial_mesh.py | 33 ++++ ef/config/components/time_grid.py | 29 ++++ ef/config/data_class.py | 17 +++ ef/config/efconf.py | 107 +++++++++++++ ef/config/section.py | 77 ++++++++++ ef/config/test_config.py | 49 ++++++ ef/config/visualizer.py | 144 ++++++++++++++++++ ef/util/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 108 bytes ef/util/__pycache__/runner.cpython-35.pyc | Bin 0 -> 1946 bytes ef/util/runner.py | 56 +++++++ 48 files changed, 950 insertions(+) create mode 100644 ef/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/config/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/config/__pycache__/component.cpython-35.pyc create mode 100644 ef/config/__pycache__/data_class.cpython-35.pyc create mode 100644 ef/config/__pycache__/efconf.cpython-35.pyc create mode 100644 ef/config/__pycache__/section.cpython-35.pyc create mode 100644 ef/config/__pycache__/visualizer.cpython-35.pyc create mode 100644 ef/config/component.py create mode 100644 ef/config/components/__init__.py create mode 100644 ef/config/components/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/boundary_conditions.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/inner_region.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/output_file.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/particle_interaction_model.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/particle_source.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/shapes.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/spatial_mesh.cpython-35.pyc create mode 100644 ef/config/components/__pycache__/time_grid.cpython-35.pyc create mode 100644 ef/config/components/boundary_conditions.py create mode 100644 ef/config/components/fields/__init__.py create mode 100644 ef/config/components/fields/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/config/components/fields/__pycache__/field.cpython-35.pyc create mode 100644 ef/config/components/fields/electric/__init__.py create mode 100644 ef/config/components/fields/electric/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/config/components/fields/electric/__pycache__/h5.cpython-35.pyc create mode 100644 ef/config/components/fields/electric/__pycache__/uniform.cpython-35.pyc create mode 100644 ef/config/components/fields/electric/h5.py create mode 100644 ef/config/components/fields/electric/uniform.py create mode 100644 ef/config/components/fields/field.py create mode 100644 ef/config/components/fields/magnetic/__init__.py create mode 100644 ef/config/components/fields/magnetic/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/config/components/fields/magnetic/__pycache__/uniform.cpython-35.pyc create mode 100644 ef/config/components/fields/magnetic/uniform.py create mode 100644 ef/config/components/inner_region.py create mode 100644 ef/config/components/output_file.py create mode 100644 ef/config/components/particle_interaction_model.py create mode 100644 ef/config/components/particle_source.py create mode 100644 ef/config/components/shapes.py create mode 100644 ef/config/components/spatial_mesh.py create mode 100644 ef/config/components/time_grid.py create mode 100644 ef/config/data_class.py create mode 100644 ef/config/efconf.py create mode 100644 ef/config/section.py create mode 100644 ef/config/test_config.py create mode 100644 ef/config/visualizer.py create mode 100644 ef/util/__pycache__/__init__.cpython-35.pyc create mode 100644 ef/util/__pycache__/runner.cpython-35.pyc create mode 100644 ef/util/runner.py diff --git a/ef/__pycache__/__init__.cpython-35.pyc b/ef/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e4e5644b335f6fdc37801b8934c012b20e6cc21 GIT binary patch literal 103 zcmWgR<>k7~;Tg>U1dl-k3@`#24nSPY0whux7=kq!{Z=v*frJsnFA+UGeIQCr(~pnO c%*!l^kJl@xyv1RYo1apelWGT2T@1tw0RD0kNdN!< literal 0 HcmV?d00001 diff --git a/ef/config/__pycache__/__init__.cpython-35.pyc b/ef/config/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc6cf04c899fa8af7affc0b2d7b960fcf2e16c43 GIT binary patch literal 110 zcmWgR<>k7~;Tg>U1dl-k3@`#24nSPY0whux7=kq!{Z=v*frJsnFKInJeIQCr(@)ON jOUq2xkB`sH%PfhH*DI*J#bJ}1pHiBWY6sF(48#loo*5MN literal 0 HcmV?d00001 diff --git a/ef/config/__pycache__/component.cpython-35.pyc b/ef/config/__pycache__/component.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae8858673d44ea1c4841a626a29f43a3974a4f09 GIT binary patch literal 478 zcmZ8d%}T>S5T4DZn1&WTh#>R@*2DG%L<$kaOD?&^!xEB>1oBg}tzd7WFXJn%M^C%ua8!O%~OeF*#-S=3%fxm<2iwM2GCuje$p$DJRxM`1UmF+Om3# zx@O(Ay4~c|Ohz|DLDRW_7jGeNPq6{Vb$h;?d!P` sacEY4Kp;}4NT!fUZWxbrVKD{8c^soq=-DE9McGLoUzkm$P>t0PDiDUSv$7#P8e}o z3awYc$%~8<)1WWmI>n_=P#AzOI2p_tAS`tZCZLloa|#|Z_>4eJh-uj*jcm8j#X=jg z$fWIXp3xng+epbq+5Jgv&Z?%Am0TE^r-6;MEN3>Z&81Y|LACMDU(W`EAx=3PX7yrL zTnuvr>oX0S8=G)0uQ*Q#BMD%K?1-)Uca#R+A!B63LEi|MWAz*rWMw=7fO!b>2)2$A zlELYS#5TGV`qIW`iEwSAYlVPqoAbQL3=)b9BP*RUMGzcrFFLp-H;U%`0q(SWUA8*h z1?bK9AX21*_#PFk=-l-H0`>1bchyDRBQ{vq+ZvnX)~Dr|1Vm#C!FifYR{ixtRC05? zz82+D>P>1RI?WYt=4-mG7$P0DOCt5qcoYEdH!^$-71 zDXLGo7(b+BxM1~ampwHD=YvsMXQI?&RJ?oIR*%rxM&_oG^ooQOAN$Y+2z9}=Y=jb~ zRyLZ~#lnV_Xl!&XlvW2A`Tu(OEBDdoPt$bR!Cy7;E|avS=$Euz&Z|0KmIR>3`PEXC z9*%l~X4NIpC-Ia7b?%O!Vk&C95SJ!nk0n7b?uG7jf-qoy literal 0 HcmV?d00001 diff --git a/ef/config/__pycache__/efconf.cpython-35.pyc b/ef/config/__pycache__/efconf.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..385e2aed748ae1be260f59c5c4a322fe808632c7 GIT binary patch literal 4849 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 literal 0 HcmV?d00001 diff --git a/ef/config/component.py b/ef/config/component.py new file mode 100644 index 00000000..5e87f1cf --- /dev/null +++ b/ef/config/component.py @@ -0,0 +1,6 @@ +from ef.config.data_class import DataClass + + +class ConfigComponent(DataClass): + def visualize(self, visualizer): + pass diff --git a/ef/config/components/__init__.py b/ef/config/components/__init__.py new file mode 100644 index 00000000..8f4be431 --- /dev/null +++ b/ef/config/components/__init__.py @@ -0,0 +1,14 @@ +"""This package contains all supported configuration components + +use "from components import *" before turning a configparser object into components +to make sure all config section names are registered in ef.config.section:ConfigSection""" + +from ef.config.components.fields import * +from ef.config.components.boundary_conditions import * +from ef.config.components.inner_region import * +from ef.config.components.output_file import * +from ef.config.components.particle_interaction_model import * +from ef.config.components.particle_source import * +from ef.config.components.shapes import * +from ef.config.components.spatial_mesh import * +from ef.config.components.time_grid import * diff --git a/ef/config/components/__pycache__/__init__.cpython-35.pyc b/ef/config/components/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e722118331f905cab510eeed9e2684203b0f92e GIT binary patch literal 809 zcmaJ*vNm>BXr1>h&Ox9wn-Bdz<$FHZOzYU_c=n~akFK~Oq{ou*L!*cyq(fES%iYs!JZK_;7`OHtIQ zp`2~qgt`^6(jcf&;N_|bqO}TidHOXW*yJntq{Oh04n7ke9G$h)f;zn@zi-VFu?5k1 zR8P#0skJNfc2*QK`k+@hv{lj*d!SqN)*6NpCkT4c#X$}#3WAnaG9A9Mq#Rpt zFLluN&_B|@v_1CZzmQYESz7WVT5yNUeaw6_JM(dCZLRZf`{!RS8$|!njn>3?554>d zF~%ckYS9n_i^eu3f>L~1B>1~QiA}?XS!+_#q|~9ILureKt$K$;Nt;rahAxTM;}&a# zVo2%I1X`*}8wt2oa}z4iGVRwtK0 zO}@~bEK23LniYv0_?B)fIgQIoD!s5T3Tno0XTUE~3ChPcE0C~Iu$knz>wm)J3-1yx>H(-9k0AV}^ zBuI|~(TDIb zKh;)N=w_r;bf(&{rhCha3Dc-3kY((SnjS{A%eHY@cTVDR7A5gqs$usePiCnMOa8hO z3wOD11KP_51c_&2{&Yn=mRkqDt$NsaEqsUXm+K%5bteqdd^}5d?1kZb;HX#B8Xw}Y zt4A!Zv6vBGj>%pAD)}-!v2J_z2%}}=Anc)+FCp$!35rA31hK|9fP#>C9Fr!lNfS^7 zmrU1`4~W3SCEPO!Gv8cz+`rD-;lst`{xX-}aGmRo!$p=CM{%g)=~1Qc-fT?o{~pF|T=5MYZxi2gDq^3voR zsemd&s`&g}P-R~&BQr6>nYdKV<-@9fVAandP;0+_LBD}3ggIq6_0c<<-ir62BR*~! zx*34tT8JJq2WZ0zzSH95RuS?Qe{y%GGLxr;+U>Imw&&11c<~wWBIuJKjo!&%C&@=q zQtk%Jqw{ZHofb^2*|Z-eN&jX{`&HgQl>O>R_DiH>;5W4$WoPOeSkj%hQ8JUSl*(02 zlwV@@CdkFua7Ba7f6XvCA4xnH-b&V5LLKm9?#AHcN{$#T2XW%Siw`_90t94y|^TP#jIFIl+CZgrQ16-G3+v> TmYmR@)w4WnOZ4n5uP6QovX|zE literal 0 HcmV?d00001 diff --git a/ef/config/components/__pycache__/inner_region.cpython-35.pyc b/ef/config/components/__pycache__/inner_region.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..860580ec3fa15a09cc3d4d0ea271cc635113580d GIT binary patch literal 4680 zcmb_fOLODK5uO3T2PjgaUb~9pid8PVY}#cicI?C{JC3p&+gT@(T`Tzly9&i6K`pf) z!3?0agpw}V&CP!!hn#cBf0-O}+G{SU%E{k1145vdRwY#?i6+oJ(?EBB{lKkuyZO(? zy|4b>5#nE>^r_-}A4l>ZkRg6j3>`77h@mTvtHKATDuy)?NilJR?+D53x`--b;tJmt z6D+EVNlo}QF{ul`ZdWu!)DRO-_@0c(?dX+z z(`l&ghsWc1sy9j(cjB|V@pPnbmw0#2qw#bYDkIn|364%5goe3YVjj*OhRU#?!NsC0 zb#vOEgv0c77KL3`S8xm6nCsLomFSw?)pc}3;h~Z=RC?op+o*qhd>9T=JSvo}S;pOX zGK;6-G#&6J^G^}oeQ-h{d_)%%0fuMcR^vt+?5A+Qn+F?iLQE0mVS_G1WQQKy)Kn5` z{8q`hw^a?SfeXS9{~a86aU`#TTu5;tM1T{=oJf)4!~~Rlz)V1~Jb)iudPP^0hy5A; zMp4{PwKt1X*fj1(T#$s(NUcGu0*Zy&0Qs9xTlo9$8@=8he&J|uVElu>+#ku_n4t~m z#(3JBo$E#rjHlx?2wK>g@cD)G@XfYLhV|x)2DFDC{x@(K+up+o3^>6L7Q(BBo;c#9 zV)!m0TO<-v=83FGV{7d|H%I+JIEbTICEELDtiq9k>7VE(U=@Bd9;OcymQg*rAMnEZK%gy#TU~BQY_PugI(n z)?Tr3z*zI+;h>X@-H*Xdt?7~`BASmHLY)|+C9VIuA}*!4bi}3ggkjQEIB~^oid}J8 z6_+(};Tn7Z5LII0q9%@O;<7GMNE>4M@5MXS%$@!r()#>n4f6!WrN_(L`K_Ab7#CEw zVwC-LQCZ6#tS+2D$jvcFY)oA^EQR|w~`dLZq9O<XABT^M*)Ke@ucv{KOG| z#(BdSw;?X9S)0a;ZzYn%Imq6$xxPE-Pj{zrx~syO3KJyAuE992iSb>NLftLxrsqJ2 z$rS=u*J3q39#4@-fRZAf6@#nY2n7b2m-bbvE8%pgrBW4&n-t6Yida!AB&;afJA&em zm5P;3M`mKzGtg77aDjZFf`-6CNw zsJBR1L`uYD3KM`fn-bBIJJpsuz-95F@Ah#dzXiEgTzzyPR8th%RYn%oG{g@8M^$Kw zt#46AQC-pJTBg7@#ITJJubH6u9aD$$iWNMF&-}8vXctFp9l8lga27=2DAgJ~4~u`x~KiT3PO8qe&GESQFUW!YIU>MK(@p4DMQk@Iv>k@JsmVQbwG{X&YrIO0cN zBT?sBUgUEM{F>^Hd>$d6&E*cWydrjx-C*y44}UN(O+#S0v`9yVK+&uipg-fiyH#CB zSXuvV$QX|j-WnEfU2+LJ^%}_=B(Iaa38E`7{#snMJLx|P--a}={jbWmWLEC)cWlq^ zaUDRVxK_PG@-vcmN!S3?&q1^+j7g6Mi;`?R8WbqncBb4>UYi?Ffqai~)D z0rxCJA=8vLChS%%rzUHX?P}9mz#iuk1iIK+w(O+|7@HDrlJ#-YsGfeAC6BG zi*SOvKsepXT4uW1?{QLewJVU@q7TF)j|uv`2{?o)(4X1{XPcO9aOm?yaOL2cDWC5{ zZ?|TOtokL+)h8s^aBN$)-XaR4hd~k z1cv$;B)jgNn-Hu%|fX%1MFT#Q@*ET9UUV%VAw9VfLpVAb!STN%LCP20DC z{hZt{NS344r6-mlmK$@#eju}QxuE|`X!X=Rs{f3Hd8qzC@<);bkj%Qj?6{m&cH9pP zVHY=MswAA9t{}Vw#LSF+uDlV%c)7hc12hT{dmIR!VjjQ@umTXb&4`tE{CV9?%sZs| zHSVO>>_uRQIn>2#41jNOMW)+1ql_{ZzXg=hXj2WD+24yYyyCEb-&Fma0|O=$KBl%*t#*E_cI0<8mj2lQ literal 0 HcmV?d00001 diff --git a/ef/config/components/__pycache__/output_file.cpython-35.pyc b/ef/config/components/__pycache__/output_file.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..563e967978e08c0fa69d2cc7b746b0898be152f6 GIT binary patch literal 1312 zcmZ`(J&)5s5S_K1*vTa%cP9cRts7rOhft)GIMIY6RHj%(@h14N1ndL(!pP~lS4Hn zc~ices(F)FD{LINiOZ^7S1Yw@=6u1f1#rfA>jXv-rGi0&C9pZ(pn4D9K4mQeCBOz} zP?*M%7C4j!)uxeKKY9Or?qkf2d!&~x@zx`lTS2!(5{_pb8)X`c381&vS}pP$)2=rQ z^s#S(S``Z&;hgTkd?kH??pK^77kJd-Vs0s-d!STb00VL+7f4#N{%j&C^HttR*~P({ zNs{P@?fD^{$!^D-NpRzyfm_h$a2miRy0*}-J-YM28Hjz&)DG}sp*f1h=u8ho6E>A( zoR2W5`2>mWk(|lH@+59CS{74E)0MKUvQ5E$Ps)#*S>aZ69~Wr8>nILqMRPDK1gFYU z?TNEsj={d|MIK}7FOeZEYLcanm?0_|C8^r{@DQcCmpyW_cP#(yz~%B8r<^gSN6DRF z{FW=zjE;n{OdiDkXWohbhZy?`V(xn}zL1l_v!qRk95<-y|ELg@vk&Si&hI36awq}D z!{0{2dr*n`4Er=IA9wVC{UMuE7!%g+d(xas)59?i>Nxs!qb-h4N;mp3pRvjujMa$g zaDM-NKk5v{Z&r62rT_o{ literal 0 HcmV?d00001 diff --git a/ef/config/components/__pycache__/particle_interaction_model.cpython-35.pyc b/ef/config/components/__pycache__/particle_interaction_model.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bbbcc5802dd4f54611850e78badf41878a0427b GIT binary patch literal 1517 zcmaJ>O>fgc5S{gRg43jwMx_ENJzACAD?)&fB2@K2R4Ks094r%WqO|@9YX`NVTxkCc ze`z^#;xBMwW*w)cZLsY1dS~~|%$qmvwpz`f&e6w(4)63IbY-Ul)<8nSr zxTi@+@X5459ND5x)5(x4K-}1_7W#QQ%QK#pBLb)|B&Q-{A153jV3Z7Ik1CY;(J_Mk)_3^-x5h&pg!p85U(-h6y8Qk>#z z$zgF&`wwsU4;7R@JC{y~>(ep}n}}0bH~@3mHe2kiE3IFj7!j{@(VG}mY*JXrAd-a) zB6mZN4LnVdU4`s1u|ac+yBaz;lXjVh6gw>!s!VLrTN}c1^T0DH0n%3(%4SGu%@dlt zVfc9-C6z^NlCOs=hyS`(@*w&PQiRLe=KZ>RiPl@XL%Cu5kovHjuoX?}9&V}b!IoMn z(W#W^C>9pGby{~75j?GJw_EKUwU6ALEyZ-VnD?1J#g#0=H s`sWI21G#l=Ty>MM$xM2$1bt5+(s@y-@D*(0N^FCIi_2LJ#7 literal 0 HcmV?d00001 diff --git a/ef/config/components/__pycache__/particle_source.cpython-35.pyc b/ef/config/components/__pycache__/particle_source.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d7d73aafa5cf0e6087efde392800c9d6eac08cc GIT binary patch literal 5525 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 Pd;6li0K(149q05zY>Z zV@hmfDay|RTQ|cni)Kj}>UJ2;=J9IEwI7DBR?)PO{4P`nUmNn*r~UpZe#zu?Y<9<^ z+&(|eX?xyZTpI07?rwqja?3IYtN~?xkS)vLZ#cq)XKjfQ7A8FK6ykx{oCj@1g~|iD z;R(hz2nU~>L(KdukfjhWrFbjN!V=e(cq>F`SA1)m5A_1-N#H6*ShYynB=3-%kkk$_D|*EQjtk=u zUmiJQylloMRlfvl4=Ad%T_Xwi;(~rGtfiS_<8>_fq-cnlliU{12~p2 z@zet-QwJo>Fzx2cNG-LUWU-bia7;MVdz8OVxu>EyUF`5=<$P)Yw7NPH41z2DdS{Yr)7*vn)|z0jZHc zUoAIpyOCp3KE}+MtOdXhknLeRD0^V6{67)K_|vo!VJF@e!YCb3y024hD$@Q=)l$SyjjgRtMVuK}eTKyb zRwXphnoRzdueVy$Eu@&{!@{$bzlQrExjXqw6nueMcB3PCbx>R;_G5qtHyF5ML+Y5g z=jM7+^u;?@l~Yr-4f zL1T*s5G+b<$^{h|S|oVvP;S$}F*=uWmkN&t9u+^0gEZtIaoK>|{`hZtu<4ZZLvvPu!N6o}VSxn1`oYX@;VbdH4=jhdU07*0uY%Ls4 z+mym0n~k~y1rS_HJ+_(|fNL!yV={o%p%OUYzRkeW&<1ehFZT=d>RUif^h(gJFoH!Z zi*AWxdoypnJ&&sh$BTaCYHKppZlY8&*X}4UlUh4fwvfPDP9qy%nUB-~?ofw-ze#oR z`R_@ucZQ!Foed2$_^g?z&Z>s#xB%6?>0GzsIGbd39EaFhF*zhY6pM#@LFjFk3@uip z@s}MpE`ALG&bEs0Jm_|Q7A@_yuRP<>(xs(GHR6geJ9G=drCY4= zXyrArw0BWbY(wNXmWyAt_)Uv}7B^bVwOG7szh9RzNAU!kDi1*77=MeCeGk@a`jM?# zSg`|4FmXGGJdQZOq&U{?I4;U`ma`nj@f%RsXw)%p`iS8Z2BvNgm{^nj9SFv#P#im< zJ;ZR^*pPGd>LBCfO8~1AYzzmn423jgdM5J^TwV4$sZJ$2-U<9wBHvG|Vl+ zwdapHg$La|9-eb;(4Dc$()cDeFJ(KxgIR0l`x*>(n#H~b18DA<@@-yle(;3Ji*F#{ zX|I#s)I6mApEf;OPobokg2z+v@-njf_Z<6mE*l25kKop*QIRkS@ea${DU#RnF*a>7 zcmH8>nd|X_e&ney_~b7czG8?No-jNGM1lI0<3zV?Zfgc5S{f$9J@)Iw6q8b2@Xiem-G+tQK^TDOE2w(a9Bn0CUUXkuy!kmdTWpT z5&qJ0>YI7xcQa%*#`fNorx_DT74k_5<& zfFR`|a3g3MQqrfhI5hq9G*>4&P0XP6j*HdeC!?ZRm}oW2RZ?B9GZi~#h)}vbQ}Ze< zR;I7jGA%2mdErhSIxg~cu~MsQjs)%1k$uf?6wy=wEVE-wHD9Kf_;kytT>_N=h?y>< zOhW7XlthepOAF4itTVqqYMd9X^Xu*0Yj(xx@N>*h`HaoAplc$@j$<93y4GPF#Hqz; z>>9r+s##{ja(1O8b6Xn^HuiW_*+Ta@kq%)ZHs44Oc=-KtGCAa1Ee_|lLOe8eC=V+x zuv~J5C+jb!FJ-z)D=G1H*<(W@VP!Cxeag(Bd}N;0Mt~?CX5}sgNd%v z@^Y4?8>R2>7IvXs{w%#a3r@?HdLZ`wf%~vBLF%3|S&G~+&}T?D+>Ac@N%fi=9mcKIX_bYFj*4vgHz8O( z$Ei<;Ex~hik%_FpWW{l_4J+@Y*34ONvg8W>qX1OjRvx@M1$=FU>sY2H;M1(Q`~Gv@ zUOxM%o}wJhVRx!S_f8Py9?<)|LLb09W)p`R=cV^x9prCff%_5!;$y{*HSEf literal 0 HcmV?d00001 diff --git a/ef/config/components/boundary_conditions.py b/ef/config/components/boundary_conditions.py new file mode 100644 index 00000000..0f715227 --- /dev/null +++ b/ef/config/components/boundary_conditions.py @@ -0,0 +1,34 @@ +__all__ = ["BoundaryConditions", "BoundaryConditionsConf"] + +from collections import namedtuple + +import numpy as np + +from ef.config.section import register, ConfigSection +from ef.config.component import ConfigComponent + + +class BoundaryConditions(ConfigComponent): + def __init__(self, potential=0): + self.potential = float(potential) + + def to_conf(self): + return BoundaryConditionsConf(*[self.potential] * 6) + + def visualize(self, visualizer, volume_size=(1, 1, 1)): + visualizer.draw_box(np.array(volume_size, np.float), wireframe=True, + colors=visualizer.potential_mapper.to_rgba(self.potential)) + + +@register +class BoundaryConditionsConf(ConfigSection): + section = "Boundary conditions" + ContentTuple = namedtuple("BoundaryConditionsTuple", + ('boundary_phi_right', 'boundary_phi_left', 'boundary_phi_bottom', + 'boundary_phi_top', 'boundary_phi_near', 'boundary_phi_far')) + convert = ContentTuple(*[float] * 6) + + def make(self): + if any(v != self.content[0] for v in self.content): + raise ValueError("Expecting all boundary_phi to be the same.") + return BoundaryConditions(self.content.boundary_phi_right) diff --git a/ef/config/components/fields/__init__.py b/ef/config/components/fields/__init__.py new file mode 100644 index 00000000..33dddc98 --- /dev/null +++ b/ef/config/components/fields/__init__.py @@ -0,0 +1,3 @@ +from ef.config.components.fields.electric import * +from ef.config.components.fields.magnetic import * +from ef.config.components.fields.field import * \ No newline at end of file diff --git a/ef/config/components/fields/__pycache__/__init__.cpython-35.pyc b/ef/config/components/fields/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..859e49d6587b589d67bca9ddd91b1be0e86bcad7 GIT binary patch literal 280 zcmWgR<>k7~;Tb)Zfq~&M5W@izkmUfx#Y#XT1&A0Kau^swpe#l>iwVeLVhGk`ehE~^ zpvicPQOi%0IZ7opO)oh=FD)}2NaYsf=cVSA6zipBrskv+>!s$TCYKatCgWF?o0y)L zS^`w1h|esrkt-RBn1Qx~iC?;Udip?=nx+qN0FZ(?Kp)}&{rLFIyv&mLc)fzkTO2mI V`6;D2sdkJ&6N*`Y1Q#P0BLJ|eO_u-w literal 0 HcmV?d00001 diff --git a/ef/config/components/fields/__pycache__/field.cpython-35.pyc b/ef/config/components/fields/__pycache__/field.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df2ee3faf51b32c63383e993449d02bd36b9338c GIT binary patch literal 368 zcmYLDu};G<5Ix%|O{-Q^LgF9XAsJW^s#FQY(xsa_oTA#PDmigU=)ep=!!MbUiCBp3T=|*}q`VQtHL@w)c0(8*M@`kqh+$C_PUH literal 0 HcmV?d00001 diff --git a/ef/config/components/fields/electric/__init__.py b/ef/config/components/fields/electric/__init__.py new file mode 100644 index 00000000..c0338ed8 --- /dev/null +++ b/ef/config/components/fields/electric/__init__.py @@ -0,0 +1,2 @@ +from ef.config.components.fields.electric.h5 import * +from ef.config.components.fields.electric.uniform import * diff --git a/ef/config/components/fields/electric/__pycache__/__init__.cpython-35.pyc b/ef/config/components/fields/electric/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31c2dc817b2bbe527bda2caf9d7a5c9276663cf1 GIT binary patch literal 253 zcmWgR<>k7~;Tc`Xz`*brh~a<<$Z`PUVhJFT0z`}qISdRTP!=PQ#l#S-$@~&1%b>}4 zi&4u@lPO9)HBB!$KQApa9Z2OCV+0yi4DuZpBLH(uMkN3M literal 0 HcmV?d00001 diff --git a/ef/config/components/fields/electric/__pycache__/h5.cpython-35.pyc b/ef/config/components/fields/electric/__pycache__/h5.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a94433aae803962b2c6a14509a10ed1dc02d12de GIT binary patch literal 799 zcmb7>v2NQi5QdMGWXq`o6dkg66i`T6+^HxE6mgox1)5DA2tlMA0hF9lc99y{nwRM- zF=MB`LZ|*m)q&R#ZRLspBBs3nsR3FgK7}&(}tDg zH;X#@uY|qC`iM20!Q2bFCsGJx2!gQdOsq2L;$P^GF=TyiN~%wZKdo5)~0VeO!%>a9KU zB0K@_vK%?_3Y_?6owTSe5i9TP?9R^4-#6}UZ?}GgZ$Er@iGI>w!vnvES)PN8@d=tb z6oYYS>{2Eu$JQai`vzq$#SJU-DDx=yDfVlbPgy{DNO4Gclj0_c!*Reb;$cwb(;0MI zq(V9zhir!hf>e_QXM*A_HgFU*&7*_kO6g*h9b_u0beg&C^ZN{aG>j zg-9q8rd5n`HLe!(ObxO1>?@IDLanB02@9q>>Dy%`v#_@j3DX#-p*%A#r*F4nqql#g8|6`$D4}lrZ7JE6;t*au?)O&?%85ssw#> z=+vx6VrctVFqC>o@+aTXa-@@|y zWxqebrzV5MrU6SnpB1X8%7Ila2WnM?!J@7}f4(%KlxdMxQu13R=ZQp9oa}Cjw!hX% zI5RwWy$JXYHWgwf`e?&Oz+Xijo~s=QFP)Es-sS_2peDD$nV_0Uj-rE2$r>b1o^SMM zr7(f=U2YxxW`MjVhBB2g`yrTED@V!1Ayw1t$b@>#b` z=Zf(7+R5_T$;p2g4nA94y4IL2oC#2x3b^`5@f!K)lX{3$tSh$>OWd=4*5K0JgO=_w zxetQQ>oU)H(sw~jP}aSz8K-d@y6ne0{xN8-k9F;f(v^O|IyaS)(Zp|+Es(b33m@ulZWZH`-3e!4y5W*X)QtOWj=e literal 0 HcmV?d00001 diff --git a/ef/config/components/fields/electric/h5.py b/ef/config/components/fields/electric/h5.py new file mode 100644 index 00000000..fe88b8db --- /dev/null +++ b/ef/config/components/fields/electric/h5.py @@ -0,0 +1,16 @@ +__all__ = [] + +from ef.config.components.fields.field import Field + + +class ExternalFieldElectricOnRegularGridFromH5File(Field): + + def __init__(self, name="elec_file", filename="field.h5"): + self.name = name + self.filename = filename + + def visualize(self, ax): + pass + +# TODO: Section class and tests + diff --git a/ef/config/components/fields/electric/uniform.py b/ef/config/components/fields/electric/uniform.py new file mode 100644 index 00000000..603ec888 --- /dev/null +++ b/ef/config/components/fields/electric/uniform.py @@ -0,0 +1,28 @@ +__all__ = ["ExternalElectricFieldUniform", "ExternalElectricFieldUniformConf"] + +from collections import namedtuple + +import numpy as np + +from ef.config.components.fields.field import Field +from ef.config.section import register, NamedConfigSection + + +class ExternalElectricFieldUniform(Field): + def __init__(self, name="ExternalElectricFieldUniform1", field=(0, 0, 0)): + self.name = name + self.field = np.array(field, np.float) + + def to_conf(self): + return ExternalElectricFieldUniformConf(self.name, *self.field) + + +@register +class ExternalElectricFieldUniformConf(NamedConfigSection): + section = "External_electric_field_uniform" + ContentTuple = namedtuple("ExternalElectricFieldUniform", + ('electric_field_x', 'electric_field_y', 'electric_field_z')) + convert = ContentTuple(float, float, float) + + def make(self): + return ExternalElectricFieldUniform(self.name, self.content) diff --git a/ef/config/components/fields/field.py b/ef/config/components/fields/field.py new file mode 100644 index 00000000..a9c61be2 --- /dev/null +++ b/ef/config/components/fields/field.py @@ -0,0 +1,8 @@ +__all__ = ["Field"] + +from ef.config.component import ConfigComponent + + +class Field(ConfigComponent): + pass + diff --git a/ef/config/components/fields/magnetic/__init__.py b/ef/config/components/fields/magnetic/__init__.py new file mode 100644 index 00000000..a8f74d99 --- /dev/null +++ b/ef/config/components/fields/magnetic/__init__.py @@ -0,0 +1 @@ +from ef.config.components.fields.magnetic.uniform import * \ No newline at end of file diff --git a/ef/config/components/fields/magnetic/__pycache__/__init__.cpython-35.pyc b/ef/config/components/fields/magnetic/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3d904c3a2616591d8de79868d668e067ee4ca6d GIT binary patch literal 200 zcmWgR<>k7~;Tdhkz`*brh~a<<$Z`PUVm=^|0z`}qISdRTDNGE(n#?bOVhoy$w-~kj zG#R6GQq%O3^YhX&(}7fOL4ICpUP-ZDT4riaO0iyUVtQU`NoKNMX&ryk0@&Ee@O9{FKt1R69nXQN;$P^Ahg4&+j}n8V6SHj%-0!rDPi)mwYy zMR)? zC2jhTW#KWM zzYqyU!nBGcSL13i&(siG&%P2lCe&)0mat&DL%zW}>GV*Is&rP2Sb04fVD>P}K8PZU z1&4xcz;BMhWslF0A21$`eGZA!6L1(>Kq-E>$=hcd<)9H09=!70$1Hb2P6VA0Nuo;7 zM~6-v%OG3eaSlraOJW##roq7)rZBbx>+`FvKWqACYgwH)NDDhw8N1^V-)38hSnw33qFDmhOin&NnOQ?&iH z&WJO^gV*zbZ(&m*W}=TaYy|vO)Zw|>f$+llNa$@o;0S7R6PyXEndB%s*p#e6;`r%C zk5&r9$hDQYAjl{}D$lGPH;mh=vj3s)@1{#XEPfwB2%i*93t z&G|U*JTCVoZ~;)NZZmWT@a2)h=dV6)B(#>#Kx4!Ki6IizvRR5Bts|D}!bw|5xhkJ^ z%XBUYkFK07ubdqJcj4f(#ieVF*}|CsrKy0ce-y8gC!f@Pq+(sUjacHI?Xw1#?hdqc zkI7vSbY7Qv#*@AcVuG^nZOu3}ZRoNe@A$`{xjxplFG^SX9_w6JN=6gERklFdjxT(` zz3W766G)k48IZ9xnq^sSsH`n{#Uh_CbrUM)iJJ5oo9PtGy2JW)ht-Q_K$xA2duy+K joa=xI_e^0kt0w4IKFDSB?N*%5yLV&+{| literal 0 HcmV?d00001 diff --git a/ef/config/components/fields/magnetic/uniform.py b/ef/config/components/fields/magnetic/uniform.py new file mode 100644 index 00000000..bad7fda7 --- /dev/null +++ b/ef/config/components/fields/magnetic/uniform.py @@ -0,0 +1,28 @@ +__all__ = ["ExternalMagneticFieldUniform", "ExternalMagneticFieldUniformConf"] + +from collections import namedtuple + +import numpy as np + +from ef.config.components.fields.field import Field +from ef.config.section import register, NamedConfigSection + + +class ExternalMagneticFieldUniform(Field): + def __init__(self, name="ExternalMagneticFieldUniform1", field=(0, 0, 0)): + self.name = name + self.field = np.array(field, np.float) + + def to_conf(self): + return ExternalMagneticFieldUniformConf(self.name, *self.field) + + +@register +class ExternalMagneticFieldUniformConf(NamedConfigSection): + section = "External_magnetic_field_uniform" + ContentTuple = namedtuple("ExternalMagneticFieldUniform", + ('magnetic_field_x', 'magnetic_field_y', 'magnetic_field_z')) + convert = ContentTuple(float, float, float) + + def make(self): + return ExternalMagneticFieldUniform(self.name, self.content) diff --git a/ef/config/components/inner_region.py b/ef/config/components/inner_region.py new file mode 100644 index 00000000..bda67054 --- /dev/null +++ b/ef/config/components/inner_region.py @@ -0,0 +1,93 @@ +__all__ = ["InnerRegion", "InnerRegionBoxConf", "InnerRegionCylinderConf", + "InnerRegionTubeConf", "InnerRegionSphereConf"] + +from collections import namedtuple + +from ef.config.components.shapes import Box, Cylinder, Tube, Sphere +from ef.config.section import register, NamedConfigSection +from ef.config.component import ConfigComponent + + +class InnerRegion(ConfigComponent): + def __init__(self, name="InnerRegion1", shape=Box(), potential=0): + self.name = name + self.shape = shape + self.potential = float(potential) + + def visualize(self, visualizer): + self.shape.visualize(visualizer, facecolors=visualizer.potential_mapper.to_rgba(self.potential), + wireframe=False, linewidths=0) + + def to_conf(self): + if type(self.shape) is Box: + r, b, n = self.shape.origin + l, t, f = self.shape.origin + self.shape.size + shape_args = [l, r, b, t, n, f] + cls = InnerRegionBoxConf + elif type(self.shape) is Cylinder: + shape_args = list(self.shape.start) + list(self.shape.end) + [self.shape.r] + cls = InnerRegionCylinderConf + elif type(self.shape) is Tube: + shape_args = list(self.shape.start) + list(self.shape.end) + [self.shape.r, self.shape.R] + cls = InnerRegionTubeConf + elif type(self.shape) is Sphere: + shape_args = list(self.shape.origin) + [self.shape.r] + cls = InnerRegionSphereConf + else: + raise TypeError("Config can not represent inner region shape", self.shape) + return cls(self.name, *(shape_args + [self.potential])) + + +@register +class InnerRegionBoxConf(NamedConfigSection): + section = "Inner_region_box" + ContentTuple = namedtuple("InnerRegionBoxTuple", ('box_x_left', 'box_x_right', 'box_y_bottom', + 'box_y_top', 'box_z_near', 'box_z_far', + 'potential')) + convert = ContentTuple(*[float] * 7) + + def make(self): + l, r, b, t, n, f = self.content[:6] + box = Box((r, b, n), (l - r, t - b, f - n)) + return InnerRegion(self.name, box, self.content.potential) + + +@register +class InnerRegionCylinderConf(NamedConfigSection): + section = "Inner_region_cylinder" + ContentTuple = namedtuple("InnerRegionCylinderTuple", ('cylinder_axis_start_x', 'cylinder_axis_start_y', + 'cylinder_axis_start_z', 'cylinder_axis_end_x', + 'cylinder_axis_end_y', 'cylinder_axis_end_z', + 'cylinder_radius', 'potential')) + convert = ContentTuple(*[float] * 8) + + def make(self): + cylinder = Cylinder(self.content[:3], self.content[3:6], self.content.cylinder_radius) + return InnerRegion(self.name, cylinder, self.content.potential) + + +@register +class InnerRegionTubeConf(NamedConfigSection): + section = "Inner_region_tube" + ContentTuple = namedtuple("InnerRegionTubeTuple", ('tube_axis_start_x', 'tube_axis_start_y', + 'tube_axis_start_z', 'tube_axis_end_x', + 'tube_axis_end_y', 'tube_axis_end_z', + 'tube_inner_radius', 'tube_outer_radius', + 'potential')) + convert = ContentTuple(*[float] * 9) + + def make(self): + tube = Tube(self.content[:3], self.content[3:6], self.content.tube_inner_radius, self.content.tube_outer_radius) + return InnerRegion(self.name, tube, self.content.potential) + + +@register +class InnerRegionSphereConf(NamedConfigSection): + section = "Inner_region_sphere" + ContentTuple = namedtuple("InnerRegionCylinderTuple", ('sphere_origin_x', 'sphere_origin_y', + 'sphere_origin_z', 'sphere_radius', 'potential')) + convert = ContentTuple(*[float] * 5) + + def make(self): + sphere = Sphere(self.content[:3], self.content.sphere_radius) + return InnerRegion(self.name, sphere, self.content.potential) diff --git a/ef/config/components/output_file.py b/ef/config/components/output_file.py new file mode 100644 index 00000000..6e257f23 --- /dev/null +++ b/ef/config/components/output_file.py @@ -0,0 +1,25 @@ +__all__ = ["OutputFile", "OutputFilenameConf"] + +from collections import namedtuple + +from ef.config.section import register, ConfigSection +from ef.config.component import ConfigComponent + + +class OutputFile(ConfigComponent): + def __init__(self, prefix="out_", suffix=".h5"): + self.prefix = prefix + self.suffix = suffix + + def to_conf(self): + return OutputFilenameConf(self.prefix, self.suffix) + + +@register +class OutputFilenameConf(ConfigSection): + section = "Output filename" + ContentTuple = namedtuple("OutputFileNameTuple", ('output_filename_prefix', 'output_filename_suffix')) + convert = ContentTuple(str, str) + + def make(self): + return OutputFile(*self.content) diff --git a/ef/config/components/particle_interaction_model.py b/ef/config/components/particle_interaction_model.py new file mode 100644 index 00000000..7c889aaf --- /dev/null +++ b/ef/config/components/particle_interaction_model.py @@ -0,0 +1,26 @@ +__all__ = ["ParticleInteractionModel", "ParticleInteractionModelConf"] + +from collections import namedtuple + +from ef.config.section import register, ConfigSection +from ef.config.component import ConfigComponent + + +class ParticleInteractionModel(ConfigComponent): + def __init__(self, model="PIC"): + if model not in ("PIC", 'noninteracting', 'binary'): + raise ValueError("Unexpected particle interaction model: {}".format(model)) + self.model = model + + def to_conf(self): + return ParticleInteractionModelConf(self.model) + + +@register +class ParticleInteractionModelConf(ConfigSection): + section = "Particle interaction model" + ContentTuple = namedtuple("ParticleInteractionModelTuple", ('particle_interaction_model',)) + convert = ContentTuple(str) + + def make(self): + return ParticleInteractionModel(self.content.particle_interaction_model) diff --git a/ef/config/components/particle_source.py b/ef/config/components/particle_source.py new file mode 100644 index 00000000..f9b5bdb6 --- /dev/null +++ b/ef/config/components/particle_source.py @@ -0,0 +1,106 @@ +__all__ = ["ParticleSource", "ParticleSourceBoxConf", "ParticleSourceCylinderConf", "ParticleSourceTubeConf"] + +from collections import namedtuple + +import numpy as np + +from ef.config.components.shapes import Box, Cylinder, Tube +from ef.config.section import register, NamedConfigSection +from ef.config.component import ConfigComponent + + +class ParticleSource(ConfigComponent): + def __init__(self, name='ParticleSource1', shape=Box(), + initial_particles=500, + particles_to_generate_each_step=500, + momentum=(0, 0, 6.641e-15), + temperature=0.0, + charge=-1.799e-6, + mass=3.672e-24): + self.name = name + self.shape = shape + self.initial_particles = initial_particles + self.particles_to_generate_each_step = particles_to_generate_each_step + self.momentum = np.array(momentum, np.float) + self.temperature = temperature + self.charge = charge + self.mass = mass + + def visualize(self, visualizer): + self.shape.visualize(visualizer, wireframe=True, label=self.name, colors='c', linewidths=1) + + @classmethod + def _from_content(cls, name, shape, c): + return cls(name, shape, c.initial_number_of_particles, c.particles_to_generate_each_step, + (c.mean_momentum_x, c.mean_momentum_y, c.mean_momentum_z), + c.temperature, c.charge, c.mass) + + def to_conf(self): + if type(self.shape) is Box: + r, b, n = self.shape.origin + l, t, f = self.shape.origin + self.shape.size + shape_args = [l, r, b, t, n, f] + cls = ParticleSourceBoxConf + elif type(self.shape) is Cylinder: + shape_args = list(self.shape.start) + list(self.shape.end) + [self.shape.r] + cls = ParticleSourceCylinderConf + elif type(self.shape) is Tube: + shape_args = list(self.shape.start) + list(self.shape.end) + [self.shape.r, self.shape.R] + cls = ParticleSourceTubeConf + else: + raise TypeError("Shape of particle source not supported by config") + return cls(self.name, *(shape_args + [self.initial_particles, self.particles_to_generate_each_step] + + list(self.momentum) + [self.temperature, self.charge, self.mass])) + + +@register +class ParticleSourceBoxConf(NamedConfigSection): + section = "Particle_source_box" + ContentTuple = namedtuple("ParticleSourceBoxTuple", ('box_x_left', 'box_x_right', 'box_y_bottom', + 'box_y_top', 'box_z_near', 'box_z_far', + 'initial_number_of_particles', + 'particles_to_generate_each_step', + 'mean_momentum_x', 'mean_momentum_y', 'mean_momentum_z', + 'temperature', 'charge', 'mass')) + convert = ContentTuple(*([float] * 6 + [int] * 2 + [float] * 6)) + + def make(self): + l, r, b, t, n, f = self.content[:6] + box = Box((r, b, n), (l - r, t - b, f - n)) + return ParticleSource._from_content(self.name, box, self.content) + + +@register +class ParticleSourceCylinderConf(NamedConfigSection): + section = "Particle_source_cylinder" + ContentTuple = namedtuple("ParticleSourceCylinderTuple", ('cylinder_axis_start_x', 'cylinder_axis_start_y', + 'cylinder_axis_start_z', 'cylinder_axis_end_x', + 'cylinder_axis_end_y', 'cylinder_axis_end_z', + 'cylinder_radius', + 'initial_number_of_particles', + 'particles_to_generate_each_step', + 'mean_momentum_x', 'mean_momentum_y', 'mean_momentum_z', + 'temperature', 'charge', 'mass')) + convert = ContentTuple(*([float] * 7 + [int] * 2 + [float] * 6)) + + def make(self): + cylinder = Cylinder(self.content[:3], self.content[3:6], self.content.cylinder_radius) + return ParticleSource(self.name, cylinder, self.content) + + +@register +class ParticleSourceTubeConf(NamedConfigSection): + section = "Particle_source_tube" + ContentTuple = namedtuple("ParticleSourceTubeTuple", ('tube_axis_start_x', 'tube_axis_start_y', + 'tube_axis_start_z', 'tube_axis_end_x', + 'tube_axis_end_y', 'tube_axis_end_z', + 'tube_inner_radius', 'tube_outer_radius', + 'initial_number_of_particles', + 'particles_to_generate_each_step', + 'mean_momentum_x', 'mean_momentum_y', 'mean_momentum_z', + 'temperature', 'charge', 'mass')) + convert = ContentTuple(*([float] * 8 + [int] * 2 + [float] * 6)) + + def make(self): + tube = Tube(self.content[:3], self.content[3:6], self.content.tube_inner_radius, self.content.tube_outer_radius) + return ParticleSource(self.name, tube, self.content) diff --git a/ef/config/components/shapes.py b/ef/config/components/shapes.py new file mode 100644 index 00000000..8f59f731 --- /dev/null +++ b/ef/config/components/shapes.py @@ -0,0 +1,48 @@ +__all__ = ['Shape', 'Box', 'Cylinder', 'Tube', 'Sphere'] + +import numpy as np + +from ef.config.component import ConfigComponent + + +class Shape(ConfigComponent): + pass + + +class Box(Shape): + def __init__(self, origin=(0, 0, 0), size=(1, 1, 1)): + self.origin = np.array(origin, np.float) + self.size = np.array(size, np.float) + + def visualize(self, visualizer, **kwargs): + visualizer.draw_box(self.size, self.origin, **kwargs) + + +class Cylinder(Shape): + def __init__(self, start=(0, 0, 0), end=(1, 0, 0), radius=1): + self.start = np.array(start, np.float) + self.end = np.array(end, np.float) + self.r = float(radius) + + def visualize(self, visualizer, **kwargs): + visualizer.draw_cylinder(self.start, self.end, self.r, **kwargs) + + +class Tube(Shape): + def __init__(self, start=(0, 0, 0), end=(1, 0, 0), inner_radius=1, outer_radius=2): + self.start = np.array(start, np.float) + self.end = np.array(end, np.float) + self.r = float(inner_radius) + self.R = float(outer_radius) + + def visualize(self, visualizer, **kwargs): + visualizer.draw_tube(self.start, self.end, self.r, self.R, **kwargs) + + +class Sphere(Shape): + def __init__(self, origin=(0,0,0), radius=1): + self.origin = np.array(origin) + self.r = float(radius) + + def visualize(self, visualizer, **kwargs): + visualizer.draw_sphere(self.origin, self.r, **kwargs) \ No newline at end of file diff --git a/ef/config/components/spatial_mesh.py b/ef/config/components/spatial_mesh.py new file mode 100644 index 00000000..5896772c --- /dev/null +++ b/ef/config/components/spatial_mesh.py @@ -0,0 +1,33 @@ +__all__ = ['SpatialMesh', 'SpatialMeshConf'] + +from collections import namedtuple + +import numpy as np + +from ef.config.section import register, ConfigSection +from ef.config.component import ConfigComponent + + +class SpatialMesh(ConfigComponent): + def __init__(self, size=(10, 10, 10), step=(1, 1, 1)): + self.size = np.array(size, np.float) + self.step = np.array(step, np.float) + + def visualize(self, visualizer): + visualizer.draw_box(self.size, wireframe=True, label='volume', colors='k', linewidths=1) + + def to_conf(self): + X, Y, Z = self.size + x, y, z = self.step + return SpatialMeshConf(X, x, Y, y, Z, z) + + +@register +class SpatialMeshConf(ConfigSection): + section = "Spatial mesh" + ContentTuple = namedtuple("SpatialMeshTuple", ('grid_x_size', 'grid_x_step', 'grid_y_size', + 'grid_y_step', 'grid_z_size', 'grid_z_step')) + convert = ContentTuple(*[float] * 6) + + def make(self): + return SpatialMesh(self.content[::2], self.content[1::2]) diff --git a/ef/config/components/time_grid.py b/ef/config/components/time_grid.py new file mode 100644 index 00000000..3768dff1 --- /dev/null +++ b/ef/config/components/time_grid.py @@ -0,0 +1,29 @@ +__all__ = ['TimeGrid', 'TimeGridConf'] + +from collections import namedtuple + +from ef.config.section import ConfigSection, register +from ef.config.component import ConfigComponent + + +class TimeGrid(ConfigComponent): + def __init__(self, total=100.0, save_step=10.0, step=1.0): + self.total = total + self.save_step = save_step + self.step = step + + def to_conf(self): + return TimeGridConf(self.total, self.save_step, self.step) + + def visualize(self, visualizer): + pass + + +@register +class TimeGridConf(ConfigSection): + section = "Time grid" + ContentTuple = namedtuple("TimeGridTuple", ('total_time', 'time_save_step', 'time_step_size')) + convert = ContentTuple(float, float, float) + + def make(self): + return TimeGrid(*self.content) diff --git a/ef/config/data_class.py b/ef/config/data_class.py new file mode 100644 index 00000000..3e1b4af1 --- /dev/null +++ b/ef/config/data_class.py @@ -0,0 +1,17 @@ +# https://codereview.stackexchange.com/questions/131761/lombokython-automatic-eq-hash-repr +# https://github.com/alexprengere/reprmixin +class DataClass: + repr_arg_separator = ', ' + + def __eq__(self, other): + if isinstance(self, other.__class__): + return repr(self) == repr(other) + return NotImplemented + + def __hash__(self): + return hash(tuple(sorted(self.__dict__.items()))) + + def __repr__(self): + return '{name}({values})'.format( + name=type(self).__name__, + values=self.repr_arg_separator.join(map(lambda pair: "{}={!r}".format(*pair), vars(self).items()))) diff --git a/ef/config/efconf.py b/ef/config/efconf.py new file mode 100644 index 00000000..6bdf8aad --- /dev/null +++ b/ef/config/efconf.py @@ -0,0 +1,107 @@ +import io +from configparser import ConfigParser + +from ef.config.components import * +from ef.config.data_class import DataClass +from ef.config.section import ConfigSection + + +class EfConf(DataClass): + # repr_arg_separator = ',\n' + + def __init__(self, time_grid=TimeGrid(), spatial_mesh=SpatialMesh(), sources=(), inner_regions=(), + output_file=OutputFile(), boundary_conditions=BoundaryConditions(), + particle_interaction_model=ParticleInteractionModel(), external_fields=()): + self.time_grid = time_grid + self.spatial_mesh = spatial_mesh + self.sources = list(sources) + self.inner_regions = list(inner_regions) + self.output_file = output_file + self.boundary_conditions = boundary_conditions + self.particle_interaction_model = particle_interaction_model + self.external_fields = list(external_fields) + + @classmethod + def from_components(cls, components): + parents = {'time_grid': TimeGrid, 'spatial_mesh': SpatialMesh, 'sources': ParticleSource, + 'inner_regions': InnerRegion, 'output_file': OutputFile, 'boundary_conditions': BoundaryConditions, + 'particle_interaction_model': ParticleInteractionModel, 'external_fields': Field} + singletons = TimeGrid, SpatialMesh, OutputFile, BoundaryConditions, ParticleInteractionModel + kwargs = {} + for arg, parent in parents.items(): + children = [c for c in components if isinstance(c, parent)] + if parent in singletons: + if len(children) > 1: + raise Exception("Several {} configured, cannot init EfConf".format(parent)) + if len(children) < 1: + raise Exception("No {} configuration found, cannot init EfConf".format(parent)) + kwargs[arg] = children[0] + else: + kwargs[arg] = children + return cls(**kwargs) + + @classmethod + def from_configparser(cls, parser): + return cls.from_components([section.make() for section in ConfigSection.parser_to_confs(parser)]) + + @classmethod + def from_fname(cls, fname): + parser = ConfigParser() + parser.read(fname) + return cls.from_configparser(parser) + + @classmethod + def from_file(cls, file): + parser = ConfigParser() + parser.read_file(file) + return cls.from_configparser(parser) + + @classmethod + def from_string(cls, s): + parser = ConfigParser() + parser.read_string(s) + return cls.from_configparser(parser) + + @property + def components(self): + return [self.time_grid, self.spatial_mesh] + self.sources + self.inner_regions + \ + [self.output_file, self.boundary_conditions, self.particle_interaction_model] + self.external_fields + + def get_potentials(self): + return [self.boundary_conditions.potential] + [region.potential for region in self.inner_regions] + + def to_sections(self): + return [c.to_conf() for c in self.components] + + def visualize_all(self, visualizer): + p = self.get_potentials() + visualizer.set_potential_lim(min(p), max(p)) + self.boundary_conditions.visualize(visualizer, self.spatial_mesh.size) + visualizer.visualize(self.sources) + visualizer.visualize(self.inner_regions) + visualizer.visualize(self.external_fields) + visualizer.show() + + def export_to_fname(self, fname): + with open(fname, 'w') as f: + self.export_to_file(f) + + def export_to_file(self, file): + parser = ConfigParser() + for section in self.to_sections(): + section.add_section_to_parser(parser) + parser.write(file) + + def export_to_string(self): + iostr = io.StringIO() + self.export_to_file(iostr) + return iostr.getvalue() + + +def main(): + ef = EfConf() + print(ef) + + +if __name__ == "__main__": + main() diff --git a/ef/config/section.py b/ef/config/section.py new file mode 100644 index 00000000..7bc7b17d --- /dev/null +++ b/ef/config/section.py @@ -0,0 +1,77 @@ +from collections import namedtuple + +from ef.config.data_class import DataClass + + +class ConfigSection(DataClass): + section_map = {} + section = "Section header string goes here" + ContentTuple = namedtuple("ConfigSectionTuple", ()) # expected content of the config section as a namedtuple + convert = ContentTuple() # tuple of types to convert config strings into + + @staticmethod + def parser_to_confs(conf): + return [ConfigSection.section_map[section.split('.')[0]].from_section(conf[section]) for section in + conf.sections()] + + @classmethod + def register(cls): + cls.section_map[cls.section] = cls + + def __init__(self, *args, **kwargs): + self.content = self.ContentTuple(*args, **kwargs) + + @classmethod + def from_section(cls, section): + if section.name != cls.section: + raise ValueError("Unexpected config section name: {}".format(section.name)) + if set(section.keys()) != set(cls.ContentTuple._fields): + unexpected = set(section.keys()) - set(cls.ContentTuple._fields) + if unexpected: + raise ValueError("Unexpected config variables {} in section {}". + format(tuple(unexpected), section.name)) + missing = set(cls.ContentTuple._fields) - set(section.keys()) + if missing: + raise ValueError("Missing config variables {} in section {}". + format(tuple(missing), section.name)) + + data = {arg: cls.convert._asdict()[arg](section[arg]) for arg in cls.convert._fields} + return cls(**data) + + def add_section_to_parser(self, conf): + conf.add_section(self.section) + for k, v in self.content._asdict().items(): + conf.set(self.section, k, str(v)) + + def make(self): + raise NotImplementedError() + + +def register(cls): + ConfigSection.section_map[cls.section] = cls + return cls + + +class NamedConfigSection(ConfigSection): + def __init__(self, name, *args, **kwargs): + self.name = name + self.section = self.section + '.' + name + super().__init__(*args, **kwargs) + + @classmethod + def from_section(cls, section): + category, name = section.name.split('.', 1) + if category != cls.section: + raise ValueError("Unexpected config section name: {}".format(section.name)) + if set(section.keys()) != set(cls.ContentTuple._fields): + unexpected = set(section.keys()) - set(cls.ContentTuple._fields) + if unexpected: + raise ValueError("Unexpected config variables {} in section {}". + format(tuple(unexpected), section.name)) + missing = set(cls.ContentTuple._fields) - set(section.keys()) + if missing: + raise ValueError("Missing config variables {} in section {}". + format(tuple(missing), section.name)) + + data = {arg: cls.convert._asdict()[arg](section[arg]) for arg in cls.convert._fields} + return cls(name, **data) diff --git a/ef/config/test_config.py b/ef/config/test_config.py new file mode 100644 index 00000000..16190da0 --- /dev/null +++ b/ef/config/test_config.py @@ -0,0 +1,49 @@ +from configparser import ConfigParser + +from ef.config.components import * +from ef.config.efconf import EfConf +from ef.config.section import ConfigSection + +comp_list = [BoundaryConditions, InnerRegion, OutputFile, ParticleInteractionModel, + ParticleSource, SpatialMesh, TimeGrid, ExternalMagneticFieldUniform, ExternalElectricFieldUniform] + + +def test_components_to_conf_and_back(): + for Component in comp_list: + x = Component() + y = x.to_conf().make() + assert x == y + + +def test_conf_to_configparser_and_back(): + confs = [C().to_conf() for C in comp_list] + parser = ConfigParser() + for c in confs: + c.add_section_to_parser(parser) + conf2 = ConfigSection.parser_to_confs(parser) + assert conf2 == confs + + +def test_minimal_example(): + parser = ConfigParser() + parser.read("examples/minimal_working_example/minimal_conf.conf") + components = [conf.make() for conf in ConfigSection.parser_to_confs(parser)] + assert components == [TimeGrid(1e-7, 1e-9, 1e-9), SpatialMesh((5, 5, 15), (0.5, 0.5, 1.5)), + ParticleInteractionModel('noninteracting'), BoundaryConditions(0), + ExternalMagneticFieldUniform('mgn_uni'), ExternalElectricFieldUniform('el_uni'), + OutputFile('example_', '.h5')] + + +class TestEfConf: + def test_conf_export(self): + conf = EfConf(sources=[ParticleSource()], inner_regions=(InnerRegion(),)) + s = conf.export_to_string() + c1 = EfConf.from_string(s) + assert c1 == conf + + def test_conf_repr(self): + from numpy import array # for use in eval + conf = EfConf(sources=[ParticleSource()], inner_regions=(InnerRegion(),)) + s = repr(conf) + c1 = eval(s) + assert c1 == conf \ No newline at end of file diff --git a/ef/config/visualizer.py b/ef/config/visualizer.py new file mode 100644 index 00000000..398721cb --- /dev/null +++ b/ef/config/visualizer.py @@ -0,0 +1,144 @@ +import matplotlib.cm +import matplotlib.pyplot as plt +import numpy as np +from mpl_toolkits.mplot3d.art3d import Line3DCollection, Poly3DCollection + + +class Visualizer3d: + def __init__(self, equal_aspect=True, potential_colormap='seismic'): + fig = plt.figure() + self.ax = fig.add_subplot(111, projection='3d') + self.ax.set_xlabel('X') + self.ax.set_ylabel('Y') + self.ax.set_zlabel('Z') + self.equal_aspect = equal_aspect + plt.rcParams["figure.figsize"] = [9, 8] + self.potential_mapper = matplotlib.cm.ScalarMappable(cmap=potential_colormap) + + def set_potential_lim(self, p_min, p_max): + self.potential_mapper.set_clim(p_min, p_max) + + def visualize(self, config_objects): + for conf in config_objects: + conf.visualize(self) + + def show(self): + if self.equal_aspect: + self.axis_equal_3d() + self.ax.legend() + plt.show() + + def axis_equal_3d(self): + # https://stackoverflow.com/questions/8130823/set-matplotlib-3d-plot-aspect-ratio + extents = np.array([getattr(self.ax, 'get_{}lim'.format(dim))() for dim in 'xyz']) + sz = extents[:, 1] - extents[:, 0] + centers = np.mean(extents, axis=1) + maxsize = max(abs(sz)) + r = maxsize / 2 + for ctr, dim in zip(centers, 'xyz'): + getattr(self.ax, 'set_{}lim'.format(dim))(ctr - r, ctr + r) + + def draw_box(self, size, position=np.zeros(3), wireframe=False, **kwargs): + cube = np.mgrid[0:2, 0:2, 0:2].reshape(3, 8).T + vertices = size * cube + position + # tell ax our extents, so that xyz limits are set correctly + self.ax.scatter(*[vertices[:, i] for i in (0, 1, 2)], alpha=0.0) + if wireframe: + edge_masks = [np.logical_and(cube[:, i] == v, cube[:, j] == w) + for w in (0, 1) for v in (0, 1) for i in (0, 1) for j in range(i + 1, 3)] + edges = [vertices[edge, :] for edge in edge_masks] + self.ax.add_collection(Line3DCollection(edges, **kwargs)) + else: + face_masks = [cube[:, i] == v for v in (0, 1) for i in (0, 1, 2)] + polygons = [vertices[face, :][(0, 1, 3, 2), :] for face in face_masks] + self.ax.add_collection(Poly3DCollection(polygons, **kwargs)) + + @staticmethod + def rotate_vectors_from_z_axis_towards_vector(arr, v): + length = np.linalg.norm(v) + if length == 0: + return arr + projection = v[:2] + shadow = np.linalg.norm(projection) + height = v[2] + cos_alpha = height / length + sin_alpha = shadow / length + arr = arr.dot(np.array([[cos_alpha, 0, -sin_alpha], + [0, 1, 0], + [sin_alpha, 0, cos_alpha]])) + if shadow == 0: + return arr + cos_beta = v[0] / shadow + sin_beta = v[1] / shadow + return arr.dot(np.array([[cos_beta, sin_beta, 0], + [-sin_beta, cos_beta, 0], + [0, 0, 1]])) + + def draw_cylinder(self, a, b, r, wireframe=False, **kwargs): + phi = np.radians(np.linspace(0, 360, 32, endpoint=wireframe)) + circle = np.stack((np.cos(phi), np.sin(phi), np.zeros_like(phi))).T + circle = self.rotate_vectors_from_z_axis_towards_vector(circle, b - a) + # tell ax our extents, so that xyz limits are set correctly + self.ax.scatter(*(a + circle * r).T, alpha=0.0) + self.ax.scatter(*(b + circle * r).T, alpha=0.0) + if wireframe: + lines = (a + circle * r, b + circle * r) + self.ax.add_collection(Line3DCollection(lines, **kwargs)) + else: + # cap = np.stack((np.zeros_like(circle), r * np.roll(circle, 1, axis=0), r * circle), axis=1) + sides = np.stack((a + r * circle, a + r * np.roll(circle, 1, axis=0), + b + r * np.roll(circle, 1, axis=0), b + r * circle), axis=1) + # caps = np.concatenate((a + cap, b + cap)) + self.ax.add_collection(Poly3DCollection(sides, **kwargs)) + # self.ax.add_collection(Poly3DCollection(caps, **kwargs)) + + def draw_tube(self, a, b, r, R, wireframe=False, **kwargs): + phi = np.radians(np.linspace(0, 360, 32, endpoint=wireframe)) + circle = np.stack((np.cos(phi), np.sin(phi), np.zeros_like(phi))).T + circle = self.rotate_vectors_from_z_axis_towards_vector(circle, b - a) + # tell ax our extents, so that xyz limits are set correctly + self.ax.scatter(*(b + circle * R).T, alpha=0.0) + if wireframe: + lines = (a + circle * r, a + circle * R, b + circle * r, b + circle * R) + self.ax.add_collection(Line3DCollection(lines, **kwargs)) + else: + ring = np.stack((r * circle, r * np.roll(circle, 1, axis=0), R * np.roll(circle, 1, axis=0), R * circle), + axis=1) + self.ax.add_collection(Poly3DCollection(a + ring, **kwargs)) + self.ax.add_collection(Poly3DCollection(b + ring, **kwargs)) + + def draw_sphere(self, origin, radius, wireframe=False, **kwargs): + longitude = np.radians(np.linspace(0, 360, 16, endpoint=False))[:, np.newaxis] + latitude = np.radians(np.linspace(-90, 90, 8, endpoint=True))[np.newaxis, :] + z = np.sin(latitude) + r = np.cos(latitude) + x = r*np.cos(longitude) + y = r*np.sin(longitude) + unit_sphere = np.stack((x, y, np.broadcast_to(z, x.shape)), axis=-1) + sphere = unit_sphere * radius + origin + if wireframe: + parallels = np.reshape(np.stack((sphere[:, 1:-1], np.roll(sphere[:, 1:-1], 1, axis=0)), axis=2), (-1, 2, 3)) + meridians = np.reshape(np.stack((sphere, np.roll(sphere, 1, axis=1)), axis=-2)[:, 1:], (-1, 2, 3)) + self.ax.add_collection(Line3DCollection(parallels, **kwargs)) + self.ax.add_collection(Line3DCollection(meridians, **kwargs)) + else: + axis = ((), (0,), (0,1), (1,)) + quads = np.reshape(np.stack([np.roll(sphere, 1, axis=ax) for ax in axis], axis=2), (-1, 4, 3)) + self.ax.add_collection(Poly3DCollection(quads, **kwargs)) + + +def main(): + v = Visualizer3d() + v.draw_tube(np.array((1, 1, 1)), np.array((5, 8, 7)), 2, 3, facecolors='r', edgecolors='k') + v.draw_cylinder(np.array((1, 7, 7)), np.array((5, 3, 2)), 1, facecolors='y', edgecolors='k') + v.draw_box(np.array((5, 2, 2)), np.array((3, 3, 5)), facecolors='g', edgecolors='k') + v.draw_sphere(np.array((8, 8, 3)), 2, facecolors='navy', edgecolors='k') + v.draw_tube(np.array((3, 1, 2)), np.array((7, 8, 7)), 1, 3, colors='cyan', wireframe=True) + v.draw_cylinder(np.array((8, 7, 7)), np.array((5, 3, 8)), 4, wireframe=True) + v.draw_box(np.array((10, 10, 10)), np.array((0, 0, 0)), colors='k', wireframe=True) + v.draw_sphere(np.array((8, 3, 3)), 2, colors='purple', wireframe=True) + v.show() + + +if __name__ == "__main__": + main() diff --git a/ef/util/__pycache__/__init__.cpython-35.pyc b/ef/util/__pycache__/__init__.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d8a5e80e9f698c35c6bdc7ab81860bfff66c247 GIT binary patch literal 108 zcmWgR<>k7~;Tg>U1dl-k3@`#24nSPY0whux7=kq!{Z=v*frJsnFG)Q;eIQCr(=RQ_ h%+ZgJ&&3;=K66gvO_ literal 0 HcmV?d00001 diff --git a/ef/util/__pycache__/runner.cpython-35.pyc b/ef/util/__pycache__/runner.cpython-35.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f41d17d38718af69b8f038281d66016f63cf4fa GIT binary patch literal 1946 zcmZWq&2HpG5U%$BOg!2AkOXNVS`Zf>m@m>}3?~A|XwXtif(?Jn`7xwkOGs z5(z1}?4$4&JV$fn#4GHHui7)SBXHd9nyT)uufD4491aHkzk(MRANxdq)5c>%evZ*x zV@mM~Du~8wSB-@NgQPG0Ex8u2%L}tl}(TE z=dfeAWPipn{H5^PGghF%+oyDk9uXHzKD>uIILltn%TlVvLH)M9tjcem4TmRqXOp}f z)^8Igi68!27^fJ`A*MS)cSJD+%LoSAt4NF@OFIc7(SB0RW|K0FZ0$6%$P|a8`!b7{ zhCfIJP`y9uT*~ZZ-sZ)LGJxP*2XUO2c^k)l_R8By^u%I!1$wx$C&mUde;KNQIfMX= zQ@j8R$^;2~rk3HKEw(`~IAJ0sFar2ahjUxr)|G1Gwu-a7klIa(s*!pCbaAGtnbBgg z2PfLUsnk`PD;;v=`Rh0yxZsExNbq-l5hqG@yE2rs3YD z@_@Iq$)P(_Q=Og(b4}i%$6_07uHF1@4)S;d(`?A}M%ruUS(e`}oFNjaomSqqKWlLl z`8sIjtTxR0v#W-GXsc?pm!(xLOYQKib`gs9l$=%9GV%=9idhz>r1nU?qjQ+mg-MY|(*fHU#hVNIh0igI@BJ0TW$km%yQBBw*&@MAWg&01+tfwg zYUljL`I-8Zw**Q~(jqUVcIv7qu%cCY9R((d<6hHFRLkk_^0}4Fm6Bx}uU+~pM;_4C z0BEV?KN(d6I6B5?KEcEp9f(~qus#zuvU)6p@We+(yExp;?neIxe8+IF#p&Z79@Q+x-Rf>cjgV&5BhOs#QaD;pCF4XZ+N;DqkpmeD3qm`7mdWv3X; zGfb$Y7PpYO(z)5Xd#k&rOeDHjXVR~}fL49Z(*fIeWH#)2YbY}oI%kaD9H72r=|iWjdC Date: Wed, 18 Jul 2018 13:41:27 +0300 Subject: [PATCH 28/30] Jupyter file has been added --- examples/maxwell_check/maxwell_check.ipynb | 582 +++++++++++++++++++++ 1 file changed, 582 insertions(+) create mode 100644 examples/maxwell_check/maxwell_check.ipynb diff --git a/examples/maxwell_check/maxwell_check.ipynb b/examples/maxwell_check/maxwell_check.ipynb new file mode 100644 index 00000000..b372a48f --- /dev/null +++ b/examples/maxwell_check/maxwell_check.ipynb @@ -0,0 +1,582 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "sys.path.append('../../')\n", + "from ef.config.visualizer import Visualizer3d\n", + "from ef.config.efconf import EfConf\n", + "from ef.config.components import *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "According to theory, the ion momentum distribution in the ion gas obeys the Maxwell distribution. The Maxwell distribution for discrete values has the following form:\n", + "\\begin{equation}\n", + "N_i = N\\ \\left(\\frac{1}{2mkT}\\right)^{3/2} p_i^2 \\ e^{-\\frac{p_i^2}{2mkT}} \\Delta p_i,\n", + "\\end{equation}\n", + "where $N_i$ is the expected number of particles in the single-particle microstate i, which is characterized by the momentum interval ($p_i$, $p_i + \\Delta p_i$), \n", + "$N$ is the total number of particles in the isolated system,\n", + "$m$ is the ion mass,\n", + "$T$ is the equilibrium temperature of the system, $k$ is the Boltzmann constant." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To perform the simulation of the ion gas evolution, the necessary step is to create an isolated system, where our gas will be contained and come to an equilibrium. As a simple case, rigid boxlike boundaries had been implemented, that cause ions to be reflected from them." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Suppose we have an ensemble of charged particles, which have the energy $E = 1\\ eV$ and are put into the isolated system. It's mass $m$ and charge $q$ are $q = 4.8\\cdot10^{-10}\\ [cgs]$, $m = 9.1\\cdot10^{-24}\\ [g]$. Since it's energy is nonrelativistic, it's possible to calculate it's speed simply as $v = \\sqrt{ 2 E / m } = 1.808\\cdot10^{9} ~ [cm/s]$. \n", + "Firstly, we have to initialize the linear size of our isolated system (assuming that it will be a cube). We put our box to be the $0.004\\ [cm]$ linear scale size, which contains $N = 1000$ particles. Knowing the number of particles and the volume of the box, you can calculate the density of particles: $V = 0.004^3 [cm^3] = 6.4\\cdot 10^{-08} [cm^3],\\ n = N/V = 1.25\\cdot 10^{10} [cm^{-3}]$, where V is the volume of the box, n is the density of ions inside this box." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "q = 4.800e-10 [cgs]\n", + "m = 1.670e-24 [g]\n", + "E = 1.000e+00 [eV] = 1.602e-12 [erg]\n", + "v = 1.385e+06 [cm/s]; p = 2.313e-18 [g * cm/s]\n", + "T = 7.740e+03 [K]\n", + "N = 8.000e+02\n", + "box_size = 4.000e-03 x 4.000e-03 x 4.000e-03 [cm^3]\n", + "n = 1.250e+10\n" + ] + } + ], + "source": [ + "from math import *\n", + "\n", + "m = 1.67e-24\n", + "q = 4.8e-10\n", + "print( \"q = {:.3e} [cgs]\".format( q ) )\n", + "print( \"m = {:.3e} [g]\".format( m ) )\n", + "\n", + "ev_to_cgs = 1.60218e-12\n", + "E = 1 * ev_to_cgs\n", + "v = sqrt( 2 * E / m )\n", + "k_B = 1.38e-16\n", + "T = (2/3)* (E /k_B)\n", + "print( \"E = {:.3e} [eV] = {:.3e} [erg]\".format( E / ev_to_cgs, E ) )\n", + "print( \"v = {:.3e} [cm/s]; p = {:.3e} [g * cm/s]\".format( v, v * m ) )\n", + "print( \"T = {:.3e} [K]\".format( T ) )\n", + "\n", + "N = 800\n", + "lin_size_box = 0.004\n", + "V = lin_size_box ** 3\n", + "n = N / V\n", + "print( \"N = {:.3e}\".format( N ) )\n", + "print( \"box_size = {:.3e} x {:.3e} x {:.3e} [cm^3]\".format( lin_size_box, lin_size_box, lin_size_box ) )\n", + "print( \"n = {:.3e}\".format( n ) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Suppose that particle distribution inside the box is uniform. Then we should choose the time step size $dt$ equals to the time for which a particle passes half the mean free path. That choice of the path allows us to take into account the most of collisions underwent by each particle. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "free_path = 1.724e-06 [cm]\n", + "dt = 6.221e-13 [s]\n" + ] + } + ], + "source": [ + "n_linear = n ** (1/3)\n", + "free_path = lin_size_box / n_linear \n", + "dt = free_path / v / 2\n", + "\n", + "print( \"free_path = {:.3e} [cm]\".format( free_path ) )\n", + "print( \"dt = {:.3e} [s]\".format( dt ) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After we have completed estimation of the parameters, we will start creating our config file to perform the simulation. First, it's necessary to set a total simulation time and the time step corresponding to the above estimated value. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Time grid]\n", + "total_time = 6.221e-09\n", + "time_save_step = 6.221e-12\n", + "time_step_size = 6.221e-13\n", + "\n", + "[Spatial mesh]\n", + "grid_x_size = 10.0\n", + "grid_x_step = 1.0\n", + "grid_y_size = 10.0\n", + "grid_y_step = 1.0\n", + "grid_z_size = 10.0\n", + "grid_z_step = 1.0\n", + "\n", + "[Output filename]\n", + "output_filename_prefix = out_\n", + "output_filename_suffix = .h5\n", + "\n", + "[Boundary conditions]\n", + "boundary_phi_right = 0.0\n", + "boundary_phi_left = 0.0\n", + "boundary_phi_bottom = 0.0\n", + "boundary_phi_top = 0.0\n", + "boundary_phi_near = 0.0\n", + "boundary_phi_far = 0.0\n", + "\n", + "[Particle interaction model]\n", + "particle_interaction_model = PIC\n", + "\n", + "\n" + ] + } + ], + "source": [ + "maxwell_check = EfConf()\n", + "#vis = Visualizer3d()\n", + "\n", + "maxwell_check.time_grid = TimeGrid(\n", + " total = 6.221e-9,\n", + " step = 6.221e-13,\n", + " save_step = 6.221e-12\n", + ")\n", + "\n", + "print( maxwell_check.export_to_string() )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we select a spatial grid that is characterized by the size of the considered volume and sets the rigid walls at the boundaries of the spatial grid. Such walls were added to the Domain.py class." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Time grid]\n", + "total_time = 6.221e-09\n", + "time_save_step = 6.221e-12\n", + "time_step_size = 6.221e-13\n", + "\n", + "[Spatial mesh]\n", + "grid_x_size = 0.004\n", + "grid_x_step = 0.0001\n", + "grid_y_size = 0.004\n", + "grid_y_step = 0.0001\n", + "grid_z_size = 0.004\n", + "grid_z_step = 0.0001\n", + "\n", + "[Output filename]\n", + "output_filename_prefix = out_\n", + "output_filename_suffix = .h5\n", + "\n", + "[Boundary conditions]\n", + "boundary_phi_right = 0.0\n", + "boundary_phi_left = 0.0\n", + "boundary_phi_bottom = 0.0\n", + "boundary_phi_top = 0.0\n", + "boundary_phi_near = 0.0\n", + "boundary_phi_far = 0.0\n", + "\n", + "[Particle interaction model]\n", + "particle_interaction_model = PIC\n", + "\n", + "\n" + ] + } + ], + "source": [ + "maxwell_check.spatial_mesh = SpatialMesh(\n", + " size = ( 0.004, 0.004, 0.004 ),\n", + " step = ( 0.0001, 0.0001, 0.0001 )\n", + ")\n", + "\n", + "print( maxwell_check.export_to_string() )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us choose the size of the source, where the ions will appear. In accordance with the values estimated above, we choose the size of a particle source, the same as the size of a spatial grid, for uniform distribution of ions. But their initial directions of momenta in space will also be distributed uniformly, but their absolute values of momenta will be the same. Such changes were added in the ParticleSource.py class." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "maxwell_check.sources = [ \n", + " ParticleSource(\n", + " name = \"cathode_emitter\",\n", + " initial_particles = 1000,\n", + " particles_to_generate_each_step = 0,\n", + " #shape = Box( origin = (0.1, 0.1, 0.1), size = ( 0.01, 0.01, 0.01 ) ),\n", + " shape = Box( origin = (0.000, 0.000, 0.000), size = ( -0.004, 0.004, 0.004 ) ), # hack left > right error\n", + " momentum = ( 0.00, 0.00, 2.313e-18 ),\n", + " temperature = 0.0,\n", + " charge = 4.8e-10,\n", + " mass = 1.67e-24\n", + " )\n", + "]\n", + "\n", + "print( maxwell_check.export_to_string() )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As is well known, the determination of the Maxwell distribution in gases is due to the presence of collisions of particles inside the isolated system. Thus, we have to assume the particle interaction model with allowance for collision. But due to the high degree of rarefaction of the gas, it is possible to apply an approximation of the binary collision model in this simulation: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "maxwell_check.particle_interaction_model = ParticleInteractionModel(\n", + " model = \"binary\"\n", + ")\n", + "\n", + "print( maxwell_check.export_to_string() )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The last step is to specify pattern for output file names. They will be of the form maxwell_check_0001000.h5, where 0001000 is a time step number." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Time grid]\n", + "total_time = 6.221e-09\n", + "time_save_step = 6.221e-12\n", + "time_step_size = 6.221e-13\n", + "\n", + "[Spatial mesh]\n", + "grid_x_size = 0.004\n", + "grid_x_step = 0.0001\n", + "grid_y_size = 0.004\n", + "grid_y_step = 0.0001\n", + "grid_z_size = 0.004\n", + "grid_z_step = 0.0001\n", + "\n", + "[Particle_source_box.cathode_emitter]\n", + "box_x_left = 0.004\n", + "box_x_right = 0.0\n", + "box_y_bottom = 0.0\n", + "box_y_top = 0.004\n", + "box_z_near = 0.0\n", + "box_z_far = 0.004\n", + "initial_number_of_particles = 1000\n", + "particles_to_generate_each_step = 0\n", + "mean_momentum_x = 0.0\n", + "mean_momentum_y = 0.0\n", + "mean_momentum_z = 2.313e-18\n", + "temperature = 0.0\n", + "charge = 4.8e-10\n", + "mass = 1.67e-24\n", + "\n", + "[Output filename]\n", + "output_filename_prefix = maxwell_check_\n", + "output_filename_suffix = .h5\n", + "\n", + "[Boundary conditions]\n", + "boundary_phi_right = 0.0\n", + "boundary_phi_left = 0.0\n", + "boundary_phi_bottom = 0.0\n", + "boundary_phi_top = 0.0\n", + "boundary_phi_near = 0.0\n", + "boundary_phi_far = 0.0\n", + "\n", + "[Particle interaction model]\n", + "particle_interaction_model = binary\n", + "\n", + "\n" + ] + } + ], + "source": [ + "maxwell_check.output_file = OutputFile(\n", + " prefix = \"maxwell_check_\",\n", + " suffix = \".h5\"\n", + ")\n", + "\n", + "print( maxwell_check.export_to_string() )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To start the simulation, the config should be provided as an argument to the main.py. \n", + "Note: before starting the simulation, make sure that all necessary packages (scipy, numpy, etc.) are already installed. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "command: python3 ../../main.py /tmp/tmp0lmq0dil.ini\n", + "b'Config file is: /tmp/tmp0lmq0dil.ini'\n", + "b'[ Time grid ]'\n", + "b'total_time = 6.221e-09'\n", + "b'time_save_step = 6.221e-12'\n", + "b'time_step_size = 6.221e-13'\n", + "b'[ Spatial mesh ]'\n", + "b'grid_x_size = 0.004'\n", + "b'grid_x_step = 0.0001'\n", + "b'grid_y_size = 0.004'\n", + "b'grid_y_step = 0.0001'\n", + "b'grid_z_size = 0.004'\n", + "b'grid_z_step = 0.0001'\n", + "b'[ Particle_source_box.cathode_emitter ]'\n", + "b'box_x_left = 0.004'\n", + "b'box_x_right = 0.0'\n", + "b'box_y_bottom = 0.0'\n", + "b'box_y_top = 0.004'\n", + "b'box_z_near = 0.0'\n", + "b'box_z_far = 0.004'\n", + "b'initial_number_of_particles = 1000'\n", + "b'particles_to_generate_each_step = 0'\n", + "b'mean_momentum_x = 0.0'\n", + "b'mean_momentum_y = 0.0'\n", + "b'mean_momentum_z = 2.313e-18'\n", + "b'temperature = 0.0'\n", + "b'charge = 4.8e-10'\n", + "b'mass = 1.67e-24'\n", + "b'[ Output filename ]'\n", + "b'output_filename_prefix = maxwell_check_'\n", + "b'output_filename_suffix = .h5'\n", + "b'[ Boundary conditions ]'\n", + "b'boundary_phi_right = 0.0'\n", + "b'boundary_phi_left = 0.0'\n", + "b'boundary_phi_bottom = 0.0'\n", + "b'boundary_phi_top = 0.0'\n", + "b'boundary_phi_near = 0.0'\n", + "b'boundary_phi_far = 0.0'\n", + "b'[ Particle interaction model ]'\n", + "b'particle_interaction_model = binary'\n", + "b'Time step was shrinked to 6.221E-13 from 6.221E-13 to fit round number of cells.'\n", + "b'Time save step was shrinked to 6.221E-12 from 6.221E-12 to be a multiple of time step.'\n", + "b'Error: box_x_left > box_x_right'\n" + ] + } + ], + "source": [ + "from ef.util.runner import EfRunner\n", + "\n", + "\n", + "runner = EfRunner( conf = maxwell_check, ef_command=\"python3 ../../main.py\" )\n", + "runner.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After the procedure of simulation completes, the corresponding *.h5 will emerge in the directory. To visualize the result obtained during the simulation, we need to extract the momenta of all the ions and make a histogram of momentum in comparison with a theoretical maxwell distribution:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os, glob\n", + "import h5py\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import math\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "\n", + "def main():\n", + " filename = 'maxwell_check_0010000.h5'\n", + " h5 = h5py.File(filename, mode=\"r\")\n", + " start_x, end_x, start_y, end_y, start_z, end_z = get_source_geometry(h5) #(*1)\n", + " \n", + " m = 1.67e-24\n", + " T = 7.740e+03\n", + " N = 1000\n", + " k_B = 1.38e-16\n", + " dN, p_grid, p = analyt_maxwell_distrib(N, T, h5) #(*2)\n", + " p_sim_av = np.mean(p)\n", + "\n", + " p_average = (8*m*k_B*T/math.pi) ** (1/2) #(*3)\n", + " p_mst_prob = (2*m*k_B*T) ** (1/2) #(*4)\n", + " print( \"p_sim_av = {:.3e} [cm/s]\".format( p_sim_av ) ) #(*5) \n", + " print( \"p_theor_average = {:.3e} [cm/s]\".format( p_average ) ) #(*6) \n", + " plt.figure()\n", + " plt.hist(p_end,20)\n", + " plt.plot(p_grid, dN)\n", + " plt.xlim(0, p_end.max())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(*1) The extraction of source geometry:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_source_geometry(h5file):\n", + " start_y = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_y_top\"]\n", + " end_y = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_y_bottom\"]\n", + " start_x = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_x_left\"]\n", + " end_x = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_x_right\"]\n", + " start_z = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_z_near\"]\n", + " end_z = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_z_far\"]\n", + " return start_x, end_x, start_y, end_y, start_z, end_z" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(*2) Obtaining an analytical representation of the Maxwell distribution for the number of particles equal to $ N $ with an extraction of absolute values of momenta for each simulated particle after a time step corresponding to the \"filename\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def analyt_maxwell_distrib(num_particles, temperature, h5file):\n", + " p_xend = h5file[\"/Particle_sources/cathode_emitter/momentum_x\"][:]\n", + " p_yend = h5file[\"/Particle_sources/cathode_emitter/momentum_y\"][:]\n", + " p_zend = h5file[\"/Particle_sources/cathode_emitter/momentum_z\"][:]\n", + " mass = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"mass\"]\n", + " kB = 1.38e-16\n", + "\n", + " p = (p_xend ** 2 + p_yend ** 2 + p_zend ** 2) ** (1/2)\n", + "\n", + " p_grid = np.arange(0.0, p.max(), (p.max() - p.min())/150)\n", + " p_grid_1 = p_grid[1:]\n", + " dp = p_grid_1 - p_grid[0:len(p_grid)-1]\n", + "\n", + " p_average = (8*m*k_B*T/math.pi) ** (1/2) #(*3)\n", + " p_mst_prob = (2*m*k_B*T) ** (1/2) #(*4)\n", + " \n", + " distr = 4*math.pi* (1/(2*math.pi*mass*kB*temperature)) ** (3/2) * (p_grid[1:] ** 2) * np.exp(-1* (p_grid[1:] ** 2) /(2*kB*mass*temperature))*dp\n", + " dN = num_particles * distr\n", + "\n", + " return dN, p_grid[1:], p" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(*3, *4) The typical values of average and most probable momenta respectively. To remind, such values are calculated by the following formulas: \n", + "\n", + "\\begin{eqnarray}\n", + " \\left< p \\right> = \\sqrt{\\frac{8mkT}{\\pi}} \\hspace{3em} p_p = \\sqrt{2mkT}\n", + "\\end{eqnarray}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you track changes in the particle distribution as a function of momenta, you will see that over time the resulting distribution approaches to the theoretical distribution. Moreover the typical values of the ion gas have to tend to the theoretical values. It is suggested to compare the obtained values by the simulation with theoretical values (*5,*6)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 2fe5a7bf8f4090f0f4c76a7037ea6a836eddb9b4 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Fri, 20 Jul 2018 10:08:12 +0300 Subject: [PATCH 29/30] maxwell_check.ipynb config parameters has been fixed --- examples/maxwell_check/maxwell_check.ipynb | 385 +++++++++++++-------- 1 file changed, 235 insertions(+), 150 deletions(-) diff --git a/examples/maxwell_check/maxwell_check.ipynb b/examples/maxwell_check/maxwell_check.ipynb index b372a48f..170bcd78 100644 --- a/examples/maxwell_check/maxwell_check.ipynb +++ b/examples/maxwell_check/maxwell_check.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -19,9 +19,9 @@ "source": [ "According to theory, the ion momentum distribution in the ion gas obeys the Maxwell distribution. The Maxwell distribution for discrete values has the following form:\n", "\\begin{equation}\n", - "N_i = N\\ \\left(\\frac{1}{2mkT}\\right)^{3/2} p_i^2 \\ e^{-\\frac{p_i^2}{2mkT}} \\Delta p_i,\n", + "\\frac{N_i}{N} = \\left(\\frac{1}{2mkT}\\right)^{3/2} p_i^2 \\ e^{-\\frac{p_i^2}{2mkT}},\n", "\\end{equation}\n", - "where $N_i$ is the expected number of particles in the single-particle microstate i, which is characterized by the momentum interval ($p_i$, $p_i + \\Delta p_i$), \n", + "where $N_i$ is the expected number of particles in the single-particle microstate i, which is characterized by the momentum interval $p_i$, \n", "$N$ is the total number of particles in the isolated system,\n", "$m$ is the ion mass,\n", "$T$ is the equilibrium temperature of the system, $k$ is the Boltzmann constant." @@ -39,12 +39,12 @@ "metadata": {}, "source": [ "Suppose we have an ensemble of charged particles, which have the energy $E = 1\\ eV$ and are put into the isolated system. It's mass $m$ and charge $q$ are $q = 4.8\\cdot10^{-10}\\ [cgs]$, $m = 9.1\\cdot10^{-24}\\ [g]$. Since it's energy is nonrelativistic, it's possible to calculate it's speed simply as $v = \\sqrt{ 2 E / m } = 1.808\\cdot10^{9} ~ [cm/s]$. \n", - "Firstly, we have to initialize the linear size of our isolated system (assuming that it will be a cube). We put our box to be the $0.004\\ [cm]$ linear scale size, which contains $N = 1000$ particles. Knowing the number of particles and the volume of the box, you can calculate the density of particles: $V = 0.004^3 [cm^3] = 6.4\\cdot 10^{-08} [cm^3],\\ n = N/V = 1.25\\cdot 10^{10} [cm^{-3}]$, where V is the volume of the box, n is the density of ions inside this box." + "Firstly, we have to initialize the linear size of our isolated system (assuming that it will be a cube). We put our box to be the $0.004\\ [cm]$ linear scale size, which contains $N = 1000$ particles. Knowing the number of particles and the volume of the box, you can calculate the density of particles: $V = 0.004^3 \\ \\ [cm^3] = 6.4\\cdot 10^{-08}\\ \\ [cm^3],\\ n = N/V = 1.25\\cdot 10^{10}\\ \\ [cm^{-3}]$, where V is the volume of the box, n is the density of ions inside this box." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -56,9 +56,9 @@ "E = 1.000e+00 [eV] = 1.602e-12 [erg]\n", "v = 1.385e+06 [cm/s]; p = 2.313e-18 [g * cm/s]\n", "T = 7.740e+03 [K]\n", - "N = 8.000e+02\n", + "N = 1.000e+03\n", "box_size = 4.000e-03 x 4.000e-03 x 4.000e-03 [cm^3]\n", - "n = 1.250e+10\n" + "n = 1.562e+10\n" ] } ], @@ -79,7 +79,7 @@ "print( \"v = {:.3e} [cm/s]; p = {:.3e} [g * cm/s]\".format( v, v * m ) )\n", "print( \"T = {:.3e} [K]\".format( T ) )\n", "\n", - "N = 800\n", + "N = 1000\n", "lin_size_box = 0.004\n", "V = lin_size_box ** 3\n", "n = N / V\n", @@ -92,27 +92,27 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Suppose that particle distribution inside the box is uniform. Then we should choose the time step size $dt$ equals to the time for which a particle passes half the mean free path. That choice of the path allows us to take into account the most of collisions underwent by each particle. " + "Suppose that particle distribution inside the box is uniform. Then we should choose the time step size $dt$ equals to the time for which a particle passes one tenth part the mean free path. That choice of the path allows us to take into account the most of collisions underwent by each particle. " ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "free_path = 1.724e-06 [cm]\n", - "dt = 6.221e-13 [s]\n" + "free_path = 1.600e-06 [cm]\n", + "dt = 1.155e-13 [s]\n" ] } ], "source": [ "n_linear = n ** (1/3)\n", "free_path = lin_size_box / n_linear \n", - "dt = free_path / v / 2\n", + "dt = free_path / v / 10\n", "\n", "print( \"free_path = {:.3e} [cm]\".format( free_path ) )\n", "print( \"dt = {:.3e} [s]\".format( dt ) )" @@ -127,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -135,9 +135,9 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 6.221e-09\n", - "time_save_step = 6.221e-12\n", - "time_step_size = 6.221e-13\n", + "total_time = 1.155e-10\n", + "time_save_step = 1.155e-11\n", + "time_step_size = 1.155e-13\n", "\n", "[Spatial mesh]\n", "grid_x_size = 10.0\n", @@ -171,9 +171,9 @@ "#vis = Visualizer3d()\n", "\n", "maxwell_check.time_grid = TimeGrid(\n", - " total = 6.221e-9,\n", - " step = 6.221e-13,\n", - " save_step = 6.221e-12\n", + " total = 1.155e-10,\n", + " step = 1.155e-13,\n", + " save_step = 1.155e-11\n", ")\n", "\n", "print( maxwell_check.export_to_string() )" @@ -188,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -196,9 +196,9 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 6.221e-09\n", - "time_save_step = 6.221e-12\n", - "time_step_size = 6.221e-13\n", + "total_time = 1.155e-10\n", + "time_save_step = 1.155e-11\n", + "time_step_size = 1.155e-13\n", "\n", "[Spatial mesh]\n", "grid_x_size = 0.004\n", @@ -245,9 +245,61 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Time grid]\n", + "total_time = 1.155e-10\n", + "time_save_step = 1.155e-11\n", + "time_step_size = 1.155e-13\n", + "\n", + "[Spatial mesh]\n", + "grid_x_size = 0.004\n", + "grid_x_step = 0.0001\n", + "grid_y_size = 0.004\n", + "grid_y_step = 0.0001\n", + "grid_z_size = 0.004\n", + "grid_z_step = 0.0001\n", + "\n", + "[Particle_source_box.cathode_emitter]\n", + "box_x_left = 0.0\n", + "box_x_right = 0.004\n", + "box_y_bottom = 0.0\n", + "box_y_top = 0.004\n", + "box_z_near = 0.0\n", + "box_z_far = 0.004\n", + "initial_number_of_particles = 1000\n", + "particles_to_generate_each_step = 0\n", + "mean_momentum_x = 0.0\n", + "mean_momentum_y = 0.0\n", + "mean_momentum_z = 2.313e-18\n", + "temperature = 0.0\n", + "charge = 4.8e-10\n", + "mass = 1.67e-24\n", + "\n", + "[Output filename]\n", + "output_filename_prefix = out_\n", + "output_filename_suffix = .h5\n", + "\n", + "[Boundary conditions]\n", + "boundary_phi_right = 0.0\n", + "boundary_phi_left = 0.0\n", + "boundary_phi_bottom = 0.0\n", + "boundary_phi_top = 0.0\n", + "boundary_phi_near = 0.0\n", + "boundary_phi_far = 0.0\n", + "\n", + "[Particle interaction model]\n", + "particle_interaction_model = PIC\n", + "\n", + "\n" + ] + } + ], "source": [ "maxwell_check.sources = [ \n", " ParticleSource(\n", @@ -255,7 +307,7 @@ " initial_particles = 1000,\n", " particles_to_generate_each_step = 0,\n", " #shape = Box( origin = (0.1, 0.1, 0.1), size = ( 0.01, 0.01, 0.01 ) ),\n", - " shape = Box( origin = (0.000, 0.000, 0.000), size = ( -0.004, 0.004, 0.004 ) ), # hack left > right error\n", + " shape = Box( origin = (0.004, 0.000, 0.000), size = ( -0.004, 0.004, 0.004 ) ), # hack left > right error\n", " momentum = ( 0.00, 0.00, 2.313e-18 ),\n", " temperature = 0.0,\n", " charge = 4.8e-10,\n", @@ -275,11 +327,63 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Time grid]\n", + "total_time = 1.155e-10\n", + "time_save_step = 1.155e-11\n", + "time_step_size = 1.155e-13\n", + "\n", + "[Spatial mesh]\n", + "grid_x_size = 0.004\n", + "grid_x_step = 0.0001\n", + "grid_y_size = 0.004\n", + "grid_y_step = 0.0001\n", + "grid_z_size = 0.004\n", + "grid_z_step = 0.0001\n", + "\n", + "[Particle_source_box.cathode_emitter]\n", + "box_x_left = 0.0\n", + "box_x_right = 0.004\n", + "box_y_bottom = 0.0\n", + "box_y_top = 0.004\n", + "box_z_near = 0.0\n", + "box_z_far = 0.004\n", + "initial_number_of_particles = 1000\n", + "particles_to_generate_each_step = 0\n", + "mean_momentum_x = 0.0\n", + "mean_momentum_y = 0.0\n", + "mean_momentum_z = 2.313e-18\n", + "temperature = 0.0\n", + "charge = 4.8e-10\n", + "mass = 1.67e-24\n", + "\n", + "[Output filename]\n", + "output_filename_prefix = out_\n", + "output_filename_suffix = .h5\n", + "\n", + "[Boundary conditions]\n", + "boundary_phi_right = 0.0\n", + "boundary_phi_left = 0.0\n", + "boundary_phi_bottom = 0.0\n", + "boundary_phi_top = 0.0\n", + "boundary_phi_near = 0.0\n", + "boundary_phi_far = 0.0\n", + "\n", + "[Particle interaction model]\n", + "particle_interaction_model = binary\n", + "\n", + "\n" + ] + } + ], "source": [ "maxwell_check.particle_interaction_model = ParticleInteractionModel(\n", " model = \"binary\"\n", @@ -297,7 +401,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -305,9 +409,9 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 6.221e-09\n", - "time_save_step = 6.221e-12\n", - "time_step_size = 6.221e-13\n", + "total_time = 1.155e-10\n", + "time_save_step = 1.155e-11\n", + "time_step_size = 1.155e-13\n", "\n", "[Spatial mesh]\n", "grid_x_size = 0.004\n", @@ -318,8 +422,8 @@ "grid_z_step = 0.0001\n", "\n", "[Particle_source_box.cathode_emitter]\n", - "box_x_left = 0.004\n", - "box_x_right = 0.0\n", + "box_x_left = 0.0\n", + "box_x_right = 0.004\n", "box_y_bottom = 0.0\n", "box_y_top = 0.004\n", "box_z_near = 0.0\n", @@ -371,56 +475,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "command: python3 ../../main.py /tmp/tmp0lmq0dil.ini\n", - "b'Config file is: /tmp/tmp0lmq0dil.ini'\n", - "b'[ Time grid ]'\n", - "b'total_time = 6.221e-09'\n", - "b'time_save_step = 6.221e-12'\n", - "b'time_step_size = 6.221e-13'\n", - "b'[ Spatial mesh ]'\n", - "b'grid_x_size = 0.004'\n", - "b'grid_x_step = 0.0001'\n", - "b'grid_y_size = 0.004'\n", - "b'grid_y_step = 0.0001'\n", - "b'grid_z_size = 0.004'\n", - "b'grid_z_step = 0.0001'\n", - "b'[ Particle_source_box.cathode_emitter ]'\n", - "b'box_x_left = 0.004'\n", - "b'box_x_right = 0.0'\n", - "b'box_y_bottom = 0.0'\n", - "b'box_y_top = 0.004'\n", - "b'box_z_near = 0.0'\n", - "b'box_z_far = 0.004'\n", - "b'initial_number_of_particles = 1000'\n", - "b'particles_to_generate_each_step = 0'\n", - "b'mean_momentum_x = 0.0'\n", - "b'mean_momentum_y = 0.0'\n", - "b'mean_momentum_z = 2.313e-18'\n", - "b'temperature = 0.0'\n", - "b'charge = 4.8e-10'\n", - "b'mass = 1.67e-24'\n", - "b'[ Output filename ]'\n", - "b'output_filename_prefix = maxwell_check_'\n", - "b'output_filename_suffix = .h5'\n", - "b'[ Boundary conditions ]'\n", - "b'boundary_phi_right = 0.0'\n", - "b'boundary_phi_left = 0.0'\n", - "b'boundary_phi_bottom = 0.0'\n", - "b'boundary_phi_top = 0.0'\n", - "b'boundary_phi_near = 0.0'\n", - "b'boundary_phi_far = 0.0'\n", - "b'[ Particle interaction model ]'\n", - "b'particle_interaction_model = binary'\n", - "b'Time step was shrinked to 6.221E-13 from 6.221E-13 to fit round number of cells.'\n", - "b'Time save step was shrinked to 6.221E-12 from 6.221E-12 to be a multiple of time step.'\n", - "b'Error: box_x_left > box_x_right'\n" + "command: python3 ../../main.py /tmp/tmp2jvwoisd.ini\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mrunner\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mEfRunner\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mconf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmaxwell_check\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mef_command\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"python3 ../../main.py\"\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mrunner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/PycharmProjects/ef_python-maxwell-check/ef/util/runner.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, workdir, save_config_as)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mconfig_fname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msave_config_as\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0mconfig_file\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_fname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'w'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_configure_and_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mworkdir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig_fname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msave_config_as\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_fname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/PycharmProjects/ef_python-maxwell-check/ef/util/runner.py\u001b[0m in \u001b[0;36m_configure_and_run\u001b[0;34m(self, workdir, config_fname, config_file)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexport_to_file\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mconfig_file\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_from_file\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_fname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mworkdir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mworkdir\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"./\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msave_config_as\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/PycharmProjects/ef_python-maxwell-check/ef/util/runner.py\u001b[0m in \u001b[0;36mrun_from_file\u001b[0;34m(self, startfile, workdir)\u001b[0m\n\u001b[1;32m 36\u001b[0m \u001b[0mprocess\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mshlex\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcommand\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstdout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 38\u001b[0;31m \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstdout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 39\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0moutput\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m''\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } ], @@ -436,119 +512,128 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "After the procedure of simulation completes, the corresponding *.h5 will emerge in the directory. To visualize the result obtained during the simulation, we need to extract the momenta of all the ions and make a histogram of momentum in comparison with a theoretical maxwell distribution:" + "After the procedure of simulation completes, the corresponding *.h5 will emerge in the directory. To visualize the result obtained during the simulation, we need to extract the momenta of all the ions and make a histogram of momentum in comparison with a theoretical maxwell distribution. The function below extracts three projections of all electronic momenta after a time step corresponding to the \"filename\" (*1), and then calculates their absolute value and the probability density function with the corresponding macro parameters:" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "import os, glob\n", - "import h5py\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import math\n", - "from mpl_toolkits.mplot3d import Axes3D\n", + "def analyt_maxwell_distrib(num_particles, temperature, mass, k_B, h5file): #(*1)\n", + " p_xend = h5file[\"/Particle_sources/cathode_emitter/momentum_x\"][:]\n", + " p_yend = h5file[\"/Particle_sources/cathode_emitter/momentum_y\"][:]\n", + " p_zend = h5file[\"/Particle_sources/cathode_emitter/momentum_z\"][:]\n", "\n", - "def main():\n", - " filename = 'maxwell_check_0010000.h5'\n", - " h5 = h5py.File(filename, mode=\"r\")\n", - " start_x, end_x, start_y, end_y, start_z, end_z = get_source_geometry(h5) #(*1)\n", - " \n", - " m = 1.67e-24\n", - " T = 7.740e+03\n", - " N = 1000\n", - " k_B = 1.38e-16\n", - " dN, p_grid, p = analyt_maxwell_distrib(N, T, h5) #(*2)\n", - " p_sim_av = np.mean(p)\n", + " p = (p_xend ** 2 + p_yend ** 2 + p_zend ** 2) ** (1/2)\n", "\n", - " p_average = (8*m*k_B*T/math.pi) ** (1/2) #(*3)\n", - " p_mst_prob = (2*m*k_B*T) ** (1/2) #(*4)\n", - " print( \"p_sim_av = {:.3e} [cm/s]\".format( p_sim_av ) ) #(*5) \n", - " print( \"p_theor_average = {:.3e} [cm/s]\".format( p_average ) ) #(*6) \n", - " plt.figure()\n", - " plt.hist(p_end,20)\n", - " plt.plot(p_grid, dN)\n", - " plt.xlim(0, p_end.max())" + " p_grid = np.arange(0.0, p.max(), (p.max() - 0)/150)\n", + " distr = 4*math.pi* (1/(2*math.pi*mass*k_B*temperature)) ** (1.5) * (p_grid ** 2) * np.exp(-1* (p_grid ** 2) /(2*k_B*mass*temperature))\n", + "\n", + " return distr, p_grid, p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "(*1) The extraction of source geometry:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def get_source_geometry(h5file):\n", - " start_y = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_y_top\"]\n", - " end_y = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_y_bottom\"]\n", - " start_x = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_x_left\"]\n", - " end_x = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_x_right\"]\n", - " start_z = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_z_near\"]\n", - " end_z = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"box_z_far\"]\n", - " return start_x, end_x, start_y, end_y, start_z, end_z" + "Next, we need to visualize a histogram corresponding to the distribution determined during the simulation and compare its form with analytical function. The main.py function visualizes both of these distributions at one figure and then print its typical momenta (*4, *5) (average and most probable) to compare them with each other. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "(*2) Obtaining an analytical representation of the Maxwell distribution for the number of particles equal to $ N $ with an extraction of absolute values of momenta for each simulated particle after a time step corresponding to the \"filename\"." + "(*2, *3) The typical values of average and most probable momenta respectively. To remind, such values are calculated by the following formulas: \n", + "\n", + "\\begin{eqnarray}\n", + " \\left< p \\right> = \\sqrt{\\frac{8mkT}{\\pi}} \\hspace{3em} p_p = \\sqrt{2mkT}\n", + "\\end{eqnarray}" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "p_sim_av = 2.313e-18 [cm/s]\n", + "p_theor_average = 2.131e-18 [cm/s]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "def analyt_maxwell_distrib(num_particles, temperature, h5file):\n", - " p_xend = h5file[\"/Particle_sources/cathode_emitter/momentum_x\"][:]\n", - " p_yend = h5file[\"/Particle_sources/cathode_emitter/momentum_y\"][:]\n", - " p_zend = h5file[\"/Particle_sources/cathode_emitter/momentum_z\"][:]\n", - " mass = h5file[\"/Particle_sources/cathode_emitter\"].attrs[\"mass\"]\n", - " kB = 1.38e-16\n", + "import os, glob\n", + "import h5py\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import math\n", + "from mpl_toolkits.mplot3d import Axes3D\n", "\n", - " p = (p_xend ** 2 + p_yend ** 2 + p_zend ** 2) ** (1/2)\n", + "def main():\n", + " filename = 'maxwell_check_0010000.h5'\n", + " h5 = h5py.File(filename, mode=\"r\")\n", + " \n", + " m = 1.67e-24\n", + " T = 7.740e+03\n", + " N = 800\n", + " k_B = 1.38e-16\n", + " distr, p_grid, p = analyt_maxwell_distrib(N, T, m, k_B, h5) #(*1)\n", + " p_sim_av = np.mean(p)\n", "\n", - " p_grid = np.arange(0.0, p.max(), (p.max() - p.min())/150)\n", - " p_grid_1 = p_grid[1:]\n", - " dp = p_grid_1 - p_grid[0:len(p_grid)-1]\n", + " p_average = (8*m*k_B*T/math.pi) ** (1/2) #(*2)\n", + " p_mst_prob = (2*m*k_B*T) ** (1/2) #(*3)\n", + " print( \"p_sim_av = {:.3e} [cm/s]\".format( p_sim_av ) ) #(*4) \n", + " print( \"p_theor_average = {:.3e} [cm/s]\".format( p_average ) ) #(*5) \n", "\n", - " p_average = (8*m*k_B*T/math.pi) ** (1/2) #(*3)\n", - " p_mst_prob = (2*m*k_B*T) ** (1/2) #(*4)\n", + " plt.figure()\n", + " plt.xlabel('p, [cm/s]')\n", + " plt.xlabel('p, [cm/s]')\n", + " plt.ylabel(r'$\\rho$ (probability density function) ')\n", + " plt.title('Probability density function (PDF)')\n", + " plt.ylabel(r'$\\rho$ probability density function')\n", + " plt.plot(p_grid,distr,label = 'Theor PDF')\n", + " plt.hist(p,20, normed=1)\n", + " plt.xlim(0.0, p.max())\n", + " plt.legend()\n", + " plt.grid(True)\n", + " h5.close()\n", " \n", - " distr = 4*math.pi* (1/(2*math.pi*mass*kB*temperature)) ** (3/2) * (p_grid[1:] ** 2) * np.exp(-1* (p_grid[1:] ** 2) /(2*kB*mass*temperature))*dp\n", - " dN = num_particles * distr\n", - "\n", - " return dN, p_grid[1:], p" + "main()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "(*3, *4) The typical values of average and most probable momenta respectively. To remind, such values are calculated by the following formulas: \n", - "\n", - "\\begin{eqnarray}\n", - " \\left< p \\right> = \\sqrt{\\frac{8mkT}{\\pi}} \\hspace{3em} p_p = \\sqrt{2mkT}\n", - "\\end{eqnarray}" + "If you track changes in the particle distribution as a function of momenta, you will see that over time the resulting distribution approaches to the theoretical distribution. Moreover the typical values of the ion gas have to tend to the theoretical values. It is suggested to compare the obtained values by the simulation with theoretical values (*4,*5)" ] }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "metadata": {}, - "source": [ - "If you track changes in the particle distribution as a function of momenta, you will see that over time the resulting distribution approaches to the theoretical distribution. Moreover the typical values of the ion gas have to tend to the theoretical values. It is suggested to compare the obtained values by the simulation with theoretical values (*5,*6)" - ] + "source": [] }, { "cell_type": "code", From 96141fff6ead65c3ba42cc118e18a2786d55ac77 Mon Sep 17 00:00:00 2001 From: inghades <40892669+inghades@users.noreply.github.com> Date: Fri, 20 Jul 2018 11:18:05 +0300 Subject: [PATCH 30/30] Add files via upload --- examples/maxwell_check/maxwell_check.ipynb | 208 ++++++++++----------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/examples/maxwell_check/maxwell_check.ipynb b/examples/maxwell_check/maxwell_check.ipynb index 170bcd78..1f83f037 100644 --- a/examples/maxwell_check/maxwell_check.ipynb +++ b/examples/maxwell_check/maxwell_check.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -38,35 +38,35 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Suppose we have an ensemble of charged particles, which have the energy $E = 1\\ eV$ and are put into the isolated system. It's mass $m$ and charge $q$ are $q = 4.8\\cdot10^{-10}\\ [cgs]$, $m = 9.1\\cdot10^{-24}\\ [g]$. Since it's energy is nonrelativistic, it's possible to calculate it's speed simply as $v = \\sqrt{ 2 E / m } = 1.808\\cdot10^{9} ~ [cm/s]$. \n", - "Firstly, we have to initialize the linear size of our isolated system (assuming that it will be a cube). We put our box to be the $0.004\\ [cm]$ linear scale size, which contains $N = 1000$ particles. Knowing the number of particles and the volume of the box, you can calculate the density of particles: $V = 0.004^3 \\ \\ [cm^3] = 6.4\\cdot 10^{-08}\\ \\ [cm^3],\\ n = N/V = 1.25\\cdot 10^{10}\\ \\ [cm^{-3}]$, where V is the volume of the box, n is the density of ions inside this box." + "Suppose we have an ensemble of charged particles, which have the energy $E = 1\\ eV$ and are put into the isolated system. It's mass $m$ and charge $q$ are $q = 4.8\\cdot10^{-8}\\ [cgs]$, $m = 9.1\\cdot10^{-22}\\ [g]$. Since it's energy is nonrelativistic, it's possible to calculate it's speed simply as $v = \\sqrt{ 2 E / m } = 1.385\\cdot10^{5} ~ [cm/s]$. \n", + "Firstly, we have to initialize the linear size of our isolated system (assuming that it will be a cube). We put our box to be the $0.04\\ [cm]$ linear scale size, which contains $N = 100$ particles. Knowing the number of particles and the volume of the box, you can calculate the density of particles: $V = 0.00^3 \\ \\ [cm^3] = 6.4\\cdot 10^{-5}\\ \\ [cm^3],\\ n = N/V = 1.25\\cdot 10^{6}\\ \\ [cm^{-3}]$, where V is the volume of the box, n is the density of ions inside this box." ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "q = 4.800e-10 [cgs]\n", - "m = 1.670e-24 [g]\n", + "q = 4.800e-08 [cgs]\n", + "m = 1.670e-22 [g]\n", "E = 1.000e+00 [eV] = 1.602e-12 [erg]\n", - "v = 1.385e+06 [cm/s]; p = 2.313e-18 [g * cm/s]\n", + "v = 1.385e+05 [cm/s]; p = 2.313e-17 [g * cm/s]\n", "T = 7.740e+03 [K]\n", - "N = 1.000e+03\n", - "box_size = 4.000e-03 x 4.000e-03 x 4.000e-03 [cm^3]\n", - "n = 1.562e+10\n" + "N = 1.000e+02\n", + "box_size = 4.000e-02 x 4.000e-02 x 4.000e-02 [cm^3]\n", + "n = 1.562e+06\n" ] } ], "source": [ "from math import *\n", "\n", - "m = 1.67e-24\n", - "q = 4.8e-10\n", + "m = 1.67e-22\n", + "q = 4.8e-8\n", "print( \"q = {:.3e} [cgs]\".format( q ) )\n", "print( \"m = {:.3e} [g]\".format( m ) )\n", "\n", @@ -79,8 +79,8 @@ "print( \"v = {:.3e} [cm/s]; p = {:.3e} [g * cm/s]\".format( v, v * m ) )\n", "print( \"T = {:.3e} [K]\".format( T ) )\n", "\n", - "N = 1000\n", - "lin_size_box = 0.004\n", + "N = 100\n", + "lin_size_box = 0.04\n", "V = lin_size_box ** 3\n", "n = N / V\n", "print( \"N = {:.3e}\".format( N ) )\n", @@ -97,15 +97,15 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "free_path = 1.600e-06 [cm]\n", - "dt = 1.155e-13 [s]\n" + "free_path = 3.447e-04 [cm]\n", + "dt = 2.489e-10 [s]\n" ] } ], @@ -127,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -135,9 +135,9 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 1.155e-10\n", - "time_save_step = 1.155e-11\n", - "time_step_size = 1.155e-13\n", + "total_time = 2.489e-06\n", + "time_save_step = 2.489e-09\n", + "time_step_size = 2.489e-10\n", "\n", "[Spatial mesh]\n", "grid_x_size = 10.0\n", @@ -171,9 +171,9 @@ "#vis = Visualizer3d()\n", "\n", "maxwell_check.time_grid = TimeGrid(\n", - " total = 1.155e-10,\n", - " step = 1.155e-13,\n", - " save_step = 1.155e-11\n", + " total = 2.489e-6,\n", + " step = 2.489e-10,\n", + " save_step = 2.489e-9\n", ")\n", "\n", "print( maxwell_check.export_to_string() )" @@ -188,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -196,17 +196,17 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 1.155e-10\n", - "time_save_step = 1.155e-11\n", - "time_step_size = 1.155e-13\n", + "total_time = 2.489e-06\n", + "time_save_step = 2.489e-09\n", + "time_step_size = 2.489e-10\n", "\n", "[Spatial mesh]\n", - "grid_x_size = 0.004\n", - "grid_x_step = 0.0001\n", - "grid_y_size = 0.004\n", - "grid_y_step = 0.0001\n", - "grid_z_size = 0.004\n", - "grid_z_step = 0.0001\n", + "grid_x_size = 0.04\n", + "grid_x_step = 0.001\n", + "grid_y_size = 0.04\n", + "grid_y_step = 0.001\n", + "grid_z_size = 0.04\n", + "grid_z_step = 0.001\n", "\n", "[Output filename]\n", "output_filename_prefix = out_\n", @@ -229,8 +229,8 @@ ], "source": [ "maxwell_check.spatial_mesh = SpatialMesh(\n", - " size = ( 0.004, 0.004, 0.004 ),\n", - " step = ( 0.0001, 0.0001, 0.0001 )\n", + " size = ( 0.04, 0.04, 0.04 ),\n", + " step = ( 0.001, 0.001, 0.001 )\n", ")\n", "\n", "print( maxwell_check.export_to_string() )" @@ -245,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -253,33 +253,33 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 1.155e-10\n", - "time_save_step = 1.155e-11\n", - "time_step_size = 1.155e-13\n", + "total_time = 2.489e-06\n", + "time_save_step = 2.489e-09\n", + "time_step_size = 2.489e-10\n", "\n", "[Spatial mesh]\n", - "grid_x_size = 0.004\n", - "grid_x_step = 0.0001\n", - "grid_y_size = 0.004\n", - "grid_y_step = 0.0001\n", - "grid_z_size = 0.004\n", - "grid_z_step = 0.0001\n", + "grid_x_size = 0.04\n", + "grid_x_step = 0.001\n", + "grid_y_size = 0.04\n", + "grid_y_step = 0.001\n", + "grid_z_size = 0.04\n", + "grid_z_step = 0.001\n", "\n", "[Particle_source_box.cathode_emitter]\n", "box_x_left = 0.0\n", - "box_x_right = 0.004\n", + "box_x_right = 0.04\n", "box_y_bottom = 0.0\n", - "box_y_top = 0.004\n", + "box_y_top = 0.04\n", "box_z_near = 0.0\n", - "box_z_far = 0.004\n", - "initial_number_of_particles = 1000\n", + "box_z_far = 0.04\n", + "initial_number_of_particles = 100\n", "particles_to_generate_each_step = 0\n", "mean_momentum_x = 0.0\n", "mean_momentum_y = 0.0\n", - "mean_momentum_z = 2.313e-18\n", + "mean_momentum_z = 2.313e-17\n", "temperature = 0.0\n", - "charge = 4.8e-10\n", - "mass = 1.67e-24\n", + "charge = 4.8e-08\n", + "mass = 1.67e-22\n", "\n", "[Output filename]\n", "output_filename_prefix = out_\n", @@ -304,14 +304,14 @@ "maxwell_check.sources = [ \n", " ParticleSource(\n", " name = \"cathode_emitter\",\n", - " initial_particles = 1000,\n", + " initial_particles = 100,\n", " particles_to_generate_each_step = 0,\n", " #shape = Box( origin = (0.1, 0.1, 0.1), size = ( 0.01, 0.01, 0.01 ) ),\n", - " shape = Box( origin = (0.004, 0.000, 0.000), size = ( -0.004, 0.004, 0.004 ) ), # hack left > right error\n", - " momentum = ( 0.00, 0.00, 2.313e-18 ),\n", + " shape = Box( origin = (0.04, 0.000, 0.000), size = ( -0.04, 0.04, 0.04 ) ), # hack left > right error\n", + " momentum = ( 0.00, 0.00, 2.313e-17 ),\n", " temperature = 0.0,\n", - " charge = 4.8e-10,\n", - " mass = 1.67e-24\n", + " charge = 4.8e-08,\n", + " mass = 1.67e-22\n", " )\n", "]\n", "\n", @@ -327,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 18, "metadata": { "scrolled": true }, @@ -337,33 +337,33 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 1.155e-10\n", - "time_save_step = 1.155e-11\n", - "time_step_size = 1.155e-13\n", + "total_time = 2.489e-06\n", + "time_save_step = 2.489e-09\n", + "time_step_size = 2.489e-10\n", "\n", "[Spatial mesh]\n", - "grid_x_size = 0.004\n", - "grid_x_step = 0.0001\n", - "grid_y_size = 0.004\n", - "grid_y_step = 0.0001\n", - "grid_z_size = 0.004\n", - "grid_z_step = 0.0001\n", + "grid_x_size = 0.04\n", + "grid_x_step = 0.001\n", + "grid_y_size = 0.04\n", + "grid_y_step = 0.001\n", + "grid_z_size = 0.04\n", + "grid_z_step = 0.001\n", "\n", "[Particle_source_box.cathode_emitter]\n", "box_x_left = 0.0\n", - "box_x_right = 0.004\n", + "box_x_right = 0.04\n", "box_y_bottom = 0.0\n", - "box_y_top = 0.004\n", + "box_y_top = 0.04\n", "box_z_near = 0.0\n", - "box_z_far = 0.004\n", - "initial_number_of_particles = 1000\n", + "box_z_far = 0.04\n", + "initial_number_of_particles = 100\n", "particles_to_generate_each_step = 0\n", "mean_momentum_x = 0.0\n", "mean_momentum_y = 0.0\n", - "mean_momentum_z = 2.313e-18\n", + "mean_momentum_z = 2.313e-17\n", "temperature = 0.0\n", - "charge = 4.8e-10\n", - "mass = 1.67e-24\n", + "charge = 4.8e-08\n", + "mass = 1.67e-22\n", "\n", "[Output filename]\n", "output_filename_prefix = out_\n", @@ -401,7 +401,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -409,33 +409,33 @@ "output_type": "stream", "text": [ "[Time grid]\n", - "total_time = 1.155e-10\n", - "time_save_step = 1.155e-11\n", - "time_step_size = 1.155e-13\n", + "total_time = 2.489e-06\n", + "time_save_step = 2.489e-09\n", + "time_step_size = 2.489e-10\n", "\n", "[Spatial mesh]\n", - "grid_x_size = 0.004\n", - "grid_x_step = 0.0001\n", - "grid_y_size = 0.004\n", - "grid_y_step = 0.0001\n", - "grid_z_size = 0.004\n", - "grid_z_step = 0.0001\n", + "grid_x_size = 0.04\n", + "grid_x_step = 0.001\n", + "grid_y_size = 0.04\n", + "grid_y_step = 0.001\n", + "grid_z_size = 0.04\n", + "grid_z_step = 0.001\n", "\n", "[Particle_source_box.cathode_emitter]\n", "box_x_left = 0.0\n", - "box_x_right = 0.004\n", + "box_x_right = 0.04\n", "box_y_bottom = 0.0\n", - "box_y_top = 0.004\n", + "box_y_top = 0.04\n", "box_z_near = 0.0\n", - "box_z_far = 0.004\n", - "initial_number_of_particles = 1000\n", + "box_z_far = 0.04\n", + "initial_number_of_particles = 100\n", "particles_to_generate_each_step = 0\n", "mean_momentum_x = 0.0\n", "mean_momentum_y = 0.0\n", - "mean_momentum_z = 2.313e-18\n", + "mean_momentum_z = 2.313e-17\n", "temperature = 0.0\n", - "charge = 4.8e-10\n", - "mass = 1.67e-24\n", + "charge = 4.8e-08\n", + "mass = 1.67e-22\n", "\n", "[Output filename]\n", "output_filename_prefix = maxwell_check_\n", @@ -475,14 +475,14 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "command: python3 ../../main.py /tmp/tmp2jvwoisd.ini\n" + "command: python3 ../../main.py /tmp/tmpg_yvsfx7.ini\n" ] }, { @@ -492,7 +492,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mrunner\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mEfRunner\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mconf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmaxwell_check\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mef_command\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"python3 ../../main.py\"\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mrunner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mrunner\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mEfRunner\u001b[0m\u001b[0;34m(\u001b[0m \u001b[0mconf\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmaxwell_check\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mef_command\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"python3 ../../main.py\"\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0mrunner\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m~/PycharmProjects/ef_python-maxwell-check/ef/util/runner.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, workdir, save_config_as)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mconfig_fname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msave_config_as\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0mconfig_file\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_fname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'w'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_configure_and_run\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mworkdir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig_fname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconfig_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msave_config_as\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0mos\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_fname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/PycharmProjects/ef_python-maxwell-check/ef/util/runner.py\u001b[0m in \u001b[0;36m_configure_and_run\u001b[0;34m(self, workdir, config_fname, config_file)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexport_to_file\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mconfig_file\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_from_file\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig_fname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mworkdir\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mworkdir\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"./\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msave_config_as\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m~/PycharmProjects/ef_python-maxwell-check/ef/util/runner.py\u001b[0m in \u001b[0;36mrun_from_file\u001b[0;34m(self, startfile, workdir)\u001b[0m\n\u001b[1;32m 36\u001b[0m \u001b[0mprocess\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mshlex\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcommand\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstdout\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msubprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPIPE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 38\u001b[0;31m \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstdout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 39\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0moutput\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m''\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mprocess\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpoll\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", @@ -517,7 +517,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -554,22 +554,22 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "p_sim_av = 2.313e-18 [cm/s]\n", - "p_theor_average = 2.131e-18 [cm/s]\n" + "p_sim_av = 2.248e-17 [cm/s]\n", + "p_theor_average = 2.131e-17 [cm/s]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -588,9 +588,9 @@ " filename = 'maxwell_check_0010000.h5'\n", " h5 = h5py.File(filename, mode=\"r\")\n", " \n", - " m = 1.67e-24\n", + " m = 1.67e-22\n", " T = 7.740e+03\n", - " N = 800\n", + " N = 100\n", " k_B = 1.38e-16\n", " distr, p_grid, p = analyt_maxwell_distrib(N, T, m, k_B, h5) #(*1)\n", " p_sim_av = np.mean(p)\n", @@ -607,7 +607,7 @@ " plt.title('Probability density function (PDF)')\n", " plt.ylabel(r'$\\rho$ probability density function')\n", " plt.plot(p_grid,distr,label = 'Theor PDF')\n", - " plt.hist(p,20, normed=1)\n", + " plt.hist(p,15, normed=True)\n", " plt.xlim(0.0, p.max())\n", " plt.legend()\n", " plt.grid(True)\n",