def check_timestep(pos, vel, B, E, q_dens, Ie, W_elec, Ib, W_mag, B_center, Ep, Bp, v_prime, S, T, temp_N,\ qq, DT, max_inc, part_save_iter, field_save_iter, idx, B_damping_array, E_damping_array): ''' Evaluates all the things that could cause a violation of the timestep: - Magnetic field dispersion (switchable in param file since this can be tiny) - Gyromotion resolution - Ion velocity (Don't cross more than half a cell in a timestep) - Electric field acceleration When a violating condition found, velocity is advanced by 0.5DT (since this happens at the top of a loop anyway). The assumption is that the timestep isn't violated by enough to cause instant instability (each criteria should have a little give), which should be valid except under extreme instability. The timestep is then halved and all time-dependent counters and quantities are doubled. Velocity is then retarded back half a timestep to de-sync back into a leapfrog scheme. ''' ND = const.ND NX = const.NX interpolate_edges_to_center(B, B_center) B_magnitude = np.sqrt(B_center[ND:ND + NX + 1, 0]**2 + B_center[ND:ND + NX + 1, 1]**2 + B_center[ND:ND + NX + 1, 2]**2) gyfreq = const.qm_ratios.max() * B_magnitude.max() ion_ts = const.orbit_res / gyfreq if E[:, 0].max() != 0: elecfreq = const.qm_ratios.max() * ( np.abs(E[:, 0] / np.abs(vel).max()).max() ) # Electron acceleration "frequency" Eacc_ts = const.freq_res / elecfreq else: Eacc_ts = ion_ts vel_ts = 0.60 * const.dx / np.abs(vel[0, :]).max( ) # Timestep to satisfy CFL condition: Fastest particle doesn't traverse more than 'half' a cell in one time step DT_part = min(Eacc_ts, vel_ts, ion_ts) # Smallest of the allowable timesteps if DT_part < 0.9 * DT: particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, Ep, Bp, B, E, v_prime, S, T, temp_N, 0.5 * DT) # Re-sync vel/pos DT *= 0.5 max_inc *= 2 qq *= 2 field_save_iter *= 2 part_save_iter *= 2 particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, Ep, Bp, B, E, v_prime, S, T, temp_N, -0.5 * DT) # De-sync vel/pos print('Timestep halved. Syncing particle velocity...') init.set_damping_array(B_damping_array, E_damping_array, DT) return qq, DT, max_inc, part_save_iter, field_save_iter, B_damping_array, E_damping_array
def test_particle_orbit(): def position_update(pos, vel, DT): for ii in range(3): pos[ii] += vel[ii, 0] * dt return pos NX = 64 B0 = 0.01 v0 = 1e5 mi = 1.672622e-27 # Mass of proton (kg) qi = 1.602177e-19 # Elementary charge (C) resolution = 10 num_rev = 5000 maxtime = int(resolution * num_rev) gyperiod = (2 * np.pi * mi) / (qi * B0) dt = gyperiod / resolution B = np.zeros((NX + 3, 3), dtype=np.float64) E = np.zeros((NX + 3, 3), dtype=np.float64) B[:, 0] += B0 Ie = np.array([NX // 2]) Ib = np.array([NX // 2]) We = np.array([0., 1., 0.]).reshape((3, 1)) Wb = np.array([0., 1., 0.]).reshape((3, 1)) idx = np.array([0]) vp = np.array([[0.], [v0], [0.]]) rL = (mi * vp[1][0]) / (qi * B0) xp = np.array([-rL, 0., 0.]) print('\nNumber of revolutions: {}'.format(num_rev)) print('Points per revolution: {}'.format(resolution)) print('Total number of points: {}'.format(maxtime)) print('\nGyroradius: {}m'.format(rL)) print('Gyroperiod: {}s'.format(round(gyperiod, 2))) print('Timestep: {}s'.format(round(dt, 3))) particles.velocity_update(vp, Ie, We, Ib, Wb, idx, B, E, -0.5 * dt) xy = np.zeros((maxtime + 1, 2)) for ii in range(maxtime + 1): xy[ii, 0] = xp[1] xy[ii, 1] = xp[2] position_update(xp, vp, dt) particles.velocity_update(vp, Ie, We, Ib, Wb, idx, B, E, dt) print(xp) print(vp) #plt.plot(xy[:, 0], xy[:, 1]) #plt.axis('equal') return
def check_timestep(pos, vel, B, E, q_dens, Ie, W_elec, Ib, W_mag, B_cent, \ qq, DT, max_inc, part_save_iter, field_save_iter, idx): interpolate_to_center_cspline3D(B, B_cent) B_tot = np.sqrt(B_cent[:, 0] ** 2 + B_cent[:, 1] ** 2 + B_cent[:, 2] ** 2) dispfreq = ((np.pi / const.dx) ** 2) * (B_tot / (const.mu0 * q_dens)).max() # Dispersion frequency local_gyfreq = const.high_rat * np.abs(B_tot).max() / (2 * np.pi) ion_ts = const.orbit_res * 1./local_gyfreq if E[:, 0].max() != 0: if vel.max() != 0: elecfreq = const.high_rat*(np.abs(E[:, 0] / vel.max()).max()) # Electron acceleration "frequency" Eacc_ts = const.freq_res / elecfreq else: Eacc_ts = ion_ts else: Eacc_ts = ion_ts if const.account_for_dispersion == True: disp_ts = const.dispersion_allowance * const.freq_res / dispfreq # Making this a little bigger so it doesn't wreck everything else: disp_ts = ion_ts vel_ts = 0.80 * const.dx / vel[0, :].max() # Timestep to satisfy CFL condition: Fastest particle doesn't traverse more than 'half' a cell in one time step DT_part = min(Eacc_ts, vel_ts, ion_ts, disp_ts) # Smallest of the allowable timesteps if DT_part < 0.9*DT: particles.velocity_update(vel, Ie, W_elec, Ib, W_mag, idx, B, E, 0.5*DT) # Re-sync vel/pos DT *= 0.5 max_inc *= 2 qq *= 2 field_save_iter *= 2 part_save_iter *= 2 particles.velocity_update(vel, Ie, W_elec, Ib, W_mag, idx, B, E, -0.5*DT) # De-sync vel/pos print('Timestep halved. Syncing particle velocity...') elif DT_part >= 4.0*DT and qq%2 == 0 and part_save_iter%2 == 0 and field_save_iter%2 == 0 and max_inc%2 == 0: particles.velocity_update(vel, Ie, W_elec, Ib, W_mag, idx, B, E, 0.5*DT) # Re-sync vel/pos DT *= 2.0 max_inc //= 2 qq //= 2 field_save_iter //= 2 part_save_iter //= 2 particles.velocity_update(vel, Ie, W_elec, Ib, W_mag, idx, B, E, -0.5*DT) # De-sync vel/pos print('Timestep Doubled. Syncing particle velocity...') return qq, DT, max_inc, part_save_iter, field_save_iter
def initialize(): pos, vel, Ie, W_elec, idx = init.initialize_particles() B, E_int = init.initialize_fields() DT, max_inc, data_iter = aux.set_timestep(vel) if data_iter == 0: data_iter = max_inc q_dens, Ji = sources.collect_moments(vel, Ie, W_elec, idx) E_int, Ve, Te = fields.calculate_E(B, Ji, q_dens) vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E_int, -0.5 * DT) return pos, vel, Ie, W_elec, idx, B, E_int, q_dens, Ji, Ve, Te, DT, max_inc, data_iter
= init.set_timestep(vel, Te0) fields.calculate_E(B, Ji, q_dens, E_int, Ve, Te, Te0, temp3De, temp3Db, temp1D, E_damping_array, 0, DT, 0) print('Saving initial conditions...') if save_particles == 1: save.save_particle_data(0, DT, part_save_iter, 0, pos, vel, idx) if save_fields == 1: save.save_field_data(0, DT, field_save_iter, 0, Ji, E_int,\ B, Ve, Te, q_dens, B_damping_array, E_damping_array) # Retard velocity print('Retarding velocity...') particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, Ep, Bp, B, E_int, v_prime, S, T, temp_N, -0.5 * DT) qq = 1 sim_time = DT print('Starting main loop...') while qq < max_inc: qq, DT, max_inc, part_save_iter, field_save_iter = \ aux.main_loop(pos, vel, idx, Ie, W_elec, Ib, W_mag, Ep, Bp, v_prime, S, T, temp_N,\ B, E_int, E_half, q_dens, q_dens_adv, Ji, ni, nu, mp_flux, \ Ve, Te, Te0, temp3De, temp3Db, temp1D, old_particles, old_fields, \ B_damping_array, E_damping_array, qq, DT, max_inc, part_save_iter, field_save_iter) if qq % part_save_iter == 0 and save_particles == 1: save.save_particle_data(sim_time, DT, part_save_iter, qq, pos, vel, idx)
def check_timestep(qq, DT, pos, vel, B, E, dns, Ie, W_elec, max_inc, data_iter, plot_iter, subcycles, idx): max_Vx = np.max(vel[0, :]) max_V = np.max(vel) k_max = np.pi / const.dx B_cent = interpolate_to_center_cspline3D(B) B_tot = np.sqrt(B_cent[:, 0]**2 + B_cent[:, 1]**2 + B_cent[:, 2]**2) high_rat = (const.charge / const.mass).max() gyfreq = high_rat * np.abs(B_tot).max() / (2 * np.pi) ion_ts = const.orbit_res * 1. / gyfreq if E.max() != 0: elecfreq = high_rat * max(abs( E[:, 0] / max_V)) # Electron acceleration "frequency" Eacc_ts = const.freq_res / elecfreq else: Eacc_ts = ion_ts vel_ts = 0.75 * const.dx / max_Vx # Timestep to satisfy CFL condition: Fastest particle doesn't traverse more than 'half' a cell in one time step DT_part = min(Eacc_ts, vel_ts, ion_ts) # Smallest of the particle conditions if DT_part < 0.9 * DT: vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E, 0.5 * DT) # Re-sync vel/pos DT *= 0.5 max_inc *= 2 qq *= 2 vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E, -0.5 * DT) # De-sync vel/pos if plot_iter != None: plot_iter *= 2 if data_iter != None: data_iter *= 2 print('Timestep halved. Syncing particle velocity with DT = {}'.format( DT)) elif DT_part >= 4.0 * DT and qq % 2 == 0: vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E, 0.5 * DT) # Re-sync vel/pos DT *= 2.0 max_inc = (max_inc + 1) / 2 qq /= 2 vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E, -0.5 * DT) # De-sync vel/pos if plot_iter == None or plot_iter == 1: plot_iter = 1 else: plot_iter /= 2 if data_iter == None or data_iter == 1: data_iter = 1 else: data_iter /= 2 print( 'Timestep Doubled. Syncing particle velocity with DT = {}'.format( DT)) if const.adaptive_subcycling == True: dispfreq = (k_max** 2) * (B_tot / (const.mu0 * dns)).max() # Dispersion frequency dt_sc = const.freq_res * 1. / dispfreq new_subcycles = int(DT / dt_sc + 1) if subcycles < 0.75 * new_subcycles: subcycles *= 2 print('Number of subcycles per timestep doubled to {}'.format( subcycles)) if subcycles > 3.0 * new_subcycles: subcycles = (subcycles + 1) / 2 print('Number of subcycles per timestep halved to {}'.format( subcycles)) if subcycles > const.subcycle_max: const.adaptive_subcycling = False subcycles = const.subcycle_max else: pass return pos, vel, qq, DT, max_inc, data_iter, plot_iter, subcycles
qq, DT, part, B, E, dns_int, max_inc, data_dump_iter, plot_dump_iter) if change_flag == 1: print( 'Timestep halved. Syncing particle velocity/position with DT = {}' .format(DT)) part, dns_int, dns_half, J_plus, J_minus, G, L = sources.init_collect_moments( part, 0.5 * DT) B = fields.cyclic_leapfrog(B, dns_int, J_minus, DT) E = fields.calculate_E(B, J_minus, dns_half) J = sources.push_current(J_plus, E, B, L, G, DT) E = fields.calculate_E(B, J, dns_half) part = particles.velocity_update(part, B, E, DT) part, dns_int, J_plus, J_minus, G, L = sources.collect_moments( part, DT) dns_int = 0.5 * (dns_int + dns_half) J = 0.5 * (J_plus + J_minus) B = fields.cyclic_leapfrog(B, dns_int, J, DT) if qq % data_dump_iter == 0 and generate_data == 1: # Save data, if flagged pas.save_data(DT, data_dump_iter, qq, part, J, E, B, dns_int) if qq % plot_dump_iter == 0 and generate_plots == 1: # Generate and save plots, if flagged pas.create_figure_and_save(part, J, B, dns_int, qq, DT, plot_dump_iter)
DT, max_inc, part_save_iter, field_save_iter = init.set_timestep(vel) # Collect initial moments and save initial state sources.collect_moments(vel, Ie, W_elec, idx, q_dens, Ji, ni, nu, temp1D) fields.calculate_E(B, Ji, q_dens, E_int, Ve, Te, temp3D, temp3D2, temp1D) if save_particles == 1: save.save_particle_data(DT, part_save_iter, 0, pos, vel) if save_fields == 1: save.save_field_data(DT, field_save_iter, 0, Ji, E_int, B, Ve, Te, q_dens) particles.assign_weighting_TSC(pos, Ib, W_mag, E_nodes=False) particles.velocity_update(vel, Ie, W_elec, Ib, W_mag, idx, B, E_int, -0.5 * DT) qq = 1 print('Starting main loop...') while qq < max_inc: qq, DT, max_inc, part_save_iter, field_save_iter = \ aux.main_loop(pos, vel, idx, Ie, W_elec, Ib, W_mag, \ B, E_int, E_half, q_dens, q_dens_adv, Ji, ni, nu, \ Ve, Te, temp3D, temp3D2, temp1D, old_particles, old_fields,\ qq, DT, max_inc, part_save_iter, field_save_iter) if qq % part_save_iter == 0 and save_particles == 1: save.save_particle_data(DT, part_save_iter, qq, pos, vel) if qq % field_save_iter == 0 and save_fields == 1: save.save_field_data(DT, field_save_iter, qq, Ji, E_int, B, Ve, Te,
if __name__ == '__main__': start_time = timer() pos, vel, Ie, W_elec, idx = init.initialize_particles() B, E_int = init.initialize_fields() DT, max_inc, data_iter = aux.set_timestep(vel) print 'Timestep: %.4fs, %d iterations total' % (DT, max_inc) if generate_data == 1: save.store_run_parameters(DT, data_iter) q_dens, Ji = sources.collect_moments(vel, Ie, W_elec, idx) E_int, Ve, Te = fields.calculate_E(B, Ji, q_dens) vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E_int, -0.5 * DT) qq = 0 while qq < max_inc: # Check timestep vel, qq, DT, max_inc, data_iter, ch_flag \ = aux.check_timestep(qq, DT, pos, vel, B, E_int, q_dens, Ie, W_elec, max_inc, data_iter, idx) if ch_flag == 1: print 'Timestep halved. Syncing particle velocity with DT = {}'.format( DT) elif ch_flag == 2: print 'Timestep Doubled. Syncing particle velocity with DT = {}'.format( DT) # Main loop
pos, Ie, W_elec, dns_int, dns_half, J_plus, J_minus, G, L = sources.init_collect_moments(pos, vel, Ie, W_elec, idx, 0.5*DT) elif change_flag == 2: print('Timestep doubled. Syncing particle velocity/position with DT = {}'.format(DT)) pos, Ie, W_elec, dns_int, dns_half, J_plus, J_minus, G, L = sources.init_collect_moments(pos, vel, Ie, W_elec, idx, 0.5*DT) ####################### ###### MAIN LOOP ###### ####################### B = fields.cyclic_leapfrog(B, dns_int, J_minus, DT, subcycles) E, Ve, Te = fields.calculate_E(B, J_minus, dns_half) J = sources.push_current(J_plus, E, B, L, G, DT) E, Ve, Te = fields.calculate_E(B, J, dns_half) vel = particles.velocity_update(pos, vel, Ie, W_elec, idx, B, E, J, DT) # Store pc(1/2) here while pc(3/2) is collected dns_int = dns_half pos, Ie, W_elec, dns_half, J_plus, J_minus, G, L = sources.collect_moments(pos, vel, Ie, W_elec, idx, DT) dns_int = 0.5 * (dns_int + dns_half) J = 0.5 * (J_plus + J_minus) B = fields.cyclic_leapfrog(B, dns_int, J, DT, subcycles) E, Ve, Te = fields.calculate_E(B, J, dns_int) # This one's just for output ######################## ##### OUTPUT DATA ##### ########################
def check_timestep(pos, vel, B, E, q_dens, Ie, W_elec, Ib, W_mag, B_center, \ qq, DT, max_inc, part_save_iter, field_save_iter, idx, damping_array): ''' Evaluates all the things that could cause a violation of the timestep: - Magnetic field dispersion (switchable in param file since this can be tiny) - Gyromotion resolution - Ion velocity (Don't cross more than half a cell in a timestep) - Electric field acceleration When a violating condition found, velocity is advanced by 0.5DT (since this happens at the top of a loop anyway). The assumption is that the timestep isn't violated by enough to cause instant instability (each criteria should have a little give), which should be valid except under extreme instability. The timestep is then halved and all time-dependent counters and quantities are doubled. Velocity is then retarded back half a timestep to de-sync back into a leapfrog scheme. Also evaluates if a timestep is unnneccessarily too small, which can sometimes happen after wave-particle interactions are complete and energetic particles are slower. This criteria is higher in order to provide a little hysteresis and prevent constantly switching timesteps. ''' interpolate_edges_to_center(B, B_center) B_magnitude = np.sqrt(B_center[ND:ND + NX + 1, 0]**2 + B_center[ND:ND + NX + 1, 1]**2 + B_center[ND:ND + NX + 1, 2]**2) gyfreq = qm_ratios.max() * B_magnitude.max() ion_ts = orbit_res / gyfreq if E[:, 0].max() != 0: elecfreq = qm_ratios.max() * (np.abs( E[:, 0] / np.abs(vel).max()).max() ) # Electron acceleration "frequency" Eacc_ts = freq_res / elecfreq else: Eacc_ts = ion_ts if account_for_dispersion == True: B_tot = np.sqrt(B_center[:, 0]**2 + B_center[:, 1]**2 + B_center[:, 2]**2) dispfreq = ( (np.pi / dx)**2) * (B_tot / (mu0 * q_dens)).max() # Dispersion frequency disp_ts = dispersion_allowance * freq_res / dispfreq # Making this a little bigger so it doesn't wreck everything else: disp_ts = ion_ts vel_ts = 0.60 * dx / np.abs(vel[0, :]).max( ) # Timestep to satisfy CFL condition: Fastest particle doesn't traverse more than 'half' a cell in one time step DT_part = min(Eacc_ts, vel_ts, ion_ts, disp_ts) # Smallest of the allowable timesteps if DT_part < 0.9 * DT: particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, B, E, 0.5 * DT) # Re-sync vel/pos DT *= 0.5 max_inc *= 2 qq *= 2 field_save_iter *= 2 part_save_iter *= 2 particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, B, E, -0.5 * DT) # De-sync vel/pos print('Timestep halved. Syncing particle velocity...') init.set_damping_array(damping_array, DT) # ============================================================================= # elif DT_part >= 4.0*DT and qq%2 == 0 and part_save_iter%2 == 0 and field_save_iter%2 == 0 and max_inc%2 == 0: # particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, B, E, 0.5*DT) # Re-sync vel/pos # DT *= 2.0 # max_inc //= 2 # qq //= 2 # # field_save_iter //= 2 # part_save_iter //= 2 # # particles.velocity_update(pos, vel, Ie, W_elec, Ib, W_mag, idx, B, E, -0.5*DT) # De-sync vel/pos # print('Timestep Doubled. Syncing particle velocity...') # init.set_damping_array(damping_array, DT) # ============================================================================= return qq, DT, max_inc, part_save_iter, field_save_iter, damping_array