def calculate_E(B, J, qn, DX=dx): '''Calculates the value of the electric field based on source term and magnetic field contributions, assuming constant electron temperature across simulation grid. This is done via a reworking of Ampere's Law that assumes quasineutrality, and removes the requirement to calculate the electron current. Based on equation 10 of Buchner (2003, p. 140). INPUT: B -- Magnetic field array. Displaced from E-field array by half a spatial step. J -- Ion current density. Source term, based on particle velocities qn -- Charge density. Source term, based on particle positions OUTPUT: E_out -- Updated electric field array ''' curlB = get_curl_B(B, DX=DX) / mu0 Ve = np.zeros((J.shape[0], 3)) Ve[:, 0] = (J[:, 0] - curlB[:, 0]) / qn Ve[:, 1] = (J[:, 1] - curlB[:, 1]) / qn Ve[:, 2] = (J[:, 2] - curlB[:, 2]) / qn Te = get_electron_temp(qn) del_p = get_grad_P(qn, Te) B_center = interpolate_to_center_cspline3D(B, DX=DX) VexB = cross_product(Ve, B_center) E = np.zeros((J.shape[0], 3)) E[:, 0] = -VexB[:, 0] - del_p / qn E[:, 1] = -VexB[:, 1] E[:, 2] = -VexB[:, 2] E[0] = E[J.shape[0] - 3] E[J.shape[0] - 2] = E[1] E[J.shape[0] - 1] = E[2] return E, Ve, Te
def calculate_E_old(B, J, qn, DX=dx): '''Calculates the value of the electric field based on source term and magnetic field contributions, assuming constant electron temperature across simulation grid. This is done via a reworking of Ampere's Law that assumes quasineutrality, and removes the requirement to calculate the electron current. Based on equation 10 of Buchner (2003, p. 140). INPUT: B -- Magnetic field array. Displaced from E-field array by half a spatial step. J -- Ion current density. Source term, based on particle velocities qn -- Charge density. Source term, based on particle positions OUTPUT: E_out -- Updated electric field array ''' Te = get_electron_temp(qn) B_center = interpolate_to_center_cspline3D(B, DX=DX) JxB = cross_product(J, B_center) curlB = get_curl_B(B, DX=DX) BdB = cross_product(B_center, curlB) / mu0 del_p = get_grad_P(qn, Te) E_out = np.zeros((J.shape[0], 3)) E_out[:, 0] = (- JxB[:, 0] - BdB[:, 0] - del_p ) / qn E_out[:, 1] = (- JxB[:, 1] - BdB[:, 1] ) / qn E_out[:, 2] = (- JxB[:, 2] - BdB[:, 2] ) / qn E_out[0] = E_out[J.shape[0] - 3] E_out[J.shape[0] - 2] = E_out[1] E_out[J.shape[0] - 1] = E_out[2] # This doesn't really get used, but might as well return E_out
def calculate_E(B, Ji, q_dens, E, Ve, Te, temp3D, temp3D2, temp1D, qq=0): '''Calculates the value of the electric field based on source term and magnetic field contributions, assuming constant electron temperature across simulation grid. This is done via a reworking of Ampere's Law that assumes quasineutrality, and removes the requirement to calculate the electron current. Based on equation 10 of Buchner (2003, p. 140). INPUT: B -- Magnetic field array. Displaced from E-field array by half a spatial step. Ji -- Ion current density. Source term, based on particle velocities qn -- Charge density. Source term, based on particle positions OUTPUT: E -- Updated electric field array Ve -- Electron velocity moment Te -- Electron temperature arr3D, arr1D are tertiary arrays used for intermediary computations ''' curl_B_term(B, temp3D) # temp3D is now curl B term Ve[:, 0] = (Ji[:, 0] - temp3D[:, 0]) / q_dens Ve[:, 1] = (Ji[:, 1] - temp3D[:, 1]) / q_dens Ve[:, 2] = (Ji[:, 2] - temp3D[:, 2]) / q_dens get_electron_temp(q_dens, Te) get_grad_P(q_dens, Te, temp1D, temp3D2[:, 0]) # temp1D is now del_p term, temp3D2 slice used for computation aux.interpolate_to_center_cspline3D(B, temp3D2) # temp3d2 is now B_center aux.cross_product(Ve, temp3D2, temp3D) # temp3D is now Ve x B term E[:, 0] = - temp3D[:, 0] - temp1D[:] / q_dens[:] E[:, 1] = - temp3D[:, 1] E[:, 2] = - temp3D[:, 2] E[0] = E[Ji.shape[0] - 3] E[Ji.shape[0] - 2] = E[1] E[Ji.shape[0] - 1] = E[2] return
def test_interp_cross_manual(): ''' Test order of cross product with interpolation, separate from hall term calculation (double check) ''' grids = [16, 32, 64, 128, 256, 512, 1024] errors = np.zeros(len(grids)) #NX = 32 #const.NX xmin = 0.0 #const.xmin xmax = 2*np.pi #const.xmax k = 1.0 marker_size = 20 for NX, ii in zip(grids, list(range(len(grids)))): dx = xmax / NX #x = np.arange(xmin, xmax, dx/100.) # Physical location of nodes E_nodes = (np.arange(NX + 3) - 0.5) * dx B_nodes = (np.arange(NX + 3) - 1.0) * dx ## TEST INPUT FIELDS ## A = np.ones((NX + 3, 3)) Ax = np.sin(1.0*E_nodes*k) Ay = np.sin(2.0*E_nodes*k) Az = np.sin(3.0*E_nodes*k) B = np.ones((NX + 3, 3)) Bx = np.cos(1.0*B_nodes*k) By = np.cos(2.0*B_nodes*k) Bz = np.cos(3.0*B_nodes*k) Be = np.ones((NX + 3, 3)) Bxe = np.cos(1.0*E_nodes*k) Bye = np.cos(2.0*E_nodes*k) Bze = np.cos(3.0*E_nodes*k) A[:, 0] = Ax ; B[:, 0] = Bx ; Be[:, 0] = Bxe A[:, 1] = Ay ; B[:, 1] = By ; Be[:, 1] = Bye A[:, 2] = Az ; B[:, 2] = Bz ; Be[:, 2] = Bze B_inter = aux.interpolate_to_center_cspline3D(B) ## RESULTS (AxB) ## anal_result = np.ones((NX + 3, 3)) anal_result[:, 0] = Ay*Bze - Az*Bye anal_result[:, 1] = Az*Bxe - Ax*Bze anal_result[:, 2] = Ax*Bye - Ay*Bxe test_result = aux.cross_product(A, Be) inter_result = aux.cross_product(A, B_inter) error_x = abs(anal_result[:, 0] - inter_result[:, 0]).max() error_y = abs(anal_result[:, 1] - inter_result[:, 1]).max() error_z = abs(anal_result[:, 2] - inter_result[:, 2]).max() errors[ii] = np.max([error_x, error_y, error_z]) for ii in range(len(grids) - 1): order = np.log(errors[ii] / errors[ii + 1]) / np.log(2) print(order) # OUTPUTS (Plots the highest grid-resolution version) plot = False if plot == True: plt.figure() plt.scatter(E_nodes, anal_result[:, 0], s=marker_size, c='k', marker='o') plt.scatter(E_nodes, anal_result[:, 1], s=marker_size, c='k', marker='o') plt.scatter(E_nodes, anal_result[:, 2], s=marker_size, c='k', marker='o') plt.scatter(E_nodes, test_result[:, 0], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, test_result[:, 1], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, test_result[:, 2], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, inter_result[:, 0], s=marker_size, c='b', marker='x') plt.scatter(E_nodes, inter_result[:, 1], s=marker_size, c='b', marker='x') plt.scatter(E_nodes, inter_result[:, 2], s=marker_size, c='b', marker='x') for kk in range(NX + 3): plt.axvline(E_nodes[kk], linestyle='--', c='r', alpha=0.2) plt.axvline(B_nodes[kk], linestyle='--', c='b', alpha=0.2) plt.axvline(xmin, linestyle='-', c='k', alpha=0.2) plt.axvline(xmax, linestyle='-', c='k', alpha=0.2) plt.xlim(xmin - 1.5*dx, xmax + 2*dx) plt.legend() return
def test_cspline_interpolation(): ''' Tests E-field update, hall term (B x curl(B)) only by selection of inputs ''' grids = [8, 16, 32] errors = np.zeros(len(grids)) for NX, ii in zip(grids, list(range(len(grids)))): #NX = 32 #const.NX xmin = 0.0 #const.xmin xmax = 2*np.pi #const.xmax dx = xmax / NX x = np.arange(xmin - 1.5*dx, xmax + 2*dx, dx/100.) k = 1.0 marker_size = 20 # Physical location of nodes E_nodes = (np.arange(NX + 3) - 0.5) * dx B_nodes = (np.arange(NX + 3) - 1.0) * dx Bxc = np.cos(1.0*k*x) Byc = np.cos(2.0*k*x) Bzc = np.cos(3.0*k*x) Bx = np.cos(1.0*k*B_nodes) By = np.cos(2.0*k*B_nodes) Bz = np.cos(3.0*k*B_nodes) Bxe = np.cos(1.0*k*E_nodes) Bye = np.cos(2.0*k*E_nodes) Bze = np.cos(3.0*k*E_nodes) B_input = np.zeros((NX + 3, 3)) B_input[:, 0] = Bx B_input[:, 1] = By B_input[:, 2] = Bz ## TEST INTERPOLATION ## B_center = aux.interpolate_to_center_cspline3D(B_input, DX=dx) error_x = abs(B_center[:, 0] - Bxe).max() error_y = abs(B_center[:, 1] - Bye).max() error_z = abs(B_center[:, 2] - Bze).max() errors[ii] = np.max([error_x, error_y, error_z]) for ii in range(len(grids) - 1): order = np.log(errors[ii] / errors[ii + 1]) / np.log(2) print(order) plot = True if plot == True: plt.figure() plt.scatter(B_nodes, B_input[:, 0], s=marker_size, c='b', marker='x') plt.scatter(B_nodes, B_input[:, 1], s=marker_size, c='b', marker='x') plt.scatter(B_nodes, B_input[:, 2], s=marker_size, c='b', marker='x') plt.scatter(E_nodes, Bxe, s=marker_size, c='k', marker='o') plt.scatter(E_nodes, Bye, s=marker_size, c='k', marker='o') plt.scatter(E_nodes, Bze, s=marker_size, c='k', marker='o') plt.scatter(E_nodes, B_center[:, 0], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, B_center[:, 1], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, B_center[:, 2], s=marker_size, c='r', marker='x') plt.plot(x, Bxc, linestyle=':', c='k') plt.plot(x, Byc, linestyle=':', c='k') plt.plot(x, Bzc, linestyle=':', c='k') for kk in range(NX + 3): plt.axvline(E_nodes[kk], linestyle='--', c='r', alpha=0.2) plt.axvline(B_nodes[kk], linestyle='--', c='b', alpha=0.2) plt.axvline(xmin, linestyle='-', c='k', alpha=0.2) plt.axvline(xmax, linestyle='-', c='k', alpha=0.2) plt.xlim(xmin - 1.5*dx, xmax + 2*dx) plt.legend() return
def test_E_hall(): ''' Tests E-field update, hall term (B x curl(B)) only by selection of inputs ''' # ============================================================================= # grids = [32, 64, 128, 256, 512, 1024, 2048] # err_curl = np.zeros(len(grids)) # err_interp = np.zeros(len(grids)) # err_hall = np.zeros(len(grids)) # ============================================================================= #for NX, ii in zip(grids, range(len(grids))): NX = 32 #const.NX xmin = 0.0 #const.xmin xmax = 2*np.pi #const.xmax dx = xmax / NX k = 1.0 marker_size = 70 E_nodes = (np.arange(NX + 3) - 0.5) * dx # Physical location of nodes B_nodes = (np.arange(NX + 3) - 1.0) * dx ## INPUTS ## Bx = np.sin(1.0*k*B_nodes) By = np.sin(2.0*k*B_nodes) Bz = np.sin(3.0*k*B_nodes) Bxe = np.sin(1.0*k*E_nodes) Bye = np.sin(2.0*k*E_nodes) Bze = np.sin(3.0*k*E_nodes) dBy = 2.0 * k * np.cos(2.0*k*E_nodes) dBz = 3.0 * k * np.cos(3.0*k*E_nodes) B_input = np.zeros((NX + 3, 3)) B_input[:, 0] = Bx B_input[:, 1] = By B_input[:, 2] = Bz Be_input = np.zeros((NX + 3, 3)) Be_input[:, 0] = Bxe Be_input[:, 1] = Bye Be_input[:, 2] = Bze B_center = aux.interpolate_to_center_cspline3D(B_input, DX=dx) ## TEST CURL B (AGAIN JUST TO BE SURE) curl_B_FD = fields.get_curl_B(B_input, DX=dx) curl_B_anal = np.zeros((NX + 3, 3)) curl_B_anal[:, 1] = -dBz curl_B_anal[:, 2] = dBy ## ELECTRIC FIELD CALCULATION ## E_FD = fields.calculate_E( B_input, np.zeros((NX + 3, 3)), np.ones(NX + 3), DX=dx) E_FD2 = fields.calculate_E_w_exel(B_input, np.zeros((NX + 3, 3)), np.ones(NX + 3), DX=dx) E_anal = np.zeros((NX + 3, 3)) E_anal[:, 0] = - (Bye * dBy + Bze * dBz) E_anal[:, 1] = Bxe * dBy E_anal[:, 2] = Bxe * dBz E_anal /= const.mu0 # ============================================================================= # ## Calculate errors ## # err_curl[ii] = np.abs(curl_B_FD[1: -2, :] - curl_B_anal[1: -2, :]).max() # err_interp[ii] = np.abs(B_center[1: -2, :] - Be_input[1: -2, :] ).max() # err_hall[ii] = np.abs(E_FD[1: -2, :] - E_anal[1: -2, :] ).max() # # for ii in range(len(grids) - 1): # order_curl = np.log(err_curl[ii] / err_curl[ii + 1] ) / np.log(2) # order_interp = np.log(err_interp[ii] / err_interp[ii + 1]) / np.log(2) # order_hall = np.log(err_hall[ii] / err_hall[ii + 1] ) / np.log(2) # # print '' # print 'Grid reduction: {} -> {}'.format(grids[ii], grids[ii + 1]) # print 'Curl order: {}, \nC-spline Interpolation order: {}'.format(order_curl, order_interp) # print 'E-field Hall order: {}'.format(order_hall) # ============================================================================= plot = True if plot == True: # ============================================================================= # # Plot curl test # plt.figure() # plt.scatter(E_nodes, curl_B_anal[:, 1], s=marker_size, c='k') # plt.scatter(E_nodes, curl_B_anal[:, 2], s=marker_size, c='k') # plt.scatter(E_nodes, curl_B_FD[:, 1], s=marker_size, marker='x') # plt.scatter(E_nodes, curl_B_FD[:, 2], s=marker_size, marker='x') # # # Plot center-interpolated B test # plt.figure() # plt.scatter(B_nodes, B_input[:, 0], s=marker_size, c='b') # plt.scatter(B_nodes, B_input[:, 1], s=marker_size, c='b') # plt.scatter(B_nodes, B_input[:, 2], s=marker_size, c='b') # plt.scatter(E_nodes, B_center[:, 0], s=marker_size, c='r') # plt.scatter(E_nodes, B_center[:, 1], s=marker_size, c='r') # plt.scatter(E_nodes, B_center[:, 2], s=marker_size, c='r') # ============================================================================= # Plot E-field test solutions plt.scatter(E_nodes, E_anal[:, 0], s=marker_size, marker='o', c='k') plt.scatter(E_nodes, E_anal[:, 1], s=marker_size, marker='o', c='k') plt.scatter(E_nodes, E_anal[:, 2], s=marker_size, marker='o', c='k') plt.scatter(E_nodes, E_FD[:, 0], s=marker_size, c='b', marker='+') plt.scatter(E_nodes, E_FD[:, 1], s=marker_size, c='b', marker='+') plt.scatter(E_nodes, E_FD[:, 2], s=marker_size, c='b', marker='+') plt.scatter(E_nodes, E_FD2[:, 0], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, E_FD2[:, 1], s=marker_size, c='r', marker='x') plt.scatter(E_nodes, E_FD2[:, 2], s=marker_size, c='r', marker='x') for kk in range(NX + 3): plt.axvline(E_nodes[kk], linestyle='--', c='r', alpha=0.2) plt.axvline(B_nodes[kk], linestyle='--', c='b', alpha=0.2) plt.axvline(xmin, linestyle='-', c='k', alpha=0.2) plt.axvline(xmax, linestyle='-', c='k', alpha=0.2) plt.xlim(xmin - 1.5*dx, xmax + 2*dx) return