def get_iec_frequencies(use_alpha, temperature, use_maxwellian=False):
    electron_mass = PhysicalConstants.electron_mass
    deuterium_mass = 2.01410178 * UnitConversions.amu_to_kg
    deuterium_tritium_mass = 5.0064125184e-27
    alpha_mass = 3.7273 * UnitConversions.amu_to_kg

    alpha_species = ChargedParticle(alpha_mass, 2 * PhysicalConstants.electron_charge)
    dt_species = ChargedParticle(deuterium_tritium_mass, 2 * PhysicalConstants.electron_charge)
    background_species = ChargedParticle(electron_mass, -PhysicalConstants.electron_charge)
    n_background = 1e20
    dt_velocity = np.sqrt(2 * 50e3 * PhysicalConstants.electron_charge / dt_species.m)
    alpha_velocity = np.sqrt(2 * 3.5e6 * PhysicalConstants.electron_charge / alpha_species.m)

    beam_species = alpha_species if use_alpha else dt_species
    beam_velocity = alpha_velocity if use_alpha else dt_velocity

    collision = CoulombCollision(beam_species, background_species, 1.0, beam_velocity)
    relaxation_process = MaxwellianRelaxationProcess(collision)

    if use_maxwellian:
        v_K = relaxation_process.numerical_kinetic_loss_maxwellian_frequency(n_background, temperature, beam_velocity)
        v_P = relaxation_process.numerical_kinetic_loss_maxwellian_frequency(n_background, temperature, beam_velocity)
        return v_K, v_P

        # maxwellian_frequency = relaxation_process.maxwellian_collisional_frequency(n_background, temperature, beam_velocity)
        # return maxwellian_frequency, maxwellian_frequency
    else:
        stationary_kinetic_frequency = relaxation_process.kinetic_loss_stationary_frequency(n_background, temperature, beam_velocity)
        stationary_momentum_frequency = relaxation_process.momentum_loss_stationary_frequency(n_background, temperature, beam_velocity)
        return stationary_kinetic_frequency, stationary_momentum_frequency
예제 #2
0
def get_maxwellian_collisional_frequencies():
    # Generate charged particles
    alpha = ChargedParticle(6.64424e-27, PhysicalConstants.electron_charge * 2)
    electron = ChargedParticle(9.014e-31, -PhysicalConstants.electron_charge)

    impact_parameter_ratio = 1.0
    beam_velocity = np.sqrt(2 * 3.5e6 * PhysicalConstants.electron_charge /
                            alpha.m)
    n = 1e20
    temperature = 20e3 * UnitConversions.eV_to_K
    print(n, temperature, beam_velocity)

    # Get reactant collision frequency and energy loss rate
    collision = CoulombCollision(electron, alpha, impact_parameter_ratio,
                                 beam_velocity)
    relaxation = MaxwellianRelaxationProcess(collision)
    numerical_v_P = relaxation.momentum_loss_maxwellian_frequency(
        n, temperature, beam_velocity)

    theoretical_v_P = 2 / (3 * np.sqrt(
        2 * np.pi)) * relaxation.momentum_loss_stationary_frequency(
            n, temperature, beam_velocity)

    print("Numerical Momentum Loss Frequency: {}".format(numerical_v_P))
    print("Theoretical Momentum Loss Frequency: {}".format(theoretical_v_P))
    print("Numerical to theoretical momentum loss ratio: {}\n".format(
        numerical_v_P / theoretical_v_P))

    numerical_v_K = relaxation.kinetic_loss_maxwellian_frequency(
        n, temperature, beam_velocity)
    print("Numerical Kinetic Loss Frequency: {}".format(numerical_v_K))
    print("Numerical kinetic to momentum loss ratio: {}".format(numerical_v_K /
                                                                numerical_v_P))
예제 #3
0
def plot_collisional_frequencies():
    """
    Plot the variation of collisional frequency with density, and temperature for different
    collisions
    """
    deuterium = ChargedParticle(2.01410178 * 1.66054e-27,
                                PhysicalConstants.electron_charge)
    electron = ChargedParticle(9.014e-31, -PhysicalConstants.electron_charge)

    impact_parameter_ratio = 1.0  # Is not necessary for this analysis
    velocities = np.logspace(4, 6, 100)
    number_density = np.logspace(20, 30, 100)
    VEL, N = np.meshgrid(velocities, number_density, indexing='ij')

    collision_pairs = [["Deuterium-Deuterium", deuterium, deuterium],
                       ["Electron-Deuterium", electron, deuterium],
                       ["Electron-Electron", electron, electron]]
    num_temp = 3
    temperatures = np.logspace(3, 5, num_temp) * UnitConversions.eV_to_K
    for j, pair in enumerate(collision_pairs):
        name = pair[0]
        p_1 = pair[1]
        p_2 = pair[2]

        fig, ax = plt.subplots(2, num_temp, figsize=(15, 20))
        fig.suptitle("Collisional Frequencies for {} Relaxation".format(name))
        for i, temp in enumerate(temperatures):
            collision = CoulombCollision(p_1, p_2, impact_parameter_ratio, VEL)
            relaxation_process = RelaxationProcess(collision)

            kinetic_frequency = relaxation_process.kinetic_loss_stationary_frequency(
                N, temp, VEL)
            momentum_frequency = relaxation_process.momentum_loss_stationary_frequency(
                N, temp, VEL, first_background=True)

            im = ax[0, i].contourf(np.log10(VEL), np.log10(N),
                                   np.log10(kinetic_frequency), 100)
            ax[0, i].set_title("Kinetic: T = {}".format(
                temp * UnitConversions.K_to_eV))
            ax[0, i].set_xlabel("Velocity (ms-1)")
            ax[0, i].set_ylabel("Number density (m-3)")
            fig.colorbar(im, ax=ax[0, i])

            im = ax[1, i].contourf(np.log10(VEL), np.log10(N),
                                   np.log10(momentum_frequency), 100)
            ax[1, i].set_title("Momentum: T = {}".format(
                temp * UnitConversions.K_to_eV))
            ax[1, i].set_xlabel("Velocity (ms-1)")
            ax[1, i].set_ylabel("Number density (m-3)")
            fig.colorbar(im, ax=ax[1, i])

        plt.show()
예제 #4
0
def compare_maxwellian_and_stationary_frequencies_against_temperature(
        number_density, temperature):
    # Generate charged particles
    alpha = ChargedParticle(6.64424e-27, PhysicalConstants.electron_charge * 2)
    electron = ChargedParticle(9.014e-31, -PhysicalConstants.electron_charge)

    # Get deuterium and alpha velocities from product and reactant beam
    # energies
    e_alpha = 1e3 * PhysicalConstants.electron_charge
    v_alpha = np.sqrt(2 * e_alpha / alpha.m)

    # Get product collision frequency and energy loss rate
    impact_parameter_ratio = 1.0  # Is not necessary for this analysis
    product_collision = CoulombCollision(alpha, electron,
                                         impact_parameter_ratio, v_alpha)
    product_relaxation = MaxwellianRelaxationProcess(product_collision)
    alpha_stationary_momentum_frequency = product_relaxation.momentum_loss_stationary_frequency(
        number_density, temperature, v_alpha)

    maxwellian_frequency = np.zeros(temperature.shape)
    alpha_maxwellian_momentum_frequency = np.zeros(temperature.shape)
    for i, T in enumerate(temperature):
        # Get maxwellian frequencies
        v = product_relaxation.maxwellian_collisional_frequency(
            number_density, T, v_alpha)
        maxwellian_frequency[i] = v
        v_P = product_relaxation.numerical_momentum_loss_maxwellian_frequency(
            number_density, T, v_alpha, epsrel=1e-8)
        alpha_maxwellian_momentum_frequency[i] = v_P

        print(T, v_P, v_P - alpha_stationary_momentum_frequency[i],
              np.abs(v_P - v) / v)

    fig, ax = plt.subplots()

    temperature *= UnitConversions.K_to_eV
    ax.loglog(temperature,
              alpha_stationary_momentum_frequency,
              label="Stationary")
    ax.loglog(temperature, maxwellian_frequency, label="Maxwellian")
    ax.loglog(temperature,
              alpha_maxwellian_momentum_frequency,
              label="Numerical Maxwellian")
    ax.legend()

    plt.show()
