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))
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
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()
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()
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 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
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 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 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()
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, PhysicalConstants.electron_charge * 12) beam_velocity = np.sqrt(2 * e / proton_boron.m) beam_species = proton_boron background_species = proton_boron else: raise ValueError("Name is invalid!") collision = CoulombCollision(beam_species, background_species, 1.0, beam_velocity) relaxation_process = MaxwellianRelaxationProcess(collision) # Get thermalisation rate t_stationary, t_maxwellian, t_numerical, t_monte = get_thermalisation_rate( n, T_K, relaxation_process, beam_velocity, reaction_name=reaction_name, force_calculation=True) # Get fusion reaction rate t_fus = get_fusion_timescale(n, T_keV, reaction) fig, ax = plt.subplots(2, figsize=(10, 10))