Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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