def run_sim(sim_type):
    """
    Runs simulation of a single species in a uniform velocity distribution
    
    sim_type: Instances of simulation class for coulomb collisions
    """
    # Set seed
    # for seed in range(5):
    seed = 1
    np.random.seed(seed)

    # Set up simulation
    particle = ChargedParticle(2.01410178 * 1.66054e-27,
                               PhysicalConstants.electron_charge)
    n = 10000
    weight = 1
    weighted_particle = ChargedParticle(
        2.01410178 * 1.66054e-27 * weight,
        PhysicalConstants.electron_charge * weight)
    sim = sim_type(n, particle, weight)

    # Get initial uniform velocities
    v_max = 1.0
    velocities = np.random.uniform(-v_max, v_max, size=(n, 3))

    # Get time step from collisional frequencies
    c = CoulombCollision(weighted_particle, weighted_particle, 1.0,
                         2.0 * v_max)
    process = RelaxationProcess(c)
    v_K = process.kinetic_loss_stationary_frequency(n * weight, 1.0,
                                                    2.0 * v_max)
    v_P = process.momentum_loss_stationary_frequency(n * weight, 1.0,
                                                     2.0 * v_max)
    collision_time = 1.0 / max([v_K, v_P])
    dt = 0.1 * collision_time
    final_time = 2.5 * collision_time

    t, v_results = sim.run_sim(velocities, dt, final_time)

    post_process_results(t, v_results)
def plot_impact_parameter_variation_with_temperature():
    """
    Simple function to see how the impact parameter varies with temperature
    """
    temperatures = UnitConversions.eV_to_K * np.logspace(1, 4, 100)
    deuterium = ChargedParticle(2.01410178 * 1.66054e-27,
                                PhysicalConstants.electron_charge)
    electron = ChargedParticle(9.014e-31, -PhysicalConstants.electron_charge)

    electron_b_90 = np.zeros(temperatures.shape)
    deuterium_b_90 = np.zeros(temperatures.shape)
    for i, temperature in enumerate(temperatures):
        electron_velocity = np.sqrt(PhysicalConstants.boltzmann_constant *
                                    temperature / electron.m)
        deuterium_velocity = np.sqrt(PhysicalConstants.boltzmann_constant *
                                     temperature / deuterium.m)

        collision = CoulombCollision(deuterium, electron, 1.0,
                                     electron_velocity)
        b_90 = collision.b_90
        electron_b_90[i] = b_90

        collision = CoulombCollision(deuterium, electron, 1.0,
                                     deuterium_velocity)
        b_90 = collision.b_90
        deuterium_b_90[i] = b_90

    plt.figure()

    plt.loglog(temperatures, electron_b_90, label="Electron Velocity")
    plt.loglog(temperatures, deuterium_b_90, label="Deuterium Velocity")

    plt.title(
        "Impact Parameter vs Temperature for Deuterium-Electron binary collision"
    )
    plt.ylabel("Impact Parameter (m)")
    plt.xlabel("Temperature (K)")
    plt.legend()
    plt.show()
예제 #7
0
def run_electon_beam_into_argon_gas_sim(sim_type):
    """
    Run a simulation of an electron beam into a background gas at a
    given temperature
    """
    p_1 = ChargedParticle(PhysicalConstants.electron_mass,
                          -PhysicalConstants.electron_charge)
    p_2 = ChargedParticle(PhysicalConstants.electron_mass,
                          -PhysicalConstants.electron_charge)
    p_3 = ChargedParticle(39.948 * UnitConversions.amu_to_kg,
                          PhysicalConstants.electron_charge)
    w_1 = 1
    w_2 = 1000
    w_3 = 1000
    n = int(1e3)

    if sim_type == "Nanbu":
        sim = NanbuCollisionModel(np.asarray([n, n, n]),
                                  np.asarray([p_1, p_2, p_3]),
                                  np.asarray([w_1, w_2, w_3]),
                                  coulomb_logarithm=10.0,
                                  frozen_species=np.asarray(
                                      [False, True, True]))
    else:
        raise ValueError("Invalid sim type")

    # Set initial velocity conditions of beam
    beam_velocity = np.sqrt(2 * 100 * PhysicalConstants.electron_charge /
                            p_1.m)
    velocities = np.zeros((3 * n, 3))
    velocities[:n, :] = np.asarray([0.0, 0.0, beam_velocity])

    # Set initial velocity conditions of background
    k_T = 2.0
    sigma = np.sqrt(2 * k_T * PhysicalConstants.electron_charge / p_2.m)
    electron_velocities = np.random.normal(
        loc=0.0, scale=sigma, size=velocities[n:2 * n, :].shape) / np.sqrt(3)
    velocities[n:2 * n, :] = electron_velocities
    k_T = 0.02
    sigma = np.sqrt(2 * k_T * PhysicalConstants.electron_charge / p_3.m)
    ion_velocities = np.random.normal(
        loc=0.0, scale=sigma, size=velocities[2 * n:, :].shape) / np.sqrt(3)
    velocities[2 * n:, :] = ion_velocities

    tau = get_relaxation_time(p_1, n * w_2, beam_velocity)
    dt = 0.01 * tau
    final_time = 1.0 * tau

    t, v_results = sim.run_sim(velocities, dt, final_time)

    t /= tau
    v_z_estimate = 1.0 - 3 * t
    v_ort_estimate = 3.98 * t

    fig, ax = plt.subplots(2, figsize=(10, 10))

    l_v_z = ax[0].plot(t,
                       np.mean(v_results[:n, 2, :], axis=0) / beam_velocity,
                       color="b",
                       label="<v_z>")
    l_v_z_estimate = ax[0].plot(t,
                                v_z_estimate,
                                linestyle="--",
                                color="c",
                                label="<v_z_estimate>")
    ax[0].set_xlim([0.0, t[-1]])
    ax[0].set_xlabel("Timestep")
    ax[0].set_ylabel("Velocities ms-1")
    ax[0].set_ylim([0.0, 1.0])
    ax[0].set_title("Beam Velocities")

    # Twin axes for second plot to replicate paper set up
    ax_twin = ax[0].twinx()
    l_ort = ax_twin.plot(
        t,
        np.mean(v_results[:n, 0, :]**2 + v_results[:n, 1, :]**2, axis=0) /
        beam_velocity**2,
        color="r",
        label="<v_ort^2>")
    l_ort_estimate = ax_twin.plot(t,
                                  v_ort_estimate,
                                  linestyle="--",
                                  color="y",
                                  label="<v_ort_estimate>")
    ax_twin.set_ylabel("Square of Velocities (ms-1)^2")
    ax_twin.set_ylim([0.0, 0.33])

    lines = l_v_z + l_v_z_estimate + l_ort + l_ort_estimate
    labs = [l.get_label() for l in lines]
    ax[0].legend(lines, labs)

    ax[1].plot(t, np.mean(v_results[2 * n:, 0, :], axis=0), label="<v_x>")
    ax[1].plot(t, np.mean(v_results[2 * n:, 1, :], axis=0), label="<v_y>")
    ax[1].plot(t, np.mean(v_results[2 * n:, 2, :], axis=0), label="<v_z>")
    ax[1].set_xlim([0.0, t[-1]])
    ax[1].legend()
    ax[1].set_xlabel("Timestep")
    ax[1].set_ylabel("Velocities ms-1")
    ax[1].set_title("Background Velocities")

    plt.show()

    energy = p_1.m * np.sum(v_results[:n, 0, :]**2 + v_results[:n, 1, :]**2 +
                            v_results[:n, 2, :]**2,
                            axis=0)
    energy += p_2.m * np.sum(v_results[n:, 0, :]**2 + v_results[n:, 1, :]**2 +
                             v_results[n:, 2, :]**2,
                             axis=0)
    x_mom = p_1.m * np.sum(v_results[:n, 0, :], axis=0) + p_2.m * np.sum(
        v_results[n:, 0, :], axis=0)
    y_mom = p_1.m * np.sum(v_results[:n, 1, :], axis=0) + p_1.m * np.sum(
        v_results[n:, 1, :], axis=0)
    z_mom = p_1.m * np.sum(v_results[:n, 2, :], axis=0) + p_1.m * np.sum(
        v_results[n:, 2, :], axis=0)

    fig, ax = plt.subplots(2)

    ax[0].plot(t, energy)
    ax[0].set_title("Conservation of Energy")
    ax[1].plot(t, x_mom, label="x momentum")
    ax[1].plot(t, y_mom, label="y momentum")
    ax[1].plot(t, z_mom, label="z momentum")
    ax[1].legend()
    ax[1].set_title("Conservation of Momentum")

    fig.suptitle("Conservation Plots")
    plt.show()

    return t, v_results
