def initialize_f(q1, q2, p1, p2, p3, params): m = params.mass_particle k = params.boltzmann_constant rho = af.select(af.abs(q2) > 0.25, q1**0, 2) try: p1_bulk = af.select( af.abs(q2) > 0.25, -0.5 - 0.01 * (af.randu(1, q1.shape[1], q2.shape[2], dtype=af.Dtype.f64) - 0.5), +0.5 + 0.01 * (af.randu(1, q1.shape[1], q2.shape[2], dtype=af.Dtype.f64) - 0.5)) p2_bulk = 0.01 * ( af.randu(1, q1.shape[1], q2.shape[2], dtype=af.Dtype.f64) - 0.5) except: p1_bulk = af.select( af.abs(q2) > 0.25, -0.5 - 0.01 * (af.randu(q1.shape[0], q2.shape[1], dtype=af.Dtype.f64) - 0.5), +0.5 + 0.01 * (af.randu(q1.shape[0], q2.shape[1], dtype=af.Dtype.f64) - 0.5)) p2_bulk = 0.01 * ( af.randu(q1.shape[0], q2.shape[1], dtype=af.Dtype.f64) - 0.5) T = (2.5 / rho) f = rho * (m / (2 * np.pi * k * T))**(3 / 2) \ * af.exp(-m * (p1 - p1_bulk)**2 / (2 * k * T)) \ * af.exp(-m * (p2 - p2_bulk)**2 / (2 * k * T)) \ * af.exp(-m * p3**2 / (2 * k * T)) af.eval(f) return (f)
def initialize_f(q1, q2, v1, v2, v3, params): m = params.mass k = params.boltzmann_constant n = af.select(af.abs(q2)>0.25, q1**0, 2) # Random Numbers under to seed the instability: seeding_velocities = 0.01 * (af.randu(1, 1, q1.shape[2], q1.shape[3], dtype = af.Dtype.f64 ) - 0.5 ) v1_bulk = af.select(af.abs(q2)>0.25, -0.5 - seeding_velocities, +0.5 + seeding_velocities ) v2_bulk = seeding_velocities T = (2.5 / n) f = n * (m / (2 * np.pi * k * T))**(3 / 2) \ * af.exp(-m * (v1 - v1_bulk)**2 / (2 * k * T)) \ * af.exp(-m * (v2 - v2_bulk)**2 / (2 * k * T)) \ * af.exp(-m * v3**2 / (2 * k * T)) af.eval(f) return (f)
def test_dirichlet(): obj = test('dirichlet', 'dirichlet') obj._A_q1, obj._A_q2 = af.Array([100]), af.Array([100]) obj._A_q1 = af.tile(obj._A_q1, 1, 1, obj.q1_center.shape[2]) obj._A_q2 = af.tile(obj._A_q2, 1, 1, obj.q1_center.shape[2]) obj.dt = 0.001 obj.f = af.constant(0, obj.q1_center.shape[0], obj.q1_center.shape[1], obj.q1_center.shape[2], dtype=af.Dtype.f64) apply_bcs_f(obj) expected = af.constant(0, obj.q1_center.shape[0], obj.q1_center.shape[1], obj.q1_center.shape[2], dtype=af.Dtype.f64) N_g = obj.N_ghost # Only ingoing characteristics should be affected: expected[:N_g] = af.select(obj.q1_center < obj.q1_start, 1, expected)[:N_g] expected[:, :N_g] = af.select(obj.q2_center < obj.q2_start, 2, expected)[:, :N_g] assert (af.max(af.abs(obj.f[:, N_g:-N_g] - expected[:, N_g:-N_g])) < 5e-14) assert (af.max(af.abs(obj.f[N_g:-N_g, :] - expected[N_g:-N_g, :])) < 5e-14)
def initialize_f(q1, q2, p1, p2, p3, params): m = params.mass k = params.boltzmann_constant rho = af.select(q1<0.5, q1**0, 0.125) T = af.select(q1<0.5, q1**0, 0.8) f = rho * af.sqrt(m / (2 * np.pi * k * T))**3 \ * af.exp(-m * p1**2 / (2 * k * T)) \ * af.exp(-m * p2**2 / (2 * k * T)) \ * af.exp(-m * p3**2 / (2 * k * T)) af.eval(f) return (f)
def initialize_f(q1, q2, v1, v2, v3, params): m = params.mass k = params.boltzmann_constant n = af.select(q1 < 0.5, q1**0, 0.125) T = af.select(q1 < 0.5, q1**0, 0.8) f = n * (m / (2 * np.pi * k * T))**(3 / 2) \ * af.exp(-m * v1**2 / (2 * k * T)) \ * af.exp(-m * v2**2 / (2 * k * T)) \ * af.exp(-m * v3**2 / (2 * k * T)) af.eval(f) return (f)
def initialize_f(q1, q2, v1, v2, v3, params): m = params.mass k = params.boltzmann_constant # Calculating the perturbed density: n = af.select(q1 + q2 > 0.15, q1**0, 0.125) T = af.select(q1 + q2 > 0.15, q1**0, 0.373) f = n * (m / (2 * np.pi * k * T)) \ * af.exp(-m * v1**2 / (2 * k * T)) \ * af.exp(-m * v2**2 / (2 * k * T)) af.eval(f) return (f)
def initialize_f(q1, q2, p1, p2, p3, params): m = params.mass_particle k = params.boltzmann_constant rho = af.select(af.abs(q2) > 0.25, q1**0, 2) # Seeding the instability p1_bulk = af.reorder(p1_bulk, 1, 2, 0, 3) p1_bulk += af.to_array( np.random.rand(1, q1.shape[1], q1.shape[2]) * np.random.choice([-1, 1], size=(1, q1.shape[1], q1.shape[2]))) * 0.005 p2_bulk = af.to_array( np.random.rand(1, q1.shape[1], q1.shape[2]) * np.random.choice([-1, 1], size=(1, q1.shape[1], q1.shape[2]))) * 0.005 p1_bulk = af.reorder(p1_bulk, 2, 0, 1, 3) p2_bulk = af.reorder(p2_bulk, 2, 0, 1, 3) T = (2.5 / rho) f = rho * (m / (2 * np.pi * k * T))**(3 / 2) \ * af.exp(-m * (p1 - p1_bulk)**2 / (2 * k * T)) \ * af.exp(-m * (p2 - p2_bulk)**2 / (2 * k * T)) \ * af.exp(-m * (p3)**2 / (2 * k * T)) af.eval(f) return (f)
def initialize_f(q1, q2, p1, p2, p3, params): m = params.mass_particle k = params.boltzmann_constant # Calculating the perturbed density: rho = af.select(q1 + q2 > 0.15, q1**0, 0.125) T = af.select(q1 + q2 > 0.15, q1**0, 0.373) f = rho * (m / (2 * np.pi * T))**(3 / 2) \ * af.exp(-p1**2 / (2 * T)) \ * af.exp(-p2**2 / (2 * T)) \ * af.exp(-p3**2 / (2 * T)) af.eval(f) return (f)
def BGK(f, q1, q2, p1, p2, p3, moments, params): """Return BGK operator -(f-f0)/tau.""" n = moments('density') # Floor used to avoid 0/0 limit: eps = 1e-15 p1_bulk = moments('mom_p1_bulk') / (n + eps) p2_bulk = moments('mom_p2_bulk') / (n + eps) p3_bulk = moments('mom_p3_bulk') / (n + eps) T = (1 / params.p_dim) \ * ( moments('energy') - n * p1_bulk**2 - n * p2_bulk**2 - n * p3_bulk**2 ) / (n + eps) + eps C_f = -(f - f0(p1, p2, p3, n, T, p1_bulk, p2_bulk, p3_bulk, params)) / params.tau(q1, q2, p1, p2, p3) # When (f - f0) is NaN. Dividing by np.inf doesn't give 0 # WORKAROUND: C_f = af.select(params.tau(q1, q2, p1, p2, p3) == np.inf, 0, C_f) af.eval(C_f) return (C_f)
def initialize_f(q1, q2, p1, p2, p3, params): m = params.mass_particle k = params.boltzmann_constant rho = af.select(q1 < 0.5, q1**0, 2 / 3) T = af.select(q1 < 0.5, 2 / 3 * q1**0, 1 / 4) p1_b = af.select(q1 < 0.5, q1**0, 1.5) f = rho * af.sqrt(m / (2 * np.pi * k * T))**3 \ * af.exp(-m * (p1-p1_b)**2 / (2 * k * T)) \ * af.exp(-m * p2**2 / (2 * k * T)) \ * af.exp(-m * p3**2 / (2 * k * T)) af.eval(f) return (f)
def simple_data(verbose=False): display_func = _util.display_func(verbose) print_func = _util.print_func(verbose) display_func(af.constant(100, 3,3, dtype=af.Dtype.f32)) display_func(af.constant(25, 3,3, dtype=af.Dtype.c32)) display_func(af.constant(2**50, 3,3, dtype=af.Dtype.s64)) display_func(af.constant(2+3j, 3,3)) display_func(af.constant(3+5j, 3,3, dtype=af.Dtype.c32)) display_func(af.range(3, 3)) display_func(af.iota(3, 3, tile_dims=(2,2))) display_func(af.identity(3, 3, 1, 2, af.Dtype.b8)) display_func(af.identity(3, 3, dtype=af.Dtype.c32)) a = af.randu(3, 4) b = af.diag(a, extract=True) c = af.diag(a, 1, extract=True) display_func(a) display_func(b) display_func(c) display_func(af.diag(b, extract = False)) display_func(af.diag(c, 1, extract = False)) display_func(af.join(0, a, a)) display_func(af.join(1, a, a, a)) display_func(af.tile(a, 2, 2)) display_func(af.reorder(a, 1, 0)) display_func(af.shift(a, -1, 1)) display_func(af.moddims(a, 6, 2)) display_func(af.flat(a)) display_func(af.flip(a, 0)) display_func(af.flip(a, 1)) display_func(af.lower(a, False)) display_func(af.lower(a, True)) display_func(af.upper(a, False)) display_func(af.upper(a, True)) a = af.randu(5,5) display_func(af.transpose(a)) af.transpose_inplace(a) display_func(a) display_func(af.select(a > 0.3, a, -0.3)) af.replace(a, a > 0.3, -0.3) display_func(a)
def simple_data(verbose=False): display_func = _util.display_func(verbose) display_func(af.constant(100, 3, 3, dtype=af.Dtype.f32)) display_func(af.constant(25, 3, 3, dtype=af.Dtype.c32)) display_func(af.constant(2**50, 3, 3, dtype=af.Dtype.s64)) display_func(af.constant(2+3j, 3, 3)) display_func(af.constant(3+5j, 3, 3, dtype=af.Dtype.c32)) display_func(af.range(3, 3)) display_func(af.iota(3, 3, tile_dims=(2, 2))) display_func(af.identity(3, 3, 1, 2, af.Dtype.b8)) display_func(af.identity(3, 3, dtype=af.Dtype.c32)) a = af.randu(3, 4) b = af.diag(a, extract=True) c = af.diag(a, 1, extract=True) display_func(a) display_func(b) display_func(c) display_func(af.diag(b, extract=False)) display_func(af.diag(c, 1, extract=False)) display_func(af.join(0, a, a)) display_func(af.join(1, a, a, a)) display_func(af.tile(a, 2, 2)) display_func(af.reorder(a, 1, 0)) display_func(af.shift(a, -1, 1)) display_func(af.moddims(a, 6, 2)) display_func(af.flat(a)) display_func(af.flip(a, 0)) display_func(af.flip(a, 1)) display_func(af.lower(a, False)) display_func(af.lower(a, True)) display_func(af.upper(a, False)) display_func(af.upper(a, True)) a = af.randu(5, 5) display_func(af.transpose(a)) af.transpose_inplace(a) display_func(a) display_func(af.select(a > 0.3, a, -0.3)) af.replace(a, a > 0.3, -0.3) display_func(a) display_func(af.pad(a, (1, 1, 0, 0), (2, 2, 0, 0)))
def initialize_B(q1, q2, params): B1 = 0 * q1**0 B2 = af.select(q1 < 0.5, params.B0 * 1 * q1**0, -params.B0) B3 = 0 * q1**0 af.eval(B1, B2, B3) return (B1, B2, B3)
def initialize_f(r, theta, rdot, thetadot, phidot, params): # The initialization is performed to setup particles # between r = 0.9 and 1.1 at theta = 0 rho = af.select(r < 2, 1 * r**0, 0) rho = af.select(r > 1, rho, 0) rho = af.select(theta == 0, rho, 0) # Getting f at the right shape: f = rdot * rho f[:] = 0 # Changing to velocities_expanded form: f = af.moddims(f, N_p1, N_p2, N_p3, r.shape[2] * r.shape[3]) rdot = af.moddims(rdot, N_p1, N_p2, N_p3) thetadot = af.moddims(thetadot, N_p1, N_p2, N_p3) # Assigning the velocities such that thetadot ∝ 1 / r for i in range(r.shape[2]): for j in range(r.shape[3]): rho_new = rho * 0 rho_new[:, :, i, j] = rho[:, :, i, j] rho_new = af.moddims(rho_new, 1, 1, 1, r.shape[2] * r.shape[3]) # We set rdot = rdot[64] for all particles and # thetadot is inversely proportional to r # At initialization: thetadot1d = af.flat(thetadot[0, :, 0]) ind = af.imin(af.abs(thetadot1d - 1 / af.sum(r[:, :, i, j])))[1] print('rdot =', af.sum(rdot[64, ind, 0])) print('thetadot =', af.sum(thetadot[64, ind, 0])) f[64, ind, 0] += rho_new f = af.moddims(f, N_p1 * N_p2 * N_p3, 1, r.shape[2], r.shape[3]) af.eval(f) return (f)
def lowpass_filter(f): f_hat = af.fft(f) dp1 = (domain.p1_end[0] - domain.p1_start[0]) / domain.N_p1 k_v = af.tile(af.to_array(np.fft.fftfreq(domain.N_p1, dp1)), 1, 1, f.shape[2], f.shape[3]) # Applying the filter: f_hat_filtered = 0.5 * (f_hat * (af.tanh( (k_v + 0.9 * af.max(k_v)) / 0.5) - af.tanh( (k_v + 0.9 * af.min(k_v)) / 0.5))) f_hat = af.select(af.abs(k_v) < 0.8 * af.max(k_v), f_hat, f_hat_filtered) f = af.real(af.ifft(f_hat)) return (f)
def f_interp(config, dt, x, vel_x, f): x_new = x - vel_x*dt step_size = af.sum(x[1,0] - x[0,0]) f_interp = af.constant(0, N_positions + 2*ghost_zones, N_velocity) f_interp = af.Array.as_type(f_interp, af.Dtype.f64) # Interpolating: x_temp = x_new[ghost_zones:-ghost_zones, :].copy() while(af.sum(x_temp<left_boundary)!=0): x_temp = af.select(x_temp<left_boundary, x_temp + length, x_temp ) while(af.sum(x_temp>right_boundary)!=0): x_temp = af.select(x_temp>right_boundary, x_temp - length, x_temp ) x_temp = af.Array.as_type(x_temp, af.Dtype.f64) x_interpolant = x_temp/step_size + ghost_zones x_interpolant = af.Array.as_type(x_interpolant, af.Dtype.f64) f = af.Array.as_type(f, af.Dtype.f64) f_interp[ghost_zones:-ghost_zones, :] = af.approx1(f, x_interpolant,\ af.INTERP.CUBIC_SPLINE ) f_interp = af.Array.as_type(f_interp, af.Dtype.f64) af.eval(f_interp) return f_interp
def riemann_solver(self, left_state, right_state, velocity): """ Returns the upwinded state, using the 1st order upwind Riemann solver. Parameters ---------- left_state : af.Array Array holding the values for the state at the left edge of the cells. right_state : af.Array Array holding the values for the state at the right edge of the cells. velocity : af.Array Velocity array whose sign will be used to determine whether the left or right state is chosen. """ if (self.performance_test_flag == True): tic = af.time() # Checking if array isn't 4D: try: size_axis_2 = left_state.shape[2] except: size_axis_2 = 1 try: size_axis_3 = left_state.shape[3] except: size_axis_3 = 1 # Tiling to get to appropriate shape: try: assert (velocity.shape[2] == left_state.shape[2]) except: velocity = af.tile(velocity, 1, 1, size_axis_2, size_axis_3) upwind_state = af.select(velocity > 0, left_state, right_state) af.eval(upwind_state) if (self.performance_test_flag == True): af.sync() toc = af.time() self.time_riemann += toc - tic return (upwind_state)
def initialize_f(q1, q2, v1, v2, v3, params): m = params.mass k = params.boltzmann_constant n_b = params.density_background T_b = params.temperature_background n = n_b + 0 * q1**0 T = af.select((q1**2 + q2**2) < 0.01, 100 * T_b * q1**0, T_b) f = n * (m / (2 * np.pi * k * T)) \ * af.exp(-m * v1**2 / (2 * k * T)) \ * af.exp(-m * v2**2 / (2 * k * T)) af.eval(f) return (f)
def BGK(f, q1, q2, p1, p2, p3, moments, params, flag = False): """Return BGK operator -(f-f0)/tau.""" n = moments('density', f) # Floor used to avoid 0/0 limit: eps = 1e-30 p1_bulk = moments('mom_p1_bulk', f) / (n + eps) p2_bulk = moments('mom_p2_bulk', f) / (n + eps) p3_bulk = moments('mom_p3_bulk', f) / (n + eps) T = (1 / params.p_dim) * ( 2 * moments('energy', f) - n * p1_bulk**2 - n * p2_bulk**2 - n * p3_bulk**2 ) / (n + eps) + eps if(af.any_true(params.tau(q1, q2, p1, p2, p3) == 0)): f_MB = f0(p1, p2, p3, n, T, p1_bulk, p2_bulk, p3_bulk, params) if(flag == False): f_MB[:] = 0 return(f_MB) else: C_f = -( f - f0(p1, p2, p3, n, T, p1_bulk, p2_bulk, p3_bulk, params) ) / params.tau(q1, q2, p1, p2, p3) # When (f - f0) is NaN. Dividing by np.inf doesn't give 0 # Setting when tau is zero we assign f = f0 manually # WORKAROUND: if(isinstance(params.tau(q1, q2, p1, p2, p3), af.Array) is True): C_f = af.select(params.tau(q1, q2, p1, p2, p3) == np.inf, 0, C_f) af.eval(C_f) else: if(params.tau(q1, q2, p1, p2, p3) == np.inf): C_f = 0 return(C_f)
def RK5_step(self, dt): """ Evolves the physical system defined using an RK5 integrator. This method is 5th order accurate. Parameters ---------- dt: double The timestep size. """ # For purely collisional cases: tau = self.physical_system.params.tau(self.q1_center, self.q2_center, self.p1, self.p2, self.p3) if (af.any_true(tau == 0)): f0 = self._source( 0.5 * self.N_q1 * self.N_q2 * af.real(ifft2(self.f_hat)), self.time_elapsed, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.compute_moments, self.physical_system.params, True) self.f_hat = af.select(tau == 0, 2 * fft2(f0) / (self.N_q1 * self.N_q2), self.f_hat) if (self.physical_system.params.EM_fields_enabled == True and self.physical_system.params.fields_type == 'electrodynamic'): # Since the fields and the distribution function are coupled, # we evolve the system by making use of a coupled integrator # which ensures that throughout the timestepping they are # evaluated at the same temporal locations. self.f_hat, self.fields_solver.fields_hat = \ integrators.RK5_coupled(df_hat_dt, self.f_hat, dfields_hat_dt, self.fields_solver.fields_hat, dt, self ) else: self.f_hat = integrators.RK5(df_hat_dt, self.f_hat, dt, self.fields_solver.fields_hat, self) return
def upwind_flux(left_flux, right_flux, velocity): """ Returns the flux, using the 1st order upwind flux Riemann solver. Parameters ---------- left_flux : af.Array Array holding the values for the flux at the left edge of the cells. right_flux : af.Array Array holding the values for the flux at the right edge of the cells. velocity : af.Array Velocity array whose sign will be used to determine whether the left or right flux is chosen. """ flux = af.select(velocity > 0, left_flux, right_flux) af.eval(flux) return (flux)
def op_solve_src(self, dt): """ Evolves the source term of the equations specified: df/dt = source Parameters ---------- dt : double Time-step size to evolve the system """ if (self.performance_test_flag == True): tic = af.time() # Solving for tau = 0 systems: tau = self.physical_system.params.tau(self.q1_center, self.q2_center, self.p1_center, self.p2_center, self.p3_center) if (af.any_true(tau == 0)): self.f = af.select( tau == 0, self._source(self.f, self.time_elapsed, self.q1_center, self.q2_center, self.p1_center, self.p2_center, self.p3_center, self.compute_moments, self.physical_system.params, True), self.f) self.f = integrators.RK2(self._source, self.f, dt, self.time_elapsed, self.q1_center, self.q2_center, self.p1_center, self.p2_center, self.p3_center, self.compute_moments, self.physical_system.params) if (self.performance_test_flag == True): af.sync() toc = af.time() self.time_sourcets += toc - tic return
assert(params.dt_dump_moments > dt) assert(params.dt_dump_fields > dt) #if (params.restart): # nls.load_distribution_function(params.restart_file) density = nls.compute_moments('density') print("rank = ", params.rank, "\n", " <mu> = ", af.mean(params.mu[0, 0, N_g:-N_g, N_g:-N_g]), "\n", " max(mu) = ", af.max(params.mu[0, 0, N_g:-N_g, N_g:-N_g]), "\n", " <n> = ", af.mean(density[0, 0, N_g:-N_g, N_g:-N_g]), "\n", " max(n) = ", af.max(density[0, 0, N_g:-N_g, N_g:-N_g]), "\n" ) nls.f = af.select(nls.f < 1e-20, 1e-20, nls.f) while(time_elapsed < t_final): # Refine to machine error if (time_step==0): params.collision_nonlinear_iters = 10 else: params.collision_nonlinear_iters = params.collision_operator_nonlinear_iters dump_steps = params.dump_steps if(params.dt_dump_moments != 0): # We step by delta_dt to get the values at dt_dump delta_dt = (1 - math.modf(time_elapsed/params.dt_dump_moments)[0]) \ * params.dt_dump_moments
def initialize_f(q1, q2, p1, p2, p3, params): f = af.select(af.abs(p1)<2.5, q1**0 * p1**0, 0) af.eval(f) return (f)
def apply_dirichlet_bcs_f(self, boundary): """ Applies Dirichlet boundary conditions along boundary specified for the distribution function Parameters ---------- boundary: str Boundary along which the boundary condition is to be applied. """ N_g = self.N_ghost if (self.physical_system.params.solver_method_in_q == 'FVM'): velocity_q1, velocity_q2 = \ af.broadcast(self._C_q, self.time_elapsed, self.q1_center, self.q2_center, self.p1_center, self.p2_center, self.p3_center, self.physical_system.params ) else: velocity_q1, velocity_q2 = \ af.broadcast(self._A_q, self.time_elapsed, self.q1_center, self.q2_center, self.p1_center, self.p2_center, self.p3_center, self.physical_system.params ) if (velocity_q1.elements() == self.N_species * self.N_p1 * self.N_p2 * self.N_p3): # If velocity_q1 is of shape (Np1 * Np2 * Np3) # We tile to get it to form (Np1 * Np2 * Np3, 1, Nq1, Nq2) velocity_q1 = af.tile(velocity_q1, 1, 1, self.f.shape[2], self.f.shape[3]) if (velocity_q2.elements() == self.N_species * self.N_p1 * self.N_p2 * self.N_p3): # If velocity_q2 is of shape (Np1 * Np2 * Np3) # We tile to get it to form (Np1 * Np2 * Np3, 1, Nq1, Nq2) velocity_q2 = af.tile(velocity_q2, 1, 1, self.f.shape[2], self.f.shape[3]) # Arguments that are passing to the called functions: args = (self.f, self.time_elapsed, self.q1_center, self.q2_center, self.p1_center, self.p2_center, self.p3_center, self.physical_system.params) if (boundary == 'left'): f_left = self.boundary_conditions.f_left(*args) # Only changing inflowing characteristics: f_left = af.select(velocity_q1 > 0, f_left, self.f) self.f[:, :, :N_g] = f_left[:, :, :N_g] elif (boundary == 'right'): f_right = self.boundary_conditions.f_right(*args) # Only changing inflowing characteristics: f_right = af.select(velocity_q1 < 0, f_right, self.f) self.f[:, :, -N_g:] = f_right[:, :, -N_g:] elif (boundary == 'bottom'): f_bottom = self.boundary_conditions.f_bottom(*args) # Only changing inflowing characteristics: f_bottom = af.select(velocity_q2 > 0, f_bottom, self.f) self.f[:, :, :, :N_g] = f_bottom[:, :, :, :N_g] elif (boundary == 'top'): f_top = self.boundary_conditions.f_top(*args) # Only changing inflowing characteristics: f_top = af.select(velocity_q2 < 0, f_top, self.f) self.f[:, :, :, -N_g:] = f_top[:, :, :, -N_g:] else: raise Exception('Invalid choice for boundary') return
def apply_shearing_box_bcs_f(self, boundary): """ Applies the shearing box boundary conditions along boundary specified for the distribution function Parameters ---------- boundary: str Boundary along which the boundary condition is to be applied. """ N_g = self.N_ghost q = self.physical_system.params.q omega = self.physical_system.params.omega L_q1 = self.q1_end - self.q1_start L_q2 = self.q2_end - self.q2_start if (boundary == 'left'): sheared_coordinates = self.q2_center[:, :, : N_g] - q * omega * L_q1 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q2_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q2_end, sheared_coordinates - L_q2, sheared_coordinates) while (af.sum(sheared_coordinates < self.q2_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q2_start, sheared_coordinates + L_q2, sheared_coordinates) # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.f[:, :, :N_g] = af.reorder( af.approx2(af.reorder(self.f[:, :, :N_g], 2, 3, 0, 1), af.reorder(self.q1_center[:, :, :N_g], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp=af.reorder(self.q1_center[:, :, :N_g], 2, 3, 0, 1), yp=af.reorder(self.q2_center[:, :, :N_g], 2, 3, 0, 1)), 2, 3, 0, 1) elif (boundary == 'right'): sheared_coordinates = self.q2_center[:, :, -N_g:] + q * omega * L_q1 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q2_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q2_end, sheared_coordinates - L_q2, sheared_coordinates) while (af.sum(sheared_coordinates < self.q2_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q2_start, sheared_coordinates + L_q2, sheared_coordinates) # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.f[:, :, -N_g:] = af.reorder( af.approx2(af.reorder(self.f[:, :, -N_g:], 2, 3, 0, 1), af.reorder(self.q1_center[:, :, -N_g:], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp=af.reorder(self.q1_center[:, :, -N_g:], 2, 3, 0, 1), yp=af.reorder(self.q2_center[:, :, -N_g:], 2, 3, 0, 1)), 2, 3, 0, 1) elif (boundary == 'bottom'): sheared_coordinates = self.q1_center[:, :, :, : N_g] - q * omega * L_q2 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q1_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q1_end, sheared_coordinates - L_q1, sheared_coordinates) while (af.sum(sheared_coordinates < self.q1_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q1_start, sheared_coordinates + L_q1, sheared_coordinates) # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.f[:, :, :, :N_g] = af.reorder( af.approx2(af.reorder(self.f[:, :, :, :N_g], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.reorder(self.q2_center[:, :, :, :N_g], 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp=af.reorder(self.q1_center[:, :, :, :N_g], 2, 3, 0, 1), yp=af.reorder(self.q2_center[:, :, :, :N_g], 2, 3, 0, 1)), 2, 3, 0, 1) elif (boundary == 'top'): sheared_coordinates = self.q1_center[:, :, :, -N_g:] + q * omega * L_q2 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q1_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q1_end, sheared_coordinates - L_q1, sheared_coordinates) while (af.sum(sheared_coordinates < self.q1_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q1_start, sheared_coordinates + L_q1, sheared_coordinates) # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.f[:, :, :, -N_g:] = af.reorder( af.approx2(af.reorder(self.f[:, :, :, -N_g:], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.reorder(self.q2_center[:, :, :, -N_g:], 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp=af.reorder(self.q1_center[:, :, :, -N_g:], 2, 3, 0, 1), yp=af.reorder(self.q2_center[:, :, :, -N_g:], 2, 3, 0, 1)), 2, 3, 0, 1) else: raise Exception('Invalid choice for boundary') return
def dY_dt_multimode_evolution(Y, self): """ Returns the value of the derivative of the fourier mode quantities of the distribution function, and the field quantities with respect to time. This is used to evolve the system in time. Input: ------ Y : The array Y is the state of the system as given by the result of the last time-step's integration. The elements of Y, hold the following data: f_hat = Y[:, :, :, 0] E1_hat = Y[:, :, :, 1] E2_hat = Y[:, :, :, 2] E3_hat = Y[:, :, :, 3] B1_hat = Y[:, :, :, 4] B2_hat = Y[:, :, :, 5] B3_hat = Y[:, :, :, 6] At t = 0 the initial state of the system is passed to this function: Output: ------- dY_dt : The time-derivatives of all the quantities stored in Y """ f_hat = Y[:, :, :, 0] self.E1_hat = Y[:, :, :, 1] self.E2_hat = Y[:, :, :, 2] self.E3_hat = Y[:, :, :, 3] self.B1_hat = Y[:, :, :, 4] self.B2_hat = Y[:, :, :, 5] self.B3_hat = Y[:, :, :, 6] # Scaling Appropriately: f = af.real(af.ifft2(0.5 * self.N_q2 * self.N_q1 * f_hat)) C_f_hat = 2 * af.fft2(self._source(f, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.compute_moments, self.physical_system.params ) )/(self.N_q2 * self.N_q1) if( self.physical_system.params.fields_solver == 'electrostatic' or self.physical_system.params.fields_solver == 'fft' ): compute_electrostatic_fields(self, f_hat=f_hat) # When method is FDTD, this function returns the timederivatives # of the field quantities which is stepped using a numerical integrator: elif(self.physical_system.params.fields_solver == 'fdtd'): pass else: raise NotImplementedError('Method invalid/not-implemented') mom_bulk_p1 = self.compute_moments('mom_p1_bulk', f_hat=f_hat) mom_bulk_p2 = self.compute_moments('mom_p2_bulk', f_hat=f_hat) mom_bulk_p3 = self.compute_moments('mom_p3_bulk', f_hat=f_hat) J1_hat = 2 * af.fft2( self.physical_system.params.charge_electron * mom_bulk_p1 )/(self.N_q1 * self.N_q2) J2_hat = 2 * af.fft2( self.physical_system.params.charge_electron * mom_bulk_p2 )/(self.N_q1 * self.N_q2) J3_hat = 2 * af.fft2( self.physical_system.params.charge_electron * mom_bulk_p3 )/(self.N_q1 * self.N_q2) # Defining lambda functions to perform broadcasting operations: # This is done using af.broadcast, which allows us to perform # batched operations when operating on arrays of different sizes: multiply = lambda a,b:a * b addition = lambda a,b:a + b # af.broadcast(function, *args) performs batched operations on # function(*args): dE1_hat_dt = af.broadcast(addition, af.broadcast(multiply, self.B3_hat, 1j * self.k_q2), - J1_hat ) dE2_hat_dt = af.broadcast(addition, af.broadcast(multiply,-self.B3_hat, 1j * self.k_q1), - J2_hat ) dE3_hat_dt = af.broadcast(addition, af.broadcast(multiply, self.B2_hat, 1j * self.k_q1) - af.broadcast(multiply, self.B1_hat, 1j * self.k_q2), - J3_hat ) dB1_hat_dt = af.broadcast(multiply, -self.E3_hat, 1j * self.k_q2) dB2_hat_dt = af.broadcast(multiply, self.E3_hat, 1j * self.k_q1) dB3_hat_dt = af.broadcast(multiply, self.E1_hat, 1j * self.k_q2) \ - af.broadcast(multiply, self.E2_hat, 1j * self.k_q1) (A_p1, A_p2, A_p3) = af.broadcast(self._A_p, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.E1_hat, self.E2_hat, self.E3_hat, self.B1_hat, self.B2_hat, self.B3_hat, self.physical_system.params ) df_hat_dt = -1j * ( af.broadcast(multiply, self.k_q1, self._A_q1) + af.broadcast(multiply, self.k_q2, self._A_q2) ) * f_hat # Adding the fields term only when charge is non-zero if(self.physical_system.params.charge_electron != 0): fields_term = af.broadcast(multiply, A_p1, self.dfdp1_background) \ + af.broadcast(multiply, A_p2, self.dfdp2_background) \ + af.broadcast(multiply, A_p3, self.dfdp3_background) df_hat_dt -= fields_term # Avoiding addition of the collisional term when tau != inf tau = self.physical_system.params.tau(self.q1_center, self.q2_center, self.p1, self.p2, self.p3 ) df_hat_dt += af.select(tau != np.inf,\ C_f_hat,\ 0 ) # Obtaining the dY_dt vector by joining the derivative quantities of # the individual distribution function and field modes: dY_dt = af.join(3, af.join(3, df_hat_dt, dE1_hat_dt, dE2_hat_dt, dE3_hat_dt), dB1_hat_dt, dB2_hat_dt, dB3_hat_dt ) af.eval(dY_dt) return(dY_dt)
y = r * np.sin(theta) for time_index, t0 in enumerate(time): h5f = h5py.File('dump/%04d' % (time_index) + '.h5', 'r') n = np.swapaxes(h5f['moments'][:], 0, 1)[:, :, 0] h5f.close() pl.plot(x_analytic[time_index], y_analytic[time_index], 'o', color='white', alpha=0.05) import arrayfire as af n = np.array(af.select(af.to_array(n) < 0, 0, af.to_array(n))) pl.contourf(x, y, n, 200) pl.gca().set_aspect('equal') pl.xlim(0, 4) pl.ylim(-4, 4) pl.xlabel(r'$x$') pl.ylabel(r'$y$') pl.title('Time = %.3f' % t0) pl.savefig('images/%04d' % time_index + '.png') pl.clf() # ax2 = fig.add_subplot(1, 2, 2) # ax2.plot(x_analytic[time_index+1], y_analytic[time_index+1], 'or') # ax2.set_xlim(0, 4) # ax2.set_ylim(-4, 4)
def test_shear_y(): t = np.random.rand(1)[0] N = 2**np.arange(5, 10) error = np.zeros(N.size) for i in range(N.size): domain.N_q1 = int(N[i]) domain.N_q2 = int(N[i]) # Defining the physical system to be solved: system = physical_system(domain, boundary_conditions_y, params, initialize_y, advection_terms, collision_operator.BGK, moment_defs) L_q1 = domain.q1_end - domain.q1_start L_q2 = domain.q2_end - domain.q2_start nls = nonlinear_solver(system) N_g = nls.N_ghost_q # For left: f_reference_bot = af.broadcast( initialize_y.initialize_f, af.select( nls.q1_center - t < domain.q1_start, # Periodic domain nls.q1_center - t + L_q1, nls.q1_center - t), nls.q2_center, nls.p1_center, nls.p2_center, nls.p3_center, params)[:, N_g:-N_g, -2 * N_g:-N_g] # For right: f_reference_top = af.broadcast( initialize_y.initialize_f, af.select(nls.q1_center + t > domain.q1_end, nls.q1_center + t - L_q1, nls.q1_center + t), nls.q2_center, nls.p1_center, nls.p2_center, nls.p3_center, params)[:, N_g:-N_g, N_g:2 * N_g] nls.time_elapsed = t nls._communicate_f() nls._apply_bcs_f() error[i] = af.mean(af.abs(nls.f[:, N_g:-N_g, :N_g] - f_reference_bot)) \ + af.mean(af.abs(nls.f[:, N_g:-N_g, -N_g:] - f_reference_top)) pl.loglog(N, error, '-o', label='Numerical') pl.loglog(N, error[0] * 32**3 / N**3, '--', color='black', label=r'$O(N^{-3})$') pl.xlabel(r'$N$') pl.ylabel('Error') pl.legend() pl.savefig('plot2.png') poly = np.polyfit(np.log10(N), np.log10(error), 1) assert (abs(poly[0] + 3) < 0.3)
def apply_dirichlet_bcs_f(self, boundary): N_g = self.N_ghost if (self._A_q1.elements() == self.N_p1 * self.N_p2 * self.N_p3): # A_q1 is of shape (Np1 * Np2 * Np3) # We tile to get it to form (Np1 * Np2 * Np3, Nq1, Nq2) A_q1 = af.tile(self._A_q1, 1, self.f.shape[1], self.f.shape[2]) if (self._A_q2.elements() == self.N_p1 * self.N_p2 * self.N_p3): # _A_q2 is of shape (Np1 * Np2 * Np3) # We tile to get it to form (Np1 * Np2 * Np3, Nq1, Nq2) A_q2 = af.tile(self._A_q2, 1, self.f.shape[1], self.f.shape[2]) if (boundary == 'left'): f_left = self.boundary_conditions.\ f_left(self.f, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.physical_system.params ) # Only changing inflowing characteristics: f_left = af.select(A_q1 > 0, f_left, self.f) self.f[:, :N_g] = f_left[:, :N_g] elif (boundary == 'right'): f_right = self.boundary_conditions.\ f_right(self.f, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.physical_system.params ) # Only changing inflowing characteristics: f_right = af.select(A_q1 < 0, f_right, self.f) self.f[:, -N_g:] = f_right[:, -N_g:] elif (boundary == 'bottom'): f_bottom = self.boundary_conditions.\ f_bottom(self.f, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.physical_system.params ) # Only changing inflowing characteristics: f_bottom = af.select(A_q2 > 0, f_bottom, self.f) self.f[:, :, :N_g] = f_bottom[:, :, :N_g] elif (boundary == 'top'): f_top = self.boundary_conditions.\ f_top(self.f, self.q1_center, self.q2_center, self.p1, self.p2, self.p3, self.physical_system.params ) # Only changing inflowing characteristics: f_top = af.select(A_q2 < 0, f_top, self.f) self.f[:, :, -N_g:] = f_top[:, :, -N_g:] else: raise Exception('Invalid choice for boundary') return
def apply_shearing_box_bcs_fields(self, boundary, on_fdtd_grid): """ Applies the shearing box boundary conditions along boundary specified for the EM fields Parameters ---------- boundary: str Boundary along which the boundary condition is to be applied. on_fdtd_grid: bool Flag which dictates if boundary conditions are to be applied to the fields on the Yee grid or on the cell centered grid. """ N_g_q = self.N_ghost_q q = self.physical_system.params.q omega = self.physical_system.params.omega L_q1 = self.q1_end - self.q1_start L_q2 = self.q2_end - self.q2_start if (boundary == 'left'): sheared_coordinates = self.q2_center[:, :, : N_g_q] - q * omega * L_q1 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q2_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q2_end, sheared_coordinates - L_q2, sheared_coordinates) while (af.sum(sheared_coordinates < self.q2_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q2_start, sheared_coordinates + L_q2, sheared_coordinates) if (on_fdtd_grid == True): # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.yee_grid_EM_fields[:, :, :N_g_q] = \ af.reorder(af.approx2(af.reorder(self.yee_grid_EM_fields[:, :, :N_g_q], 2, 3, 0, 1), af.reorder(self.q1_center[:, :, :N_g_q], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, :N_g_q], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, :N_g_q], 2, 3, 0, 1) ), 2, 3, 0, 1 ) else: # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.cell_centered_EM_fields[:, :, :N_g_q] = \ af.reorder(af.approx2(af.reorder(self.cell_centered_EM_fields[:, :, :N_g_q], 2, 3, 0, 1), af.reorder(self.q1_center[:, :, :N_g_q], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, :N_g_q], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, :N_g_q], 2, 3, 0, 1) ), 2, 3, 0, 1 ) elif (boundary == 'right'): sheared_coordinates = self.q2_center[:, :, -N_g_q:] + q * omega * L_q1 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q2_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q2_end, sheared_coordinates - L_q2, sheared_coordinates) while (af.sum(sheared_coordinates < self.q2_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q2_start, sheared_coordinates + L_q2, sheared_coordinates) if (on_fdtd_grid == True): # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.yee_grid_EM_fields[:, :, -N_g_q:] = \ af.reorder(af.approx2(af.reorder(self.yee_grid_EM_fields[:, :, -N_g_q:], 2, 3, 0, 1), af.reorder(self.q1_center[:, :, -N_g_q:], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, -N_g_q:], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, -N_g_q:], 2, 3, 0, 1) ), 2, 3, 0, 1 ) else: # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.cell_centered_EM_fields[:, :, -N_g_q:] = \ af.reorder(af.approx2(af.reorder(self.cell_centered_EM_fields[:, :, -N_g_q:],2, 3, 0, 1), af.reorder(self.q1_center[:, :, -N_g_q:], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, -N_g_q:], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, -N_g_q:], 2, 3, 0, 1) ), 2, 3, 0, 1 ) elif (boundary == 'bottom'): sheared_coordinates = self.q1_center[:, :, :, : N_g_q] - q * omega * L_q2 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q1_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q1_end, sheared_coordinates - L_q1, sheared_coordinates) while (af.sum(sheared_coordinates < self.q1_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q1_start, sheared_coordinates + L_q1, sheared_coordinates) if (on_fdtd_grid == True): # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.yee_grid_EM_fields[:, :, :, :N_g_q] = \ af.reorder(af.approx2(af.reorder(self.yee_grid_EM_fields[:, :, :, :N_g_q], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.reorder(self.q2_center[:, :, :, :N_g_q], 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, :, :N_g_q], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, :, :N_g_q], 2, 3, 0, 1) ), 2, 3, 0, 1 ) else: # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.cell_centered_EM_fields[:, :, :, :N_g_q] = \ af.reorder(af.approx2(af.reorder(self.cell_centered_EM_fields[:, :, :, :N_g_q], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.reorder(self.q2_center[:, :, :, :N_g_q], 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, :, :N_g_q], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, :, :N_g_q], 2, 3, 0, 1) ), 2, 3, 0, 1 ) elif (boundary == 'top'): sheared_coordinates = self.q1_center[:, :, :, -N_g_q:] + q * omega * L_q2 * self.time_elapsed # Applying periodic boundary conditions to the points which are out of domain: while (af.sum(sheared_coordinates > self.q1_end) != 0): sheared_coordinates = af.select(sheared_coordinates > self.q1_end, sheared_coordinates - L_q1, sheared_coordinates) while (af.sum(sheared_coordinates < self.q1_start) != 0): sheared_coordinates = af.select( sheared_coordinates < self.q1_start, sheared_coordinates + L_q1, sheared_coordinates) if (on_fdtd_grid == True): # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.yee_grid_EM_fields[:, :, :, -N_g_q:] = \ af.reorder(af.approx2(af.reorder(self.yee_grid_EM_fields[:, :, :, -N_g_q:], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.reorder(self.q2_center[:, :, :, -N_g_q:], 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, :, -N_g_q:], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, :, -N_g_q:], 2, 3, 0, 1) ), 2, 3, 0, 1 ) else: # Reordering from (N_p, N_s, N_q1, N_q2) --> (N_q1, N_q2, N_p, N_s) # and reordering back from (N_q1, N_q2, N_p, N_s) --> (N_p, N_s, N_q1, N_q2) self.cell_centered_EM_fields[:, :, :, -N_g_q:] = \ af.reorder(af.approx2(af.reorder(self.cell_centered_EM_fields[:, :, :, -N_g_q:], 2, 3, 0, 1), af.reorder(sheared_coordinates, 2, 3, 0, 1), af.reorder(self.q2_center[:, :, :, -N_g_q:], 2, 3, 0, 1), af.INTERP.BICUBIC_SPLINE, xp = af.reorder(self.q1_center[:, :, :, -N_g_q:], 2, 3, 0, 1), yp = af.reorder(self.q2_center[:, :, :, -N_g_q:], 2, 3, 0, 1) ), 2, 3, 0, 1 ) else: raise Exception('Invalid choice for boundary') return