def test_create_checkpoints_with_fluxdiag():

    testing_util.initialize_testingdir("test_create_checkpoints_with_fluxdiag")

    # use a fixed random seed
    np.random.seed(47239475)

    run = get_run()

    run.init_injectors()

    checkpoint = CheckPointDiagnostic(DIAG_STEPS,
                                      CHECKPOINT_NAME,
                                      clear_old_checkpoints=True)

    run.init_runinfo()
    run.init_fluxdiag()
    mwxrun.init_run(restart=False)

    checkpoint.flux_diag = run.fluxdiag

    # Run the main WARP loop
    mwxrun.simulation.step(MAX_STEPS)

    checkpoint_names = [
        f"{CHECKPOINT_NAME}{i:05}"
        for i in range(DIAG_STEPS, MAX_STEPS + 1, DIAG_STEPS)
    ]

    for name in checkpoint_names:
        print(f"Looking for checkpoint file 'diags/{name}'...")
        assert os.path.isdir(os.path.join("diags", name))
    assert os.path.isfile("diags/checkpoint00004/fluxdata.ckpt")
def test_restart_from_checkpoint(caplog, force, files_exist):

    caplog.set_level(logging.WARNING)
    testing_util.initialize_testingdir(
        f"test_restart_from_checkpoint_{force}_{files_exist}")

    run = get_run()

    if force:
        restart = True
    else:
        restart = None

    if not files_exist:
        prefix = "nonexistent_prefix"
    else:
        prefix = "checkpoint"

    try:
        mwxrun.init_run(restart=restart,
                        checkpoint_dir=os.path.join(testing_util.test_dir,
                                                    "checkpoint"),
                        checkpoint_prefix=prefix)
    except RuntimeError as e:
        if ("There were no checkpoint directories starting with "
                "nonexistent_prefix!" in str(e)):
            # There should only be an exception if this restart was forced
            assert force

    # if the files didn't exist then we didn't restart,
    # so there's no need to verify the correct number of steps passed
    if files_exist:
        new_max_steps = mwxrun.simulation.max_steps

        start_step = mwxrun.get_it()

        if not force and not files_exist:
            log_lines = [r.msg for r in caplog.records]
            assert any([
                f"There were no checkpoint directories starting with {prefix}!"
                in l for l in log_lines
            ])

        mwxrun.simulation.step()

        end_step = mwxrun.get_it()

        assert end_step - start_step == new_max_steps
def test_checkpoints_fluxdiag():

    testing_util.initialize_testingdir("test_checkpoints_fluxdiag")

    # use a fixed random seed
    np.random.seed(47239475)

    run = get_run()

    run.init_injectors()

    run.init_runinfo()
    run.init_fluxdiag()

    mwxrun.init_run(
        restart=True,
        checkpoint_dir=os.path.join(testing_util.test_dir, "checkpoint"),
    )

    # Run the main WarpX loop
    mwxrun.simulation.step()

    # load flux diagnostic from a completed run for comparison
    basedir = os.path.join(testing_util.test_dir, "checkpoint")
    original_flux_file = os.path.join(
        testing_util.test_dir, "checkpoint/fluxes/fluxdata_0000000008.dpkl")
    original_flux = FluxDiagFromFile(basedir, original_flux_file)

    # compare injected flux from the restarted history with the original history
    flux_key = ('inject', 'cathode', 'electrons')
    for key in ['J', 'P', 'n']:
        # if key == 'J':
        #     continue
        old = original_flux.fullhist_dict[flux_key].get_timeseries_by_key(key)
        new = run.fluxdiag.fullhist_dict[flux_key].get_timeseries_by_key(key)
        print("~~~~~~~~~~~~~~~~~~~~~~~~~~~")
        print(f"The key is {key}")
        print(f"old: \n {old}")
        print(f"new: \n {new}")
        assert np.allclose(old, new)