예제 #8
0
def generate_sim_results(number_densities, T):
    # Set simulation independent parameters
    N = int(1e4)
    dt_factor = 0.01
    w_1 = int(1)
    
    # Generate result lists
    energy_results = dict()
    velocity_results = dict()
    t_halves = dict()
    t_theory = dict()
    
    # Run simulations
    names = ["reactant", "product"]
    for j, name in enumerate(names):
        t_halves[name] = np.zeros(number_densities.shape)
        t_theory[name] = np.zeros(number_densities.shape)
        energy_results[name] = []
        velocity_results[name] = []
        for i, n in enumerate(number_densities):
            # Set up beam sim
            if "product" == name:
                p_1 = ChargedParticle(6.64424e-27, 2 * PhysicalConstants.electron_charge)
                energy = 3.5e6 * PhysicalConstants.electron_charge
            elif "reactant" == name:
                p_1 = ChargedParticle(2.014102 * UnitConversions.amu_to_kg, PhysicalConstants.electron_charge)
                energy = 50e3 * PhysicalConstants.electron_charge
            else:
                raise ValueError()
            beam_velocity = np.sqrt(2 * energy / p_1.m)
            p_2 = ChargedParticle(2.014102 * UnitConversions.amu_to_kg, PhysicalConstants.electron_charge)

            # Instantiate simulation
            w_2 = int(n / N)
            sim = AbeCoulombCollisionModel(N, p_1, w_1=w_1, N_2=N, particle_2=p_2, w_2=w_2, freeze_species_2=False)

            # Set up velocities
            velocities = np.zeros((2 * N, 3))
            velocities[:N, :] = np.asarray([0.0, 0.0, beam_velocity])
            velocities[N:] = np.asarray([0.0, 0.0, 0.0])

            # Get approximate time scale
            impact_parameter_ratio = 1.0    # Is not necessary for this analysis
            reactant_collision = CoulombCollision(p_1, p_2,
                                                  impact_parameter_ratio,
                                                  beam_velocity)
            reactant_relaxation = RelaxationProcess(reactant_collision)
            deuterium_kinetic_frequency = reactant_relaxation.kinetic_loss_stationary_frequency(N * w_2, T, beam_velocity)
            tau = 1.0 / deuterium_kinetic_frequency
            dt = dt_factor * tau
            final_time = 4.0 * tau

            t, v_results = sim.run_sim(velocities, dt, final_time)

            # Get salient results
            velocities = np.mean(np.sqrt(v_results[:N, 0, :] ** 2 + v_results[:N, 1, :] ** 2 + v_results[:N, 2, :] ** 2), axis=0)
            energies = 0.5 * p_1.m * velocities ** 2
            velocity_results[name].append(velocities)
            energy_results[name].append(velocities)

            # Get energy half times
            energy_time_interpolator = interp1d(energies / energy, t)
            t_half = energy_time_interpolator(0.5)
            t_halves[name][i] = t_half
            t_theory[name][i] = tau

        # Save results
        np.savetxt("stationary_{}_{}_{}_half_times".format(name, N, dt_factor), t_halves[name], fmt='%s')
        np.savetxt("stationary_{}_{}_{}_velocities".format(name, N, dt_factor), velocity_results[name], fmt='%s')
        np.savetxt("stationary_{}_{}_{}_energies".format(name, N, dt_factor), energy_results[name], fmt='%s')

    # Plot results
    plt.figure()

    for name in names:
        plt.loglog(number_densities, t_halves[name], label="{}_Simulation".format(name))
        plt.loglog(number_densities, t_theory[name], label="{}_Theoretical values".format(name))
    
    plt.title("Comparison of thermalisation rates of products and reactants")
    plt.legend()
    plt.savefig("energy_half_times")
    plt.show()
def run_sim():
    p_1 = ChargedParticle(PhysicalConstants.electron_mass,
                          -PhysicalConstants.electron_charge)
    p_2 = ChargedParticle(39.948 * UnitConversions.amu_to_kg,
                          PhysicalConstants.electron_charge)
    n = int(1e4)
    w = int(1e17)

    sim = NanbuCollisionModel(np.asarray([n, n]),
                              np.asarray([p_1, p_2]),
                              np.asarray([w, w]),
                              coulomb_logarithm=15.9,
                              frozen_species=np.asarray([False, True]))

    # Set initial velocity conditions of background
    velocities = np.zeros((2 * n, 3))
    k_T = 1e3
    sigma = np.sqrt(2 * k_T * PhysicalConstants.electron_charge / p_1.m)
    electron_velocities = np.random.normal(
        loc=0.0, scale=sigma, size=velocities[:n, :].shape) / np.sqrt(3)
    velocities[:n, :] = electron_velocities
    sigma = np.sqrt(2 * k_T * PhysicalConstants.electron_charge / p_2.m)
    ion_velocities = np.random.normal(
        loc=0.0, scale=sigma, size=velocities[:n, :].shape) / np.sqrt(3)
    velocities[n:, :] = ion_velocities

    V = np.sqrt(8.0 * k_T / (np.pi * p_1.m))
    num_periods = 50.0
    frequencies = [1e4, 1e5]
    v_squared_results = []
    for i, f in enumerate(frequencies):
        t_p = 1 / f
        omega = 2 * np.pi * f
        dt = t_p / 10.0
        dt = min(dt, 2.5e-7)
        t = 0.0
        n = 1
        times = [t]
        v_squared_results.append([0.0])
        I = np.zeros((n, 3))
        I[:, :] = np.asarray([1.0, 0.0, 0.0])
        new_vel = np.copy(velocities)
        while t < num_periods * t_p:
            I_results = []
            v_means = []
            while t < n * t_p:
                print(t / t_p)
                # Accelerate particles due to field
                new_vel[:n, :] -= I * V * np.sin(omega * t)

                # Carry out coulomb collisions
                new_vel = sim.single_time_step(new_vel, dt)

                # Calculate I
                v_mean = np.mean(
                    np.sqrt(new_vel[:n, 0]**2 + new_vel[:n, 1]**2 +
                            new_vel[:n, 2]**2))
                current = v_mean**2 * dt
                v_means.append(v_mean)
                I_results.append(current)

                # Correct velocities
                new_vel[:n, :] += I * V * np.sin(omega * t)

                t += dt

            # plt.figure()
            # plt.plot(v_means)
            # plt.show()

            v = np.sum(np.asarray(I_results)) / t_p
            v_squared_results[i].append(v / V**2)
            n += 1

    # Plot results
    plt.figure()
    for v_squared in v_squared_results:
        plt.plot(np.asarray(v_squared)**2)
        plt.scatter(range(len(v_squared)), np.asarray(v_squared)**2)
    plt.show()

    # Save results
    v_squared_results = np.asarray(v_squared_results)
    np.savetxt("v_squared_results", v_squared_results)
