def test_1D_particle_source_region(self): """Set up a one-dimensional source region and display it. """ fncName = '(' + __file__ + ') ' + sys._getframe( ).f_code.co_name + '():\n' print('\ntest: ', fncName, '(' + __file__ + ')') ## Set control variables ctrl = DTcontrol_C() ctrl.title = "test_ParticleGeneration.py:test_1D_particle_source_region" ctrl.author = "tph" ctrl.timeloop_count = 0 ctrl.time = 0.0 ctrl.dt = 1.0e-6 ctrl.n_timesteps = 1 ctrl.write_trajectory_files = False pin = self.pin pin.coordinate_system = 'cartesian_x' pin.force_components = [ 'x', ] ### Particle input ## Define electron species charge = -1.0 * MyPlasmaUnits_C.elem_charge mass = 1.0 * MyPlasmaUnits_C.electron_mass dynamics = 'explicit' electrons_S = ParticleSpecies_C('electrons', charge, mass, dynamics) # Add the electrons to particle input pin.particle_species = (electrons_S, ) ## Make the particle storage array for all species. particle_P = Particle_C(pin, print_flag=True) ## Give the name of the .py file containing special particle data (lists of # particles, boundary conditions, source regions, etc.) userParticlesModuleName = "UserParticles_1D" # Import this module userParticlesModule = im_m.import_module(userParticlesModuleName) # particle_P.user_particles_module_name = userParticlesModuleName particle_P.user_particles_class = userParticlesClass = userParticlesModule.UserParticleDistributions_C ### Mesh input for a 1D mesh # Specify the mesh parameters umi1D = UserMeshInput_C() umi1D.pmin = df_m.Point(-10.0) umi1D.pmax = df_m.Point(10.0) umi1D.cells_on_side = (20, ) # Need the comma to indicate a tuple ### Create the 1D particle mesh and add to the Particle_C object pmesh1D = UserMesh_C(umi1D, compute_dictionaries=True, compute_cpp_arrays=False, compute_tree=True, plot_flag=False) particle_P.pmesh_M = pmesh1D ### Input for particle sources # For each source: # a. Define the source region # b. Provide a particle-generating function # c. Provide the species name and physical source parameters # Create numpy storage needed below driftVelocity = np_m.empty(particle_P.particle_dimension, dtype=particle_P.precision) ## 1. Hot electron source on left side of the mesh # a. Provide the species name and physical parameters # for this source speciesName = 'electrons' # Check that this species has been defined if speciesName in particle_P.species_names: charge = particle_P.charge[speciesName] mass = particle_P.mass[speciesName] else: print("The species", speciesName, "has not been defined") sys.exit() sourceDistributionType = 'functional' # Compute a physical number-density creation rate for this source region # The value should always be positive chargeDensityRate = -1.0 # The timestep interval between calls to the creation function timeStepInterval = 1 # Get number-density added per invocation numberDensity = chargeDensityRate * timeStepInterval * ctrl.dt / charge # Check for positivity if numberDensity < 0: print("Check number density for species", speciesName, "is negative. Should be positive") sys.exit() # Compute a value for thermalSpeed from the temperature in eV temperature_eV = 2.0 temp_joule = temperature_eV * MyPlasmaUnits_C.elem_charge thermalSpeed = np_m.sqrt(2.0 * temp_joule / mass) velocity_coordinate_system = 'x_y_z' # This sets the interpretation of the # following values # Set a drift velocity vdrift_1 = 1.0 vdrift_2 = 2.0 vdrift_3 = 3.0 # driftVelocity = (vdrift_1, vdrift_2, vdrift_3) driftVelocity[0] = vdrift_1 # The particle storage arrays have only a v[0] velocity component # driftVelocity[1] = vdrift_2 # driftVelocity[2] = vdrift_3 # Set desired particles-per-cell numberPerCell = 1 # Collect the parameters for this source in a dictionary hotElectronParams = { 'species_name': speciesName, 'source_distribution_type': sourceDistributionType, 'number_density': numberDensity, 'thermal_speed': thermalSpeed, 'velocity_coordinate_system': velocity_coordinate_system, 'drift_velocity': driftVelocity, 'timestep_interval': timeStepInterval, 'number_per_cell': numberPerCell } # b. Name the particle-creation function. maxwellianGenerator = particle_P.create_maxwellian_particles # c. Source-region geometry xmin = -9.0 xmax = -4.0 hotElectronsRegion = RectangularRegion_C(pmesh1D, xmin, xmax) ## 2. Background electrons over the whole mesh ## Specify one or more species to be generated in the 2nd region. # a. Provide the species name and physical parameters for this source # Note that this is the same species as in ## 1. above. It's not necessary # for each source region to have a different species name. speciesName = 'electrons' # Check that this species has been defined above if speciesName in particle_P.species_names: charge = particle_P.charge[speciesName] mass = particle_P.mass[speciesName] else: print("The species", speciesName, "has not been defined") sys.exit() sourceDistributionType = 'functional' # Compute a value for numberDensity, the physical number-density # creation rate for this source region. # The value should always be positive. chargeDensityRate = -0.1 # The timestep interval between calls to the creation function timeStepInterval = 1 # Get charge-density per invocation numberDensity = chargeDensityRate * timeStepInterval * ctrl.dt / charge # Check for positivity if numberDensity < 0: print("Check number density for species", speciesName, "is negative. Should be positive") sys.exit() # Compute a value for thermalSpeed temperature_eV = 2.0 temp_joule = temperature_eV * MyPlasmaUnits_C.elem_charge thermalSpeed = np_m.sqrt(2.0 * temp_joule / mass) velocity_coordinate_system = 'x_y_z' # This sets the interpretation of the # following values # Set a drift velocity vdrift_1 = 0.5 vdrift_2 = 1.5 vdrift_3 = 2.5 # driftVelocity = (vdrift_1, vdrift_2, vdrift_3) # The particle storage arrays have only a v[0] velocity component driftVelocity[0] = vdrift_1 # Set desired particles-per-cell numberPerCell = 10 # Collect the parameters for this source in a dictionary backgroundElectronParams = { 'species_name': speciesName, 'source_distribution_type': sourceDistributionType, 'number_density': numberDensity, 'thermal_speed': thermalSpeed, 'velocity_coordinate_system': velocity_coordinate_system, 'drift_velocity': driftVelocity, 'timestep_interval': timeStepInterval, 'number_per_cell': numberPerCell } # b. Name the first function that will generate particles in the above region: # Just use same maxwellianGenerator function as above. # c. Source geometry: in this case, it's the whole mesh: wholeMesh = WholeMesh_C(pmesh1D) ## Put all the particle source data into a dictionary # The dictionary keys are mnemonic source names. # The dictionary values are lists of tuples giving the # a. the physical source parameters. # b. particle creation function and # c. source region ## Note: the names here are source-region names, NOT species names! particleSourceDict = { 'hot_electron_source': (hotElectronParams, maxwellianGenerator, hotElectronsRegion), 'background_electron_source': (backgroundElectronParams, maxwellianGenerator, wholeMesh), } particle_P.particle_source_dict = particleSourceDict ### Create input for a particle trajectory object # Use an input object to collect initialization data for the trajectory object self.trajin = TrajectoryInput_C() self.trajin.maxpoints = None # Set to None to get every point self.trajin.extra_points = 1 # Set to 1 to make sure one boundary-crossing can be # accommodated. Set to a larger value if there are # multiple boundary reflections. self.trajin.explicit_dict = { 'names': [ 'x', 'ux', ], 'formats': [np_m.float32] * 2 } ## Create the trajectory object and attach it to the particle object. # No trajectory storage is created until particles # with TRAJECTORY_FLAG on are encountered. p_P = particle_P traj_T = Trajectory_C(self.trajin, ctrl, p_P.charged_species, p_P.neutral_species, p_P.species_index, p_P.mass, p_P.charge) particle_P.traj_T = traj_T ## Invoke the source functions and write out the particles ### Select output for particles ctrl.particle_output_file = "particleGeneration.h5part" ctrl.particle_output_interval = 1 ctrl.particle_output_attributes = ('species_index', 'x', 'ux', 'weight') # Check these values particle_P.check_particle_output_parameters(ctrl) particle_P.add_more_particles(ctrl) # Dump the particle data to a file particle_P.initialize_particle_output_file(ctrl) # Write the particle attributes particle_P.write_particles_to_file(ctrl) return
def test_3_function_initialized_particles(self): """ Test initialization of particles using a function over a region of the mesh. """ fncName = '(' + __file__ + ') ' + sys._getframe( ).f_code.co_name + '():\n' print('\ntest: ', fncName, '(' + __file__ + ')') ## Control struct ctrl = DTcontrol_C() ## Mesh input mi2d_UMI = UserMeshInput_C() # Replace with TUPLES since we're at toplevel mi2d_UMI.pmin = df_m.Point(-10.0, -10.0) mi2d_UMI.pmax = df_m.Point(10.0, 10.0) mi2d_UMI.cells_on_side = (2, 2) mi2d_UMI.diagonal = 'left' ## Create a 2D particle mesh, since we're going to initialize particles ## on a region of a mesh. # UserMesh_y_Fields_FE_XYZ_Module can make the mesh from the above input. plotFlag = False plotTitle = os.path.basename( __file__) + ": " + sys._getframe().f_code.co_name + ": mesh" pmesh2d_UM = UserMesh_C(mi2d_UMI, compute_dictionaries=True, compute_cpp_arrays=False, compute_tree=True, plot_flag=plotFlag, plot_title=plotTitle) p_P = self.particle_P userParticlesClass = p_P.user_particles_class ## 1. Initial hot electrons on bottom-left side of the mesh # a. Provide the species name and physical parameters # for this particle-initialization speciesName = 'plasma_electrons' # Check that this species has been defined if speciesName in p_P.species_names: charge = p_P.charge[speciesName] mass = p_P.mass[speciesName] else: print("The species", speciesName, "needs to be defined.") sys.exit() initialDistributionType = 'function_over_region' # Specify the initial physical number-density for this species numberDensity = 1.0e12 # Check for positivity if numberDensity <= 0: print("Check number density for species", speciesName, "is zero or negative. Should be positive") sys.exit() # Compute a value for thermalSpeed from the temperature in eV temperature_eV = 2.0 temp_joule = temperature_eV * MyPlasmaUnits_C.elem_charge thermalSpeed = np_m.sqrt(2.0 * temp_joule / mass) # Set a drift velocity # Set a drift velocity vdrift_1 = 1.0 vdrift_2 = 2.0 vdrift_3 = 3.0 velocity_coordinate_system = 'x_y_z' # This sets the interpretation of the # following values driftVelocity = (vdrift_1, vdrift_2, vdrift_3) # driftVelocity[:] = (1.0,) # Set desired particles-per-cell numberPerCell = 1000 # Collect the parameters for this initialization in a dictionary hotElectronParams = { 'species_name': speciesName, 'initial_distribution_type': initialDistributionType, 'number_density': numberDensity, 'thermal_speed': thermalSpeed, 'velocity_coordinate_system': velocity_coordinate_system, 'drift_velocity': driftVelocity, 'number_per_cell': numberPerCell } # b. Name the particle-creation function. maxwellianGenerator = p_P.create_maxwellian_particles # c. Initialization-region geometry xmin = -9.0 ymin = -9.0 xmax = -4.0 ymax = -4.0 # Don't use df_m in the toplevel routine, if possible # pmin = df_m.Point(xmin, ymin) # pmax = df_m.Point(xmax, ymax) pmin = (xmin, ymin) pmax = (xmax, ymax) hotElectronsRegion_RR = RectangularRegion_C(pmesh2d_UM, pmin, pmax) ## Put all the particle-initialization data into a dictionary # The dictionary keys are mnemonics for particle-initialization names. # The dictionary values are lists of tuples giving the # a. the physical initialization parameters. # b. particle creation function and # c. initialization region ## Note: the names here are particle-initialization names, NOT species names! initialParticlesDict = { 'initial_hot_electrons': (hotElectronParams, maxwellianGenerator, hotElectronsRegion_RR), # 'initial_background_electrons': (backgroundElectronParams, maxwellianGenerator, wholeMesh), } # Replace the 'listed' initial particles made in Setup() with # the 'function_initialized' initial particles created above p_P.initial_particles_dict = initialParticlesDict # Loop on the initialization methods in the dictionary # For 'listed' initialization, the dictionary has entries like # {ipName: (ipParams,)} # For 'function_over_region' initialization, the dictionary has entries # like # {ipName: (ipParams, ipFunc, ipRegion_RR)} nCreatedTotal = 0 for ipName, ipTuple in initialParticlesDict.items(): ipParams = ipTuple[0] s = ipParams['species_name'] initialDistributionType = ipParams['initial_distribution_type'] if initialDistributionType == 'function_over_region': print(fncName, "Initializating", ipName, "particles") ipFunc = ipTuple[1] ipRegion_RR = ipTuple[2] # Invoke the creation function step = 0 time = 0.0 neg_E_field = None ipFunc(step, time, ipRegion_RR, ipParams, neg_E_field) # Check the number of stored particles for each species nCreated = ipRegion_RR.ncell * ipParams['number_per_cell'] print(fncName, "Number of particles created =", nCreated) nCreatedTotal += nCreated nStored = p_P.get_species_particle_count(s, print_flag=False) self.assertEqual( nStored, nCreated, msg="Number of stored particles is not correct") # check total number of stored particles nStored = p_P.get_total_particle_count(print_flag=False) print(fncName, "Total number of particles created =", nCreatedTotal) self.assertEqual(nStored, nCreatedTotal, msg="Total number of stored particles is not correct") # Write out the particles to an HDF5 file ctrl.timeloop_count = 0 ctrl.time = 0.0 # Run identifier ctrl.title = "test_ParticleInitialization" # Run author ctrl.author = "tph" ### Select output for particles ctrl.particle_output_file = "particleInitialization.h5part" ctrl.particle_output_interval = 1 ctrl.particle_output_attributes = ('species_index', 'x', 'y', 'z', 'ux', 'uy', 'uz', 'weight') # Check these values p_P.check_particle_output_parameters(ctrl) # Dump the particle data to a file p_P.initialize_particle_output_file(ctrl) # Write the particle attributes p_P.write_particles_to_file(ctrl) return