def test_extra_steps_after_restart():

    testing_util.initialize_testingdir("test_extra_steps_after_restart")

    # use a fixed random seed
    np.random.seed(47239475)

    run = get_run()

    additional_steps = 8

    # restart from checkpoint created by test_create_checkpoints
    mwxrun.init_run(restart=True,
                    checkpoint_dir=os.path.join(testing_util.test_dir,
                                                "checkpoint"),
                    additional_steps=additional_steps)

    start_step = mwxrun.get_it()

    mwxrun.simulation.step()

    end_step = mwxrun.get_it()

    assert start_step + additional_steps == end_step

    restart_net_charge_density = np.load(
        os.path.join(run.field_diag.write_dir,
                     "Net_charge_density_0000000008.npy"))
    # compare against data from test_create_checkpoints
    original_net_charge_density = np.load(
        os.path.join(testing_util.test_dir, "checkpoint",
                     "Net_charge_density_0000000008.npy"))

    assert np.allclose(restart_net_charge_density,
                       original_net_charge_density,
                       rtol=0.1)
    def setup_run(self, n=0, steps=None):
        """Setup run for the specific case (n) desired."""

        self.steps = steps

        # Case specific input parameters
        self.ANODE_VOLTAGE = f"{self.VOLTAGE[n]}*sin(2*pi*{self.FREQ:.5e}*t)"

        self.N_INERT = self.N_INERT[n]
        self.PLASMA_DENSITY = self.PLASMA_DENSITY[n]
        self.SEED_NPPC = self.SEED_NPPC[n]

        self.NZ = self.NZ[n]

        self.DT = self.DT[n]
        self.MAX_STEPS = int(self.TOTAL_TIME[n] / self.DT)
        self.DIAG_STEPS = int(self.DIAG_INTERVAL / self.DT)

        if self.steps is not None:
            self.MAX_STEPS = self.steps
            self.DIAG_STEPS = self.MAX_STEPS // 5

        #######################################################################
        # Set geometry, boundary conditions and timestep                      #
        #######################################################################

        mwxrun.init_grid(
            lower_bound=[-self.D_CA / self.NZ * self.NX / 2.0, 0],
            upper_bound=[self.D_CA / self.NZ * self.NX / 2.0, self.D_CA],
            number_of_cells=[self.NX, self.NZ],
            min_tiles=16)
        mwxrun.grid.potential_zmax = self.ANODE_VOLTAGE
        mwxrun.init_timestep(DT=self.DT)
        mwxrun.simulation.max_steps = self.MAX_STEPS
        mwxrun.simulation.load_balance_intervals = self.MAX_STEPS // 5000

        #######################################################################
        # Field solver                                                        #
        #######################################################################

        # self.solver = picmi.ElectrostaticSolver(
        #    grid=mwxrun.grid, method='Multigrid', required_precision=1e-12
        # )
        self.solver = poisson_solvers.PoissonSolverPseudo1D(grid=mwxrun.grid)
        mwxrun.simulation.solver = self.solver

        #######################################################################
        # Particle types setup                                                #
        #######################################################################

        self.electrons = mespecies.Species(particle_type='electron',
                                           name='electrons')
        self.ions = mespecies.Species(particle_type='He',
                                      name='he_ions',
                                      charge='q_e')

        #######################################################################
        # Collision  initialization                                           #
        #######################################################################

        self.mcc = mcc_wrapper.MCC(self.electrons,
                                   self.ions,
                                   T_INERT=self.T_INERT,
                                   N_INERT=self.N_INERT,
                                   exclude_collisions=['charge_exchange'])

        #######################################################################
        # Neutral plasma injection                                            #
        #######################################################################

        self.vol_emitter = emission.UniformDistributionVolumeEmitter(
            T=self.T_ELEC)

        self.plasma_injector = emission.PlasmaInjector(
            emitter=self.vol_emitter,
            species1=self.electrons,
            species2=self.ions,
            npart=2 * self.SEED_NPPC * self.NX * self.NZ,
            T_2=self.T_INERT,
            plasma_density=self.PLASMA_DENSITY)

        #######################################################################
        # Add diagnostics                                                     #
        #######################################################################

        self.text_diag = diag_base.TextDiag(diag_steps=self.DIAG_STEPS,
                                            preset_string='perfdebug')

        self.field_diag = FieldDiagnostic(diag_steps=self.DIAG_STEPS,
                                          barrier_slices=[0],
                                          save_pdf=False,
                                          style='roelof',
                                          min_dim=2.0)

        # array to save ion density
        self.rho_array = np.zeros(self.NZ + 1)

        #######################################################################
        # Initialize run and print diagnostic info                            #
        #######################################################################

        mwxrun.init_run()