def compare_product_and_reactant_energy_loss_rates(
        number_density,
        reaction_name,
        plot_results=True,
        plot_energy_frequencies=True):
    # Generate charged particles
    alpha = ChargedParticle(6.64424e-27, PhysicalConstants.electron_charge * 2)
    electron = ChargedParticle(9.014e-31, -PhysicalConstants.electron_charge)
    if reaction_name == "DT":
        reactant = ChargedParticle(5.0064125184e-27,
                                   2 * PhysicalConstants.electron_charge)
        e_alpha = 3.5e6 * PhysicalConstants.electron_charge
        e_reactant = 30e3 * PhysicalConstants.electron_charge
        Z = 2
    elif reaction_name == "pB":
        reactant = ChargedParticle((10.81 + 1) * UnitConversions.amu_to_kg,
                                   PhysicalConstants.electron_charge * 12)
        e_alpha = 8.6e6 / 3 * PhysicalConstants.electron_charge
        e_reactant = 500e3 * PhysicalConstants.electron_charge
        Z = 6
    else:
        raise ValueError("Name is invalid!")
    temperature = e_reactant

    # Get deuterium and alpha velocities from product and reactant beam
    # energies
    v_alpha = np.sqrt(2 * e_alpha / alpha.m)
    v_reactant = np.sqrt(2 * e_reactant / reactant.m)

    # Get reactant collision frequency and energy loss rate
    impact_parameter_ratio = 1.0  # Is not necessary for this analysis
    reactant_collision = CoulombCollision(reactant, reactant,
                                          impact_parameter_ratio, v_reactant)
    reactant_relaxation = RelaxationProcess(reactant_collision)
    deuterium_kinetic_frequency = reactant_relaxation.kinetic_loss_stationary_frequency(
        Z * number_density, temperature, v_reactant)
    deuterium_energy_loss_rates = reactant_relaxation.energy_loss_with_distance(
        Z * number_density, temperature, v_reactant)
    deuterium_momentum_frequency = reactant_relaxation.momentum_loss_stationary_frequency(
        Z * number_density, temperature, v_reactant)
    deuterium_momentum_loss_rates = deuterium_momentum_frequency * reactant.m

    # Get product collision frequency and energy loss rate
    product_collision = CoulombCollision(alpha, reactant,
                                         impact_parameter_ratio, v_alpha)
    product_relaxation = RelaxationProcess(product_collision)
    alpha_kinetic_frequency = product_relaxation.kinetic_loss_stationary_frequency(
        Z * number_density, temperature, v_alpha)
    alpha_energy_loss_rates = product_relaxation.energy_loss_with_distance(
        Z * number_density, temperature, v_alpha)
    alpha_momentum_frequency = product_relaxation.momentum_loss_stationary_frequency(
        Z * number_density, temperature, v_alpha)
    alpha_momentum_loss_rates = alpha_momentum_frequency * alpha.m

    # Compare velocities of particles
    v_electron = np.sqrt(2 * PhysicalConstants.boltzmann_constant *
                         temperature / electron.m)
    print(
        "For stationary collisions to be a reasonable approximation, the beams need "
        "to be travelling faster than the electron thermal velocity... ")
    print("Electron Normalised Velocity: {}".format(v_electron / 3e8))
    print("Alpha-Electron Velocity Ratio: {}".format(v_alpha / v_electron))
    print("Deuterium-Electron Velocity Ratio: {}".format(v_reactant /
                                                         v_electron))

    print(deuterium_energy_loss_rates * e_alpha / alpha_energy_loss_rates /
          e_reactant)

    deuterium_energy_distance_travelled = e_reactant / deuterium_energy_loss_rates
    alpha_energy_distance_travelled = e_alpha / alpha_energy_loss_rates
    if plot_results:
        fig, ax = plt.subplots(2, 2, figsize=(7, 7))
        if plot_energy_frequencies:
            # energy results
            ax[0, 0].loglog(number_density,
                            deuterium_kinetic_frequency,
                            label="Reactant Beam")
            ax[0, 0].loglog(number_density,
                            alpha_kinetic_frequency,
                            label="Product Beam")
            ax[0, 0].set_xlabel("Number density (m-3)")
            ax[0, 0].set_ylabel("Energy Collision Frequency (s-1)")
            ax[0, 0].set_title(
                "Energy Collision Frequencies - {}".format(reactant_name))
            ax[0, 0].legend()

            ax[1, 0].loglog(number_density,
                            deuterium_energy_loss_rates,
                            label="Reactant Beam")
            ax[1, 0].loglog(number_density,
                            alpha_energy_loss_rates,
                            label="Product Beam")
            ax[1, 0].set_xlabel("Number density (m-3)")
            ax[1, 0].set_ylabel("Energy Loss Rate per metre (Jm-1)")
            ax[1, 0].set_title(
                "Energy Loss Rates per metre - {}".format(reactant_name))
            ax[1, 0].legend()

            ax[0, 1].loglog(number_density,
                            deuterium_energy_loss_rates / e_reactant,
                            label="Reactant Beam")
            ax[0, 1].loglog(number_density,
                            alpha_energy_loss_rates / e_alpha,
                            label="Product Beam")
            ax[0, 1].axhline(0.5, linestyle="--")
            ax[0, 1].set_xlabel("Number density (m-3)")
            ax[0, 1].set_ylabel("Normalised Kinetic Loss Rate (m-1)")
            ax[0, 1].set_title(
                "Normalised Kinetic Loss Rate - {}".format(reactant_name))
            ax[0, 1].legend()

            ax[1, 1].loglog(number_density,
                            deuterium_energy_distance_travelled,
                            label="Reactant Beam")
            ax[1, 1].loglog(number_density,
                            alpha_energy_distance_travelled,
                            label="Product Beam")
            ax[1, 1].set_xlabel("Number density (m-3)")
            ax[1, 1].set_ylabel("Distance traveller (m)")
            ax[1, 1].set_title("Distance travelled - {}".format(reactant_name))
            ax[1, 1].legend()
        else:
            # momentum results
            ax[0, 0].loglog(number_density,
                            deuterium_momentum_frequency,
                            label="Reactant Beam")
            ax[0, 0].loglog(number_density,
                            alpha_momentum_frequency,
                            label="Product Beam")
            ax[0, 0].set_xlabel("Number density (m-3)")
            ax[0, 0].set_ylabel("Momentum Collision Frequency (s-1)")
            ax[0, 0].set_title(
                "Momentum Collision Frequencies - {}".format(reactant_name))
            ax[0, 0].legend()

            ax[1, 0].loglog(number_density,
                            deuterium_momentum_loss_rates,
                            label="Reactant Beam")
            ax[1, 0].loglog(number_density,
                            alpha_momentum_loss_rates,
                            label="Product Beam")
            ax[1, 0].set_xlabel("Number density (m-3)")
            ax[1, 0].set_ylabel("Momentum Loss Rate per metre (Jm-1)")
            ax[1, 0].set_title(
                "Momentum Loss Rates per metre - {}".format(reactant_name))
            ax[1, 0].legend()

            deuterium_momentum = reactant.m * v_reactant
            alpha_momentum = alpha.m * v_alpha
            ax[0, 1].loglog(number_density,
                            deuterium_momentum_loss_rates / deuterium_momentum,
                            label="Reactant Beam")
            ax[0, 1].loglog(number_density,
                            alpha_momentum_loss_rates / alpha_momentum,
                            label="Product Beam")
            ax[0, 1].axhline(0.1, linestyle="--")
            ax[0, 1].set_xlabel("Number density (m-3)")
            ax[0, 1].set_ylabel("Normalised Momentum Loss Rate (m-1)")
            ax[0, 1].set_title(
                "Normalised Momentum Loss Rate - {}".format(reactant_name))
            ax[0, 1].legend()

            ax[1, 1].loglog(number_density,
                            deuterium_momentum / deuterium_momentum_loss_rates,
                            label="Reactant Beam")
            ax[1, 1].loglog(number_density,
                            alpha_momentum / alpha_momentum_loss_rates,
                            label="Product Beam")
            ax[1, 1].set_xlabel("Number density (m-3)")
            ax[1, 1].set_ylabel("Distance traveller (m)")
            ax[1, 1].set_title("Distance travelled - {}".format(reactant_name))
            ax[1, 1].legend()

        # fig.suptitle("Comparison of product and reactant beam loss rates due to electron collisions")
        fig.tight_layout()
        plt.savefig("relaxation_rates_{}".format(reactant_name))
        plt.show()

    return deuterium_energy_distance_travelled, alpha_energy_distance_travelled