def test_utils_pulsing_sim():
    name = "pulsing_utility_test"
    # Include a random run number to allow parallel runs to not collide. Using
    # python randint prevents collisions due to numpy rseed below
    testing_util.initialize_testingdir(name)

    # Initialize each run with consistent, randomly-chosen, rseed. Use a random
    # seed instead for initial dataframe generation.
    # np.random.seed()
    np.random.seed(9216024)

    nx = 8
    nz = 128
    D_CA = 1e-4 # m

    xmin = 0.0
    zmin = 0.0

    xmax = D_CA / nz * nx
    zmax = D_CA

    max_steps = 50

    DT = 1e-10

    #####################################
    # grid, solver and timesteps
    #####################################

    mwxrun.init_grid([xmin, zmin], [xmax, zmax], [nx, nz])

    solver = PoissonSolverPseudo1D(grid=mwxrun.grid)

    mwxrun.simulation.solver = solver
    mwxrun.init_timestep(DT=DT)
    mwxrun.simulation.max_steps = max_steps

    pulse_expr = pulsing.linear_pulse_function(
        V_off=-1.0, V_on=7.5, pulse_period=20e-9,
        pulse_length=2e-9, t_rise=1e-9, t_fall=1e-9,
        wait_time=0.5e-9, plot=True
    )
    anode = assemblies.Anode(D_CA, pulse_expr, 100, 3.0)

    electrons = mespecies.Species(
        particle_type='electron',
        name='electrons',
    )

    # mwxrun.simulation.write_input_file()
    # exit()

    #################################
    # simulation setup
    ################################

    mwxrun.init_run()

    # check that plot was saved successfully
    assert os.path.isfile('diags/circuit/pulse_schematic.png')

    ################################
    # Simulation run
    ################################

    times = np.arange(max_steps)*DT
    Vs = np.zeros(max_steps)

    # Run the main loop
    for ii in range(max_steps):
        Vs[ii] = anode.getvoltage()
        mwxrun.simulation.step(1)

    print(repr(Vs))
    ref_Vs = np.array(
        [-1.  , -1.  , -1.  , -1.  , -1.  , -1.  , -1.  , -0.15,  0.7 ,
        1.55,  2.4 ,  3.25,  4.1 ,  4.95,  5.8 ,  6.65,  7.5 ,  7.5 ,
        7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,
        7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,  7.5 ,
        7.5 ,  6.65,  5.8 ,  4.95,  4.1 ,  3.25,  2.4 ,  1.55,  0.7 ,
       -0.15, -1.  , -1.  , -1.  , -1.  ]
    )
    assert np.allclose(Vs, ref_Vs, atol=0)