def generate_sim_results(number_densities, T):
    # Set simulation independent parameters
    N = int(1e3)
    dt_factor = 0.01
    w_1 = int(1)

    # Make results directory if it does not exist
    res_dir = "results"
    if not os.path.exists(res_dir):
        os.mkdir(res_dir)

    # Generate result lists
    energy_results = dict()
    velocity_results = dict()
    t_halves = dict()
    t_theory = dict()

    # Run simulations
    names = ["reactant", "product"]
    for j, name in enumerate(names):
        t_halves[name] = np.zeros(number_densities.shape)
        t_theory[name] = np.zeros(number_densities.shape)
        energy_results[name] = []
        velocity_results[name] = []
        for i, n in enumerate(number_densities):
            print("Number density: {}".format(n))

            # Set up beam sim
            if "product" == name:
                p_1 = ChargedParticle(6.64424e-27,
                                      2 * PhysicalConstants.electron_charge)
                energy = 3.5e6 * PhysicalConstants.electron_charge
            elif "reactant" == name:
                p_1 = ChargedParticle(2.014102 * UnitConversions.amu_to_kg,
                                      PhysicalConstants.electron_charge)
                energy = 50e3 * PhysicalConstants.electron_charge
            else:
                raise ValueError()
            beam_velocity = np.sqrt(2 * energy / p_1.m)
            p_2 = ChargedParticle(2.014102 * UnitConversions.amu_to_kg,
                                  PhysicalConstants.electron_charge)

            # Instantiate simulation
            w_2 = int(n / N)
            particle_numbers = np.asarray([N, N])
            sim = NanbuCollisionModel(particle_numbers,
                                      np.asarray([p_1, p_2]),
                                      np.asarray([w_1, w_2]),
                                      coulomb_logarithm=10.0,
                                      frozen_species=np.asarray([False,
                                                                 False]))

            # Set up velocities
            velocities = np.zeros((2 * N, 3))
            velocities[:N, :] = np.asarray([0.0, 0.0, beam_velocity])
            k_T = T * PhysicalConstants.boltzmann_constant
            sigma = np.sqrt(2 * k_T / p_2.m)
            background_velocities = np.random.normal(
                loc=0.0, scale=sigma,
                size=velocities[N:2 * N, :].shape) / np.sqrt(3)
            velocities[N:2 * N, :] = background_velocities

            # Get approximate time scale
            impact_parameter_ratio = 1.0  # Is not necessary for this analysis
            reactant_collision = CoulombCollision(p_1, p_2,
                                                  impact_parameter_ratio,
                                                  beam_velocity)
            reactant_relaxation = MaxwellianRelaxationProcess(
                reactant_collision)
            deuterium_kinetic_frequency = reactant_relaxation.numerical_kinetic_loss_maxwellian_frequency(
                N * w_2, T, beam_velocity)
            tau = 1.0 / deuterium_kinetic_frequency
            dt = dt_factor * tau
            final_time = 4.0 * tau

            t, v_results = sim.run_sim(velocities, dt, final_time)

            # Get salient results
            velocities = np.mean(
                np.sqrt(v_results[:N, 0, :]**2 + v_results[:N, 1, :]**2 +
                        v_results[:N, 2, :]**2),
                axis=0)
            energies = 0.5 * p_1.m * velocities**2
            velocity_results[name].append(velocities)
            energy_results[name].append(energies)
            velocities_deuterium = np.mean(
                np.sqrt(v_results[N:2 * N, 0, :]**2 +
                        v_results[N:2 * N, 1, :]**2 +
                        v_results[N:2 * N, 2, :]**2),
                axis=0)
            energies_deuterium = 0.5 * p_2.m * np.mean(
                np.sqrt(v_results[N:2 * N, 0, :]**2 +
                        v_results[N:2 * N, 1, :]**2 +
                        v_results[N:2 * N, 2, :]**2),
                axis=0)

            # Plot results
            fig, ax = plt.subplots(2)

            ax[0].plot(t, velocities_deuterium, label="ions")
            ax[0].plot(t, velocities, label="beam")
            ax[1].plot(t, energies_deuterium, label="ions")
            ax[1].plot(t, energies, label="beam")
            ax[1].plot(t, energies + energies_deuterium, label="total")

            plt.legend()
            plt.savefig(
                os.path.join(
                    res_dir,
                    "stationary_{}_{}_{}_{}.png".format(name, N, dt_factor,
                                                        n)))

            # Get energy half times
            energy_time_interpolator = interp1d(energies / energy, t)
            t_half = energy_time_interpolator(0.5)
            t_halves[name][i] = t_half
            t_theory[name][i] = tau

        # Save results
        np.savetxt(os.path.join(
            res_dir,
            "stationary_{}_{}_{}_half_times".format(name, N, dt_factor)),
                   t_halves[name],
                   fmt='%s')
        np.savetxt(os.path.join(
            res_dir,
            "stationary_{}_{}_{}_velocities".format(name, N, dt_factor)),
                   velocity_results[name],
                   fmt='%s')
        np.savetxt(os.path.join(
            res_dir, "stationary_{}_{}_{}_energies".format(name, N,
                                                           dt_factor)),
                   energy_results[name],
                   fmt='%s')

    # Plot results
    plt.figure()

    for name in names:
        plt.loglog(number_densities,
                   t_halves[name],
                   label="{}_Simulation".format(name))
        plt.loglog(number_densities,
                   t_theory[name],
                   label="{}_Theoretical values".format(name))

    plt.title("Comparison of thermalisation rates of products and reactants")
    plt.legend()
    plt.savefig(os.path.join(res_dir, "energy_half_times"))
    plt.show()
def run_electron_thermal_relaxation_sim(sim_type):
    """
    Run a simulation of single species electron thermal relaxation
    """
    # Set seed
    np.random.seed(1)
    
    p_1 = ChargedParticle(PhysicalConstants.electron_mass, -PhysicalConstants.electron_charge)
    n = int(1e4)

    sim = sim_type(n, p_1, 1, coulomb_logarithm=10.0)

    T_y = 11500.0
    T_factor = 1.3
    T_e = (T_factor * T_y + 2 * T_y) / 3.0
    velocities = np.zeros((n, 3))
    sigma_ort = np.sqrt(PhysicalConstants.boltzmann_constant * T_y / p_1.m)
    sigma_x = np.sqrt(PhysicalConstants.boltzmann_constant * T_factor * T_y / p_1.m)
    velocities[:, 0] = np.random.normal(loc=0.0, scale=sigma_x, size=velocities[:, 0].shape)
    velocities[:, 1] = np.random.normal(loc=0.0, scale=sigma_ort, size=velocities[:, 1].shape)
    velocities[:, 2] = np.random.normal(loc=0.0, scale=sigma_ort, size=velocities[:, 2].shape)

    # Get simulation time
    tau = get_relaxation_time(p_1, n, T_e)
    dt = 0.02 * tau
    final_time = 12.0 * tau

    T_x = np.std(velocities[:, 0], axis=0) ** 2 * p_1.m / (PhysicalConstants.boltzmann_constant)
    T_y = np.std(velocities[:, 1], axis=0) ** 2 * p_1.m / (PhysicalConstants.boltzmann_constant)
    T_z = np.std(velocities[:, 2], axis=0) ** 2 * p_1.m / (PhysicalConstants.boltzmann_constant)

    t, v_results = sim.run_sim(velocities, dt, final_time)

    t /= tau
    dT_0 = (T_factor - 1.0) * T_y
    dT = dT_0 * np.exp(-8.0 * t / (5.0 * np.sqrt(2.0 * np.pi)))
    T_x_theory = 1.0 + dT / T_e * 2.0 / 3.0
    T_ort_theory = 1.0 - dT / T_e / 3.0

    fig, ax = plt.subplots(1, figsize=(10, 10))

    T_x = np.std(v_results[:, 0, :], axis=0) ** 2 * p_1.m / (PhysicalConstants.boltzmann_constant)
    T_y = np.std(v_results[:, 1, :], axis=0) ** 2 * p_1.m / (PhysicalConstants.boltzmann_constant)
    T_z = np.std(v_results[:, 2, :], axis=0) ** 2 * p_1.m / (PhysicalConstants.boltzmann_constant)

    ax.plot(t, T_x / T_e, label="T_x")
    ax.plot(t, T_y / T_e, label="T_y")
    ax.plot(t, T_z / T_e, label="T_z")
    ax.plot(t, T_x_theory, label="T_x_num", linestyle="--")
    ax.plot(t, T_ort_theory, label="T_ort_num", linestyle="--")
    ax.set_xlim([0.0, t[-1]])
    ax.legend()
    ax.set_xlabel("Timestep")
    ax.set_ylabel("Temperature")
    ax.set_title("Electron thermal relaxation")
    plt.savefig("electron_thermal_relaxation")

    plt.show()

    # Plot velocity histograms
    fig, ax = plt.subplots(2, 3, figsize=(10, 10))

    ax[0, 0].hist(v_results[:, 0, 0], 100)
    ax[0, 1].hist(v_results[:, 1, 0], 100)
    ax[0, 2].hist(v_results[:, 2, 0], 100)

    ax[1, 0].hist(v_results[:, 0, -1], 100)
    ax[1, 1].hist(v_results[:, 1, -1], 100)
    ax[1, 2].hist(v_results[:, 2, -1], 100)

    plt.savefig("electron_thermal_relaxation_velocity_distributions")
    plt.show()

    # Plot energy conservation
    energy = np.sum(v_results[:, 0, :] ** 2 + v_results[:, 1, :] ** 2 + v_results[:, 2, :] ** 2, axis=0)
    x_mom = np.sum(v_results[:, 0, :], axis=0)
    y_mom = np.sum(v_results[:, 1, :], axis=0)
    z_mom = np.sum(v_results[:, 2, :], axis=0)

    fig, ax = plt.subplots(2)

    ax[0].plot(t, energy)
    ax[0].set_title("Conservation of Energy")
    ax[1].plot(t, x_mom, label="x momentum")
    ax[1].plot(t, y_mom, label="y momentum")
    ax[1].plot(t, z_mom, label="z momentum")
    ax[1].legend()
    ax[1].set_title("Conservation of Momentum")

    fig.suptitle("Conservation Plots")
    plt.show()
예제 #13
0
def generate_sim_results(number_densities,
                         reactant_name,
                         plot_individual_sims=False):
    # Set simulation independent parameters
    N = int(1e4)
    dt_factor = 0.01

    # Generate result lists
    energy_results = dict()
    velocity_results = dict()
    t_halves = dict()
    t_theory = dict()

    # Make results directory if it does not exist
    res_dir = os.path.join("results", reactant_name)
    if not os.path.exists(res_dir):
        os.mkdir(res_dir)

    # Run simulations
    names = ["reactant", "product"]
    for j, name in enumerate(names):
        t_halves[name] = np.zeros(number_densities.shape)
        t_theory[name] = np.zeros(number_densities.shape)
        energy_results[name] = []
        velocity_results[name] = []
        for i, n in enumerate(number_densities):
            # Set temperature of background - this is assumed to be lower than the energy of the beam
            T_b = 1e3 if reactant_name is "DT" else 10e3
            T_b *= UnitConversions.eV_to_K

            # Set up beam sim
            if "product" == name:
                p_1 = ChargedParticle(6.64424e-27,
                                      2 * PhysicalConstants.electron_charge)
                if reactant_name == "DT":
                    p_2 = ChargedParticle(
                        5.0064125184e-27,
                        2 * PhysicalConstants.electron_charge)
                    energy = 3.5e6 * PhysicalConstants.electron_charge
                    Z = 2

                    # Get number density of products from reactivity
                    T = 50
                    reaction = DTReaction()
                    reactivity_fit = BoschHaleReactivityFit(reaction)
                    reactivity = reactivity_fit.get_reactivity(T)
                    n_1 = n * reactivity
                elif reactant_name == "pB":
                    p_2 = ChargedParticle(
                        (10.81 + 1) * UnitConversions.amu_to_kg,
                        PhysicalConstants.electron_charge * 12)
                    energy = 8.6e6 / 3.0 * PhysicalConstants.electron_charge
                    Z = 6

                    # Get number density of products from reactivity
                    T = 500
                    reaction = pBReaction()
                    reactivity_fit = BoschHaleReactivityFit(reaction)
                    reactivity = reactivity_fit.get_reactivity(T)
                    n_1 = n * reactivity
                else:
                    raise ValueError("Invalid reactant")
            elif "reactant" == name:
                n_1 = n
                if reactant_name == "DT":
                    p_1 = ChargedParticle(
                        5.0064125184e-27,
                        2 * PhysicalConstants.electron_charge)
                    p_2 = ChargedParticle(
                        5.0064125184e-27,
                        2 * PhysicalConstants.electron_charge)
                    energy = 50e3 * PhysicalConstants.electron_charge
                    Z = 2
                elif reactant_name == "pB":
                    p_1 = ChargedParticle(
                        (10.81 + 1) * UnitConversions.amu_to_kg,
                        PhysicalConstants.electron_charge * 12)
                    p_2 = ChargedParticle(
                        (10.81 + 1) * UnitConversions.amu_to_kg,
                        PhysicalConstants.electron_charge * 12)
                    energy = 500e3 * PhysicalConstants.electron_charge
                    Z = 6
                else:
                    raise ValueError("Invalid reactant")
            else:
                raise ValueError()
            beam_velocity = np.sqrt(2 * energy / p_1.m)
            electron = ChargedParticle(9.014e-31,
                                       -PhysicalConstants.electron_charge)

            # Instantiate simulation
            w_b = long(n / N)
            w_1 = long(n_1 / N)
            w_1 = w_1 if w_1 > 0 else long(1)
            print(w_1, w_b)
            particle_numbers = np.asarray([N, N, N])
            sim = NanbuCollisionModel(particle_numbers,
                                      np.asarray([p_1, p_2, electron]),
                                      np.asarray([w_1, w_b, Z * w_b]),
                                      coulomb_logarithm=10.0,
                                      frozen_species=np.asarray(
                                          [False, False, False]),
                                      include_self_collisions=True)

            # Set up velocities
            velocities = np.zeros((np.sum(particle_numbers), 3))
            velocities[:N, :] = np.asarray([0.0, 0.0, beam_velocity])

            # Small maxwellian distribution used for background species
            k_T = T_b * PhysicalConstants.boltzmann_constant
            sigma = np.sqrt(2 * k_T / p_2.m)
            p_2_velocities = np.random.normal(
                loc=0.0, scale=sigma,
                size=velocities[N:2 * N, :].shape) / np.sqrt(3)
            velocities[N:2 * N, :] = p_2_velocities
            sigma = np.sqrt(2 * k_T / electron.m)
            electron_velocities = np.random.normal(
                loc=0.0, scale=sigma,
                size=velocities[2 * N:, :].shape) / np.sqrt(3)
            velocities[2 * N:, :] = electron_velocities

            # Get approximate time scale
            impact_parameter_ratio = 1.0  # Is not necessary for this analysis
            tau = sys.float_info.max
            for background_particle in [p_2]:
                reactant_collision = CoulombCollision(p_1, background_particle,
                                                      impact_parameter_ratio,
                                                      beam_velocity)
                relaxation = MaxwellianRelaxationProcess(reactant_collision)
                kinetic_frequency = relaxation.numerical_kinetic_loss_maxwellian_frequency(
                    N * w_b, T_b, beam_velocity)
                tau = min(tau, 1.0 / kinetic_frequency)
            dt = dt_factor * tau
            final_time = 4.0 * tau

            t, v_results = sim.run_sim(velocities, dt, final_time)

            # Get salient results
            velocities = np.mean(
                np.sqrt(v_results[:N, 0, :]**2 + v_results[:N, 1, :]**2 +
                        v_results[:N, 2, :]**2),
                axis=0)
            energies = 0.5 * p_1.m * np.mean(
                v_results[:N, 0, :]**2 + v_results[:N, 1, :]**2 +
                v_results[:N, 2, :]**2,
                axis=0)
            total_energies = 0.5 * p_1.m * np.sum(
                v_results[:N, 0, :]**2 + v_results[:N, 1, :]**2 +
                v_results[:N, 2, :]**2,
                axis=0)
            total_energy = N * w_1 * energy
            assert np.isclose(energies[0], energy,
                              atol=0.01 * energy), "{} != {}".format(
                                  energies[0], w_1 * N * energy)
            assert np.isclose(total_energies[0] * w_1,
                              total_energy,
                              atol=0.01 * total_energy), "{} != {}".format(
                                  total_energies[0], total_energy)
            velocity_results[name].append(velocities)
            energy_results[name].append(energies)

            velocities_p_2 = np.mean(np.sqrt(v_results[N:2 * N, 0, :]**2 +
                                             v_results[N:2 * N, 1, :]**2 +
                                             v_results[N:2 * N, 2, :]**2),
                                     axis=0)
            energies_p_2 = 0.5 * p_2.m * np.mean(
                v_results[N:2 * N, 0, :]**2 + v_results[N:2 * N, 1, :]**2 +
                v_results[N:2 * N, 2, :]**2,
                axis=0)
            velocities_electron = np.mean(np.sqrt(v_results[2 * N:, 0, :]**2 +
                                                  v_results[2 * N:, 1, :]**2 +
                                                  v_results[2 * N:, 2, :]**2),
                                          axis=0)
            energies_electron = 0.5 * electron.m * np.mean(
                v_results[2 * N:, 0, :]**2 + v_results[2 * N:, 1, :]**2 +
                v_results[2 * N:, 2, :]**2,
                axis=0)

            # Plot total velocity and energy results
            fig, ax = plt.subplots(2, sharex=True)

            ax[0].plot(t, velocities_p_2, label="ions")
            ax[0].plot(t, velocities_electron, label="electron")
            ax[0].plot(t, velocities, label="beam")
            ax[0].set_ylabel("Velocities [$ms^{-1}$]")
            ax[1].plot(t, energies_p_2, label="ions")
            ax[1].plot(t, energies_electron, label="electron")
            ax[1].plot(t, energies, label="beam")
            ax[1].plot(t,
                       energies + energies_electron + energies_p_2,
                       label="total")
            ax[1].set_ylabel("Energies [J]")
            ax[1].set_xlabel("Time [s]")

            plt.legend()
            plt.savefig(
                os.path.join(
                    res_dir,
                    "{}_{}_{}_{}_{}.png".format(name, N, dt_factor, n,
                                                reactant_name)))
            if plot_individual_sims:
                plt.show()
            plt.close()

            # Plot velocities
            fig, ax = plt.subplots(2, 2, figsize=(10, 10), sharex='col')

            v_x_1 = np.mean(v_results[:N, 0, :], axis=0)
            v_y_1 = np.mean(v_results[:N, 1, :], axis=0)
            v_z_1 = np.mean(v_results[:N, 2, :], axis=0)

            v_x_2 = np.mean(v_results[N:2 * N, 0, :], axis=0)
            v_y_2 = np.mean(v_results[N:2 * N, 1, :], axis=0)
            v_z_2 = np.mean(v_results[N:2 * N, 2, :], axis=0)

            v_x_3 = np.mean(v_results[2 * N:, 0, :], axis=0)
            v_y_3 = np.mean(v_results[2 * N:, 1, :], axis=0)
            v_z_3 = np.mean(v_results[2 * N:, 2, :], axis=0)

            ax[0, 0].plot(t, v_x_1, label="v_x_1")
            ax[0, 0].plot(t, v_x_2, label="v_x_2")
            ax[0, 0].plot(t, v_x_3, label="v_x_3")
            ax[0, 0].legend()

            ax[0, 1].plot(t, v_y_1, label="v_y_1")
            ax[0, 1].plot(t, v_y_2, label="v_y_2")
            ax[0, 1].plot(t, v_y_3, label="v_y_3")
            ax[0, 1].legend()

            ax[1, 0].plot(t, v_z_1, label="v_z_1")
            ax[1, 0].plot(t, v_z_2, label="v_z_2")
            ax[1, 0].plot(t, v_z_3, label="v_z_3")
            ax[1, 0].legend()

            ax[1, 1].plot(t, velocities, label="v_t_1")
            ax[1, 1].plot(t, velocities_p_2, label="v_t_2")
            ax[1, 1].plot(t, velocities_electron, label="v_t_3")
            ax[1, 1].legend()

            plt.savefig(
                os.path.join(
                    res_dir, "species_velocities_{}_{}_{}_{}_{}.png".format(
                        name, N, dt_factor, n, reactant_name)))
            if plot_individual_sims:
                plt.show()
            plt.close()

            # Plot temperatures
            fig, ax = plt.subplots(2,
                                   2,
                                   figsize=(10, 10),
                                   sharex='col',
                                   sharey='row')

            velocities_total = np.sqrt(v_results[:, 0, :]**2 +
                                       v_results[:, 1, :]**2 +
                                       v_results[:, 2, :]**2)

            T_x_1 = np.std(v_results[:N, 0, :], axis=0)**2 * p_1.m / (
                PhysicalConstants.boltzmann_constant)
            T_y_1 = np.std(v_results[:N, 1, :], axis=0)**2 * p_1.m / (
                PhysicalConstants.boltzmann_constant)
            T_z_1 = np.std(v_results[:N, 2, :], axis=0)**2 * p_1.m / (
                PhysicalConstants.boltzmann_constant)
            T_t_1 = np.std(velocities_total[:N, :], axis=0)**2 * p_1.m / (
                PhysicalConstants.boltzmann_constant)

            T_x_2 = np.std(v_results[N:2 * N, 0, :], axis=0)**2 * p_2.m / (
                PhysicalConstants.boltzmann_constant)
            T_y_2 = np.std(v_results[N:2 * N, 1, :], axis=0)**2 * p_2.m / (
                PhysicalConstants.boltzmann_constant)
            T_z_2 = np.std(v_results[N:2 * N, 2, :], axis=0)**2 * p_2.m / (
                PhysicalConstants.boltzmann_constant)
            T_t_2 = np.std(velocities_total[N:2 * N, :], axis=0)**2 * p_2.m / (
                PhysicalConstants.boltzmann_constant)

            T_x_3 = np.std(v_results[2 * N:, 0, :], axis=0)**2 * electron.m / (
                PhysicalConstants.boltzmann_constant)
            T_y_3 = np.std(v_results[2 * N:, 1, :], axis=0)**2 * electron.m / (
                PhysicalConstants.boltzmann_constant)
            T_z_3 = np.std(v_results[2 * N:, 2, :], axis=0)**2 * electron.m / (
                PhysicalConstants.boltzmann_constant)
            T_t_3 = np.std(velocities_total[2 * N:, :],
                           axis=0)**2 * electron.m / (
                               PhysicalConstants.boltzmann_constant)

            ax[0, 0].plot(t, T_x_1, label="T_x_1")
            ax[0, 0].plot(t, T_x_2, label="T_x_2")
            ax[0, 0].plot(t, T_x_3, label="T_x_3")
            ax[0, 0].legend()

            ax[0, 1].plot(t, T_y_1, label="T_y_1")
            ax[0, 1].plot(t, T_y_2, label="T_y_2")
            ax[0, 1].plot(t, T_y_3, label="T_y_3")
            ax[0, 1].legend()

            ax[1, 0].plot(t, T_z_1, label="T_z_1")
            ax[1, 0].plot(t, T_z_2, label="T_z_2")
            ax[1, 0].plot(t, T_z_3, label="T_z_3")
            ax[1, 0].legend()

            ax[1, 1].plot(t, T_t_1, label="T_t_1")
            ax[1, 1].plot(t, T_t_2, label="T_t_2")
            ax[1, 1].plot(t, T_t_3, label="T_t_3")
            ax[1, 1].legend()

            plt.savefig(
                os.path.join(
                    res_dir, "species_temperatures_{}_{}_{}_{}_{}.png".format(
                        name, N, dt_factor, n, reactant_name)))
            if plot_individual_sims:
                plt.show()
            plt.close()

            # Get energy half times - assuming beam has equilibrated at the end of the simulation
            energies = energies - energies[-1]
            energy_time_interpolator = interp1d(energies / energies[0], t)
            t_half = energy_time_interpolator(0.5)
            t_halves[name][i] = t_half
            t_theory[name][i] = tau

    # Plot results
    plt.figure()

    for name in names:
        plt.loglog(number_densities,
                   t_halves[name],
                   label="{}_Simulation".format(name))
        plt.loglog(number_densities,
                   t_theory[name],
                   label="{}_Theoretical values".format(name))

    plt.title("Comparison of thermalisation rates of products and reactants")
    plt.legend()
    plt.savefig(
        os.path.join(res_dir, "energy_half_times_{}".format(reactant_name)))
    plt.show()