Exemple #7
0
def test_coulomb_scattering():

    name = "coulomb_scattering_langevin"
    # Include a random run number to allow parallel runs to not collide. Using
    # python randint prevents collisions due to numpy rseed below
    testing_util.initialize_testingdir(name)

    # Initialize each run with consistent, randomly-chosen, rseed. Use a random
    # seed instead for initial dataframe generation.
    # np.random.seed()
    np.random.seed(92160000)


    class BeamEmitter(emission.UniformDistributionVolumeEmitter):

        """Child class of UniformDistributionVolumeEmitter used to overwrite the
        get_velocities function when sampling velocities for the electrons since
        we want to inject them with a delta function."""

        def __init__(self, T, v_beam):

            self.v_beam = v_beam

            super(BeamEmitter, self).__init__(T=T)

        def _get_xv_coords(self, npart, m, rseed):
            """Get velocities and call specialized function for position."""
            x_coords = self._get_x_coords(npart)

            if m == 1:
                # ion velocities can be Maxwellian
                v_coords = mwxutil.get_velocities(
                    npart, self.T, m=m, emission_type='random', rseed=None)
            else:
                # electron velocities should be a delta function, we need to
                # have small non-zero values for x and y otherwise the Coulomb
                # scattering algorithm fails due to divide by zero errors
                v_coords = np.zeros((3, npart))
                v_coords[0] = np.random.normal(0.0, 1.0, npart)
                v_coords[1] = np.random.normal(0.0, 1.0, npart)
                v_coords[2] = self.v_beam

            return (
                x_coords[:, 0], x_coords[:, 1], x_coords[:, 2],
                v_coords[0], v_coords[1], v_coords[2]
            )


    class VarianceTracker(diags.WarpXDiagnostic):

        def __init__(self, total_steps, diag_steps):
            """Diagnostic to record the electron velocity variance at every
            diagnostic step."""
            self.i = 0
            self.results_array = np.zeros((total_steps//diag_steps, 2))

            super(VarianceTracker, self).__init__(diag_steps)

            callbacks.installafterstep(self._record_variance)

        def _record_variance(self):

            if not self.check_timestep():
                return

            ux = np.concatenate(mwxrun.sim_ext.get_particle_ux('electrons'))

            self.results_array[self.i, 0] = mwxrun.get_t()
            self.results_array[self.i, 1] = np.std(ux)
            self.i += 1


    #######################################################################
    # Simulation parameters                                               #
    #######################################################################

    ZMAX = 250e-6
    NZ = 256
    NX = 8

    DT = 1e-13

    MAX_STEPS = 150
    DIAG_STEPS = 15

    SEED_DENSITY = 5e17
    SEED_COUNT = 20000
    LOGLAMBDA = 7.5

    VBEAM = 1e6

    # Derived quantities

    PERIOD = ZMAX / NZ * NX

    #######################################################################
    # Set geometry, boundary conditions and timestep                      #
    #######################################################################

    mwxrun.init_grid(
        lower_bound=[-PERIOD/2.0, 0.], upper_bound=[PERIOD/2.0, ZMAX],
        number_of_cells=[NX, NZ],
        bc_fields_z_min='periodic',
        bc_fields_z_max='periodic',
        bc_particles_z_min='periodic',
        bc_particles_z_max='periodic'
    )
    mwxrun.init_timestep(DT=DT)
    mwxrun.simulation.max_steps = MAX_STEPS

    #######################################################################
    # Dummy Poisson solver to effectively turn off the field solve        #
    #######################################################################

    solver = DummyPoissonSolver(grid=mwxrun.grid)
    mwxrun.simulation.solver = solver

    #######################################################################
    # Particle types setup                                                #
    #######################################################################

    electrons = mespecies.Species(
        particle_type='electron', name='electrons'
    )
    # artificially increase ion mass to match Lorentz gas better
    ions = mespecies.Species(
        particle_type='Xe', name='xe_ions', charge='q_e', mass=1.0
    )

    #######################################################################
    # Seed simulation with quasi-neutral plasma                           #
    #######################################################################

    volume_emitter = BeamEmitter(1.0, VBEAM)
    emission.PlasmaInjector(
        volume_emitter, electrons, ions, npart=SEED_COUNT*2,
        plasma_density=SEED_DENSITY
    )

    #######################################################################
    # Coulomb scattering                                                  #
    #######################################################################

    langevin = coulomb_scattering.LangevinElectronIonScattering(
        electron_species=electrons, ion_species=ions,
        log_lambda=LOGLAMBDA, subcycling_steps=1
    )

    #######################################################################
    # Diagnostics                                                         #
    #######################################################################

    diags.TextDiag(MAX_STEPS // 5, preset_string='perfdebug')

    variance_tracker = VarianceTracker(MAX_STEPS, DIAG_STEPS)

    #######################################################################
    # Run simulation                                                      #
    #######################################################################

    mwxrun.init_run()
    mwxrun.simulation.step()

    #######################################################################
    # Compare results to theory                                           #
    #######################################################################

    D = (
        SEED_DENSITY * mwxconstants.e**4 * LOGLAMBDA
        / (4 * np.pi * mwxconstants.epsilon_0**2 * mwxconstants.m_e**2 * VBEAM)
    )
    times = variance_tracker.results_array[:, 0]
    calculated_values = variance_tracker.results_array[:, 1] / VBEAM
    expected_values = np.sqrt(D*times) / VBEAM

    print("Calculated ratio: ", calculated_values )
    print("Expected ratio: ", expected_values)
    assert np.allclose(calculated_values, expected_values, rtol=0.01)
    '''
Exemple #8
0
 def init_warpx(self):
     logger.info("### Init Simulation Run ###")
     mwxrun.init_run()
Exemple #9
0
    def setup_run(self):

        self.rmax = 1e-3
        self.rmin = self.rmax - self.D_CA - self.USE_EB * 10e-6
        self.zmax = (self.rmax - self.rmin) / self.NR * self.NZ / 2.0
        self.zmin = -self.zmax

        self.V_ANODE = self.V_CATHODE + self.V_ANODE_CATHODE

        if self.DIAG_STEPS is None:
            self.DIAG_STEPS = ((self.TOTAL_TIMESTEPS //
                                5) if self.TOTAL_TIMESTEPS > 10 else
                               self.TOTAL_TIMESTEPS)

        #######################################################################
        # Set geometry, boundary conditions and timestep                      #
        #######################################################################

        mwxrun.init_grid(
            lower_bound=[self.rmin, self.zmin],
            upper_bound=[self.rmax, self.zmax],
            number_of_cells=[self.NR, self.NZ],  # min_tiles=1,
            use_rz=True,
            bc_fields_z_min='periodic',
            bc_fields_z_max='periodic',
            bc_particles_z_min='periodic',
            bc_particles_z_max='periodic',
            bc_fields_r_min='dirichlet',
            bc_fields_r_max='dirichlet',
        )
        mwxrun.init_timestep(DT=self.DT)
        mwxrun.simulation.max_steps = self.TOTAL_TIMESTEPS
        mwxrun.simulation.load_balance_intervals = self.TOTAL_TIMESTEPS // 5000

        #######################################################################
        # Field solver                                                        #
        #######################################################################

        self.solver = picmi.ElectrostaticSolver(grid=mwxrun.grid,
                                                method='Multigrid',
                                                required_precision=1e-6,
                                                warpx_self_fields_verbosity=0,
                                                maximum_iterations=1000)
        mwxrun.simulation.solver = self.solver

        #######################################################################
        # Conductors setup and installation                                   #
        #######################################################################

        self.cathode = assemblies.CylinderZ(V=self.V_CATHODE,
                                            T=self.CATHODE_TEMP,
                                            WF=self.CATHODE_PHI,
                                            name='cathode',
                                            r_outer=self.rmax + 1e-6,
                                            r_inner=self.rmax)
        self.anode = assemblies.CylinderZ(
            V=self.V_ANODE,
            T=self.ANODE_TEMP,
            WF=self.ANODE_PHI,
            name='anode',
            r_outer=(self.rmin if not self.USE_EB else self.rmax - self.D_CA))

        #######################################################################
        # Particle types setup                                                #
        #######################################################################

        self.electrons = mespecies.Species(particle_type='electron',
                                           name='electrons')

        #######################################################################
        # Scraper setup                                                       #
        #######################################################################

        for species in [self.electrons]:
            species.save_particles_at_xhi = 1
            if self.USE_EB:
                species.save_particles_at_eb = 1
            else:
                species.save_particles_at_xlo = 1

        #######################################################################
        # Thermionic emission                                                 #
        #######################################################################

        self.cathode_emitter = emission.ZCylinderEmitter(
            conductor=self.cathode, rdir=-1)
        self.cathode_injector = emission.ThermionicInjector(
            emitter=self.cathode_emitter,
            species=self.electrons,
            A=self.CATHODE_A,
            npart_per_cellstep=self.NPPC,
            use_Schottky=self.USE_SCHOTTKY)

        #######################################################################
        # Clean up and output RunInfo                                         #
        #######################################################################

        runvars = CylinderVacuumTEC.__dict__.copy()
        runvars.update(self.__dict__)

        self.runinfo = runinfo.RunInfo(
            local_vars=runvars,
            electrode_params={
                "CATHODE_A": self.CATHODE_A,
                "CATHODE_TEMP": self.CATHODE_TEMP,
                "CATHODE_PHI": self.CATHODE_PHI,
                "ANODE_PHI": self.ANODE_PHI
            },
            surface_dict={
                'cathode': self.cathode,
                'anode': self.anode
            },
            injector_dict={'cathode': self.cathode_injector},
        )
        # overwrite runinfo area to properly normalize currents
        self.runinfo.area = self.cathode_emitter.area

        #######################################################################
        # Add diagnostics                                                     #
        #######################################################################

        self.text_diag = diags.TextDiag(diag_steps=self.DIAG_STEPS,
                                        preset_string='perfdebug')
        # self.field_diag = diags.FieldDiagnostic(
        #    diag_steps=self.DIAG_STEPS, style='roelof', save_pdf=False
        # )
        self.fluxdiag = diags.FluxDiagnostic(diag_steps=self.DIAG_STEPS,
                                             runinfo=self.runinfo,
                                             check_charge_conservation=False,
                                             overwrite=True)

        #######################################################################
        # Initialize run and print diagnostic info                            #
        #######################################################################

        mwxrun.init_run()