def plot_collisional_angles():
    """
    Simple function to plot the scattering angles for different binary collisions at they vary with
    impact parameter ratio  
    """
    impact_parameter_ratios = np.linspace(0.1, 10.0, 1000)

    deuterium = ChargedParticle(2.01410178 * 1.66054e-27,
                                PhysicalConstants.electron_charge)
    electron = ChargedParticle(9.014e-31, -PhysicalConstants.electron_charge)

    temperature = 11600.0  # 1eV taken as base case
    electron_velocity = np.sqrt(PhysicalConstants.boltzmann_constant *
                                temperature / electron.m)
    deuterium_velocity = np.sqrt(PhysicalConstants.boltzmann_constant *
                                 temperature / deuterium.m)
    print("Electron Thermal Velocity at {}K: {}".format(
        temperature, electron_velocity))
    print("Ion Thermal Velocity at {}K: {}".format(temperature,
                                                   deuterium_velocity))

    collision_pairs = [
        ["Deuterium-Deuterium", deuterium, deuterium, deuterium_velocity],
        ["Electron-Deuterium", electron, deuterium, electron_velocity],
        ["Electron-Electron", electron, electron, electron_velocity]
    ]
    for j, pair in enumerate(collision_pairs):
        name = pair[0]
        p_1 = pair[1]
        p_2 = pair[2]
        velocity = pair[3]

        chi = np.zeros(impact_parameter_ratios.shape)
        cross_section = np.zeros(impact_parameter_ratios.shape)
        for i, impact_parameter_ratio in enumerate(impact_parameter_ratios):
            collision = CoulombCollision(p_1, p_2, impact_parameter_ratio,
                                         velocity)

            chi[i] = collision.chi
            cross_section[i] = collision.differential_cross_section

        collision = CoulombCollision(p_1, p_2, 1.0, velocity)
        b_90 = collision.b_90

        print("Impact Parameter for {} Collision: {}".format(name, b_90))

        # Convert chi to degrees
        chi /= np.pi
        chi *= 180.0

        # Plot chi vs.impact parameter
        fig, ax = plt.subplots(2, figsize=(10, 10))

        ax[0].plot(impact_parameter_ratios, chi)
        ax[0].set_title(
            "Scattering angle vs Impact parameter for {} Collision".format(
                name))
        ax[0].set_xlabel("Impact parameter ratio (b / b_90)")
        ax[0].set_ylabel("Scattering angle (degrees)")

        ax[1].semilogy(chi, cross_section)
        ax[1].set_title(
            "Cross Section vs Impact parameter for {} Collision".format(name))
        ax[1].set_xlabel("Impact parameter ratio (b / b_90)")
        ax[1].set_ylabel("Number of collisions at given solid angle")

        plt.show()
예제 #15
0
def run_electron_beam_into_stationary_target_sim(sim_type):
    """
    Run simulation of velocity relaxation of electron beam to stationary
    background species
    """
    p_1 = ChargedParticle(PhysicalConstants.electron_mass,
                          -PhysicalConstants.electron_charge)
    p_2 = ChargedParticle(1e31, -PhysicalConstants.electron_charge)
    w_1 = 1
    w_2 = 10
    n = int(1e3)
    beam_velocity = 1.0

    if sim_type == "Abe":
        sim = AbeCoulombCollisionModel(n,
                                       p_1,
                                       w_1=w_1,
                                       N_2=n,
                                       particle_2=p_2,
                                       w_2=w_2,
                                       freeze_species_2=True)
    elif sim_type == "Nanbu":
        sim = NanbuCollisionModel(np.asarray([n, n]),
                                  np.asarray([p_1, p_2]),
                                  np.asarray([w_1, w_2]),
                                  coulomb_logarithm=10.0,
                                  frozen_species=[False, True])
    else:
        raise ValueError("Invalid sim type")

    velocities = np.zeros((2 * n, 3))
    velocities[:n, :] = np.asarray([0.0, 0.0, beam_velocity])
    velocities[n:] = np.asarray([0.0, 0.0, 0.0])

    tau = get_relaxation_time(p_1, n * w_2, beam_velocity)
    dt = 0.05 * tau
    final_time = 3.0 * tau

    t, v_results = sim.run_sim(velocities, dt, final_time)

    t /= tau
    v_z_estimate = 1.0 - t
    v_ort_estimate = 2 * t

    fig, ax = plt.subplots(2, figsize=(10, 10))

    ax[0].plot(
        t,
        np.mean(v_results[:n, 0, :]**2 + v_results[:n, 1, :]**2, axis=0) /
        beam_velocity**2,
        label="<v_ort^2>")
    ax[0].plot(t, np.mean(v_results[:n, 2, :], axis=0), label="<v_z>")
    ax[0].plot(t, v_z_estimate, linestyle="--", label="<v_z_estimate>")
    ax[0].plot(t, v_ort_estimate, linestyle="--", label="<v_ort_estimate>")
    ax[0].set_ylim([0.0, 1.0])
    ax[0].set_xlim([0.0, t[-1]])
    ax[0].legend()
    ax[0].set_xlabel("Timestep")
    ax[0].set_ylabel("Velocities ms-1")
    ax[0].set_title("Beam Velocities")

    ax[1].plot(t, np.mean(v_results[n:, 0, :], axis=0), label="<v_x>")
    ax[1].plot(t, np.mean(v_results[n:, 1, :], axis=0), label="<v_y>")
    ax[1].plot(t, np.mean(v_results[n:, 2, :], axis=0), label="<v_z>")
    ax[1].legend()
    ax[1].set_xlabel("Timestep")
    ax[1].set_ylabel("Velocities ms-1")
    ax[1].set_title("Background Velocities")

    plt.show()

    return t, v_results
예제 #16
0
def run_1d_electrostatic_well(radius, num_particles=int(1e4)):
    """
    Function to run simulation - for this simulation, in a spherically symmetric potential well

    radius: size of potential well
    """
    # Define particles
    number_density = 1e12
    charge = 1.602e-19
    weight = int(number_density / num_particles)
    pic_particle = PICParticle(3.344496935079999e-27 * weight, charge * weight,
                               np.asarray([radius, 0.0, 0.0]),
                               np.asarray([0.0, 0.0, 0.0]))
    collision_particle = ChargedParticle(3.344496935079999e-27 * weight,
                                         charge * weight)

    # Define fields
    electron_charge_density = 1e20 * PhysicalConstants.electron_charge

    def e_field(x):
        E_field = np.zeros(x.shape)
        for i in range(x.shape[0]):
            r = magnitude(x[i])
            if r <= radius:
                e_field = electron_charge_density * r / (
                    3.0 * PhysicalConstants.epsilon_0)
            else:
                e_field = electron_charge_density * radius**3 / (
                    3.0 * PhysicalConstants.epsilon_0 * r**2)

            e_field *= -normalise(x[i])
            E_field[i, :] = e_field

        return E_field

    # Define time step and final time
    total_V = radius**3 * electron_charge_density
    total_V /= 2.0 * PhysicalConstants.epsilon_0 * radius
    energy = total_V * pic_particle.charge
    velocity = np.sqrt(2 * energy / pic_particle.mass)
    dt = 0.01 * radius / velocity
    final_time = 50.0 * radius / velocity
    num_steps = int(final_time / dt)

    # Define collision model
    temperature = 298.3
    thermal_velocity = np.sqrt(PhysicalConstants.boltzmann_constant *
                               temperature / pic_particle.mass)
    c = CoulombCollision(collision_particle, collision_particle, 1.0, velocity)
    debye_length = PhysicalConstants.epsilon_0 * temperature
    debye_length /= number_density * PhysicalConstants.electron_charge**2
    debye_length = np.sqrt(debye_length)
    coulomb_logarithm = np.log(debye_length / c.b_90)
    r = RelaxationProcess(c)
    v_K = r.kinetic_loss_stationary_frequency(number_density, temperature,
                                              velocity)
    print("Thermal velocity: {}".format(thermal_velocity / 3e8))
    print("Peak velocity: {}".format(velocity / 3e8))
    print("Debye Length: {}".format(debye_length))
    print("Coulomb Logarithm: {}".format(coulomb_logarithm))
    print("Kinetic Loss Time: {}".format(1.0 / v_K))
    print("Simulation Time Step: {}".format(dt))
    assert dt < 0.01 * 1.0 / v_K
    collision_model = AbeCoulombCollisionModel(
        num_particles,
        collision_particle,
        weight,
        coulomb_logarithm=coulomb_logarithm)

    # Set up initial conditions
    np.random.seed(1)
    X = np.zeros((num_particles, 3))
    for i in range(num_particles):
        x_theta = np.random.uniform(0.0, 2 * np.pi)
        y_theta = np.random.uniform(0.0, 2 * np.pi)
        z_theta = np.random.uniform(0.0, 2 * np.pi)

        X[i, :] = rotate_3d(np.asarray([1.0, 0.0, 0.0]),
                            np.asarray([x_theta, y_theta, z_theta]))
    V = np.random.normal(0.0, thermal_velocity, size=X.shape)
    V = np.zeros(X.shape)

    # Run simulation
    times = np.linspace(0.0, dt * num_steps, num_steps)
    positions = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    velocities = np.zeros((times.shape[0], X.shape[0], X.shape[1]))
    for i, t in enumerate(times):
        # Set first position and velocity
        if i == 0:
            positions[i, :, :] = X
            velocities[i, :, :] = V
            continue

        # Update position and velocity due to field
        dt = times[i] - times[i - 1]
        x = X + V + 0.5 * dt if i == 1 else X + V * dt
        E = e_field(x)
        v = V + E * pic_particle.charge / pic_particle.mass * dt

        # Update velocity due to collisions
        new_v = collision_model.single_time_step(v, dt)

        positions[i, :, :] = x
        velocities[i, :, :] = new_v
        X = x
        V = new_v

        print("Timestep: {}".format(i))

    return times, positions, velocities
예제 #17
0
    return 1.0 / stationary_frequency, 1.0 / theoretical_maxwellian_frequency, 1.0 / numerical_maxwellian_frequency, 1.0 / monte_carlo_maxwellian_frequency


if __name__ == '__main__':
    # Define parameter space of scan
    n = np.logspace(20, 30, 10)
    T_keV = 10.0
    T_K = T_keV * 1e3 * UnitConversions.eV_to_K
    e = 1.5 * PhysicalConstants.boltzmann_constant * T_K

    # Set up reaction
    reaction_name = "pB"
    if reaction_name == "DD":
        reaction = DDReaction()
        deuterium = ChargedParticle(2.01410178 * UnitConversions.amu_to_kg,
                                    PhysicalConstants.electron_charge)

        beam_velocity = np.sqrt(2 * e / deuterium.m)
        beam_species = deuterium
        background_species = deuterium
    elif reaction_name == "DT":
        reaction = DTReaction()
        deuterium_tritium = ChargedParticle(
            5.0064125184e-27, 2 * PhysicalConstants.electron_charge)

        beam_velocity = np.sqrt(2 * e / deuterium_tritium.m)
        beam_species = deuterium_tritium
        background_species = deuterium_tritium
    elif reaction_name == "pB":
        reaction = pBReaction()
        proton_boron = ChargedParticle((10.81 + 1) * UnitConversions.amu_to_kg,