Esempio n. 1
0
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
Esempio n. 2
0
def calculate_E(B, J, qn):
    '''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_i -- Ion current density. Source term, based on particle velocities
        n_i -- Ion number density. Source term, based on particle positions

    OUTPUT:
        E_out -- Updated electric field array
    '''
    size = NX + 2

    E_out = np.zeros((size, 3))  # Output array - new electric field
    Te = np.ones(size) * Te0  # Electron temperature array

    B_center = interpolate_to_center(B)
    JxB = cross_product(J, B_center)
    curlB = get_curl_B(B)
    BdB = cross_product(B_center, curlB) / mu0
    del_p = get_grad_P(qn, Te)

    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[NX]
    E_out[NX + 1] = E_out[1]
    return E_out
Esempio n. 3
0
def calculate_E(B, Ji, q_dens, E, Ve, Te, Te0, temp3De, temp3Db, grad_P,
                E_damping_array, qq, DT, half_flag):
    '''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
    
    NOTE: Sending all but the last element in the cross_product() function seems clumsy... but it works!
    Check :: Does it dereference anything?
    
    12/06/2020 -- Added E-field damping option as per Hu & Denton (2010), Ve x B term only
    '''
    # No driven wave
    if driver_status == 0:
        pass
    # Single point source
    elif driver_status == 1:
        add_J_ext(qq, Ji, DT, half_flag=half_flag)
    # Multi-point source
    elif driver_status == 2:
        add_J_ext_pol(qq, Ji, DT, half_flag=half_flag)

    curl_B_term(B, temp3De)  # temp3De is now curl B term

    Ve[:, 0] = (Ji[:, 0] - temp3De[:, 0]) / q_dens
    Ve[:, 1] = (Ji[:, 1] - temp3De[:, 1]) / q_dens
    Ve[:, 2] = (Ji[:, 2] - temp3De[:, 2]) / q_dens

    get_electron_temp(q_dens, Te, Te0)

    get_grad_P(
        q_dens, Te, grad_P, temp3Db[:, 0]
    )  # temp1D is now del_p term, temp3D2 slice used for computation
    aux.interpolate_edges_to_center(B, temp3Db)  # temp3db is now B_center

    aux.cross_product(Ve, temp3Db[:temp3Db.shape[0] - 1, :],
                      temp3De)  # temp3De is now Ve x B term
    if E_damping == 1:
        temp3De *= E_damping_array

    E[:, 0] = -temp3De[:, 0] - grad_P[:] / q_dens[:]
    E[:, 1] = -temp3De[:, 1]
    E[:, 2] = -temp3De[:, 2]

    # Diagnostic flag for testing
    if disable_waves == 1:
        E *= 0.
    return
Esempio n. 4
0
def push_current(J_in, J_out, E, B, L, G, dt):
    '''Uses an MHD-like equation to advance the current with a moment method as 
    per Matthews (1994) CAM-CL method. Fills in ghost cells at edges (excluding very last one)
    
    INPUT:
        J  -- Ionic current (J plus)
        E  -- Electric field
        B  -- Magnetic field (offset from E by 0.5dx)
        L  -- "Lambda" MHD variable
        G  -- "Gamma"  MHD variable
        dt -- Timestep
        
    OUTPUT:
        J_plus in main() becomes J_half (same memory space)
    '''
    J_out *= 0
    B_center = interpolate_to_center_cspline3D(B)
    G_cross_B = cross_product(G, B_center)

    for ii in range(3):
        J_out[:,
              ii] = J_in[:, ii] + 0.5 * dt * (L * E[:, ii] + G_cross_B[:, ii])

    J_out[0] = J_out[J_out.shape[0] - 3]
    J_out[J_out.shape[0] - 2] = J_out[1]
    J_out[J_out.shape[0] - 1] = J_out[2]
    return
Esempio n. 5
0
def test_cross_product():
    npts = 100
    x    = np.linspace(0, 1, npts)
    
    A = np.ones((npts, 3))
    B = np.ones((npts, 3))
    anal_result = np.ones((npts, 3))
    
    s1x = np.sin(2 * np.pi *   x)
    s2x = np.sin(2 * np.pi * 2*x)
    s3x = np.sin(2 * np.pi * 3*x)
    
    c1x = np.cos(2 * np.pi *   x)
    c2x = np.cos(2 * np.pi * 2*x)
    c3x = np.cos(2 * np.pi * 3*x)
    
    A[:, 0] = s1x ; B[:, 0] = c1x
    A[:, 1] = s2x ; B[:, 1] = c2x
    A[:, 2] = s3x ; B[:, 2] = c3x
    
    anal_result[:, 0] =   s2x*c3x - c2x*s3x
    anal_result[:, 1] = -(s1x*c3x - c1x*s3x)
    anal_result[:, 2] =   s2x*c3x - c2x*s3x

    test_result = aux.cross_product(A, B)
    diff        = test_result - anal_result
    print(diff)

    return
Esempio n. 6
0
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
Esempio n. 7
0
def push_E(B, J_i, n_i, dt):
    '''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_i -- Ion current density. Source term, based on particle velocities
        n_i -- Ion number density. Source term, based on particle positions
        dt  -- Simulation time cadence

    OUTPUT:
        E_out -- Updated electric field array
    '''
    size = NX + 2

    E_out = np.zeros((size, 3))  # Output array - new electric field
    JxB = np.zeros((size, 3))  # V cross B holder
    BdB = np.zeros((size, 3))  # B cross del cross B holder
    del_p = np.zeros((size, 3))  # Electron pressure tensor gradient array
    J = np.zeros((size, 3))  # Ion current
    qn = np.zeros(size, )  # Ion charge density

    Te = np.ones(size) * Te0  # Electron temperature array

    # Calculate change/current summations over each species
    for jj in range(Nj):
        qn += charge[jj] * n_i[:, jj]  # Total charge density, sum(qj * nj)

        for kk in range(3):
            J[:,
              kk] += J_i[:, jj,
                         kk]  # Total ion current vector: J_k = qj * nj * Vj_k

    B_center = 0.5 * (B[:-1, :] + B[1:, :])
    JxB = cross_product(J[1:-1, :], B_center)

    for mm in range(1, size - 1):

        # B cross curl B
        BdB[mm, 0] = B[mm, 1] * ((B[mm + 1, 1] - B[mm - 1, 1]) /
                                 (2 * dx)) + B[mm, 2] * (
                                     (B[mm + 1, 2] - B[mm - 1, 2]) / (2 * dx))
        BdB[mm, 1] = (-B[mm, 0]) * ((B[mm + 1, 1] - B[mm - 1, 1]) / (2 * dx))
        BdB[mm, 2] = (-B[mm, 0]) * ((B[mm + 1, 2] - B[mm - 1, 2]) / (2 * dx))

        # del P
        del_p[mm, 0] = ((qn[mm + 1] - qn[mm - 1]) / (2 * dx * q)) * kB * Te[mm]
        del_p[mm, 1] = 0
        del_p[mm, 2] = 0

    # Final Calculation
    E_out[:, 0] = (-JxB[:, 0] - del_p[:, 0] - (BdB[:, 0] / mu0)) / (qn[:])
    E_out[:, 1] = (-JxB[:, 1] - del_p[:, 1] - (BdB[:, 1] / mu0)) / (qn[:])
    E_out[:, 2] = (-JxB[:, 2] - del_p[:, 2] - (BdB[:, 2] / mu0)) / (qn[:])

    E_out[0] = E_out[NX]
    E_out[NX + 1] = E_out[1]
    return E_out
Esempio n. 8
0
def calculate_E(B, Ji, q_dens, E, Ve, Te, Te0, temp3De, temp3Db, grad_P):
    '''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
    
    To Do: In the interpolation function, add on B0 along with the 
    spline interpolation. No other part of the code requires B0 in the nodes.
    '''
    curl_B_term(B, temp3De)  # temp3De is now curl B term

    Ve[:, 0] = (Ji[:, 0] - temp3De[:, 0]) / q_dens
    Ve[:, 1] = (Ji[:, 1] - temp3De[:, 1]) / q_dens
    Ve[:, 2] = (Ji[:, 2] - temp3De[:, 2]) / q_dens

    get_electron_temp(q_dens, Te, Te0)

    get_grad_P(
        q_dens, Te, grad_P, temp3Db[:, 0]
    )  # temp1D is now del_p term, temp3D2 slice used for computation
    aux.interpolate_edges_to_center(B, temp3Db)  # temp3db is now B_center

    aux.cross_product(Ve, temp3Db, temp3De)  # temp3De is now Ve x B term

    E[:, 0] = -temp3De[:, 0] - grad_P[:] / q_dens[:]
    E[:, 1] = -temp3De[:, 1]
    E[:, 2] = -temp3De[:, 2]

    # Diagnostic flag for testing
    if disable_waves == True:
        E *= 0.
    return
Esempio n. 9
0
def push_current(J, E, B, L, G, dt):
    '''Uses an MHD-like equation to advance the current with a moment method. 
    Could probably be shortened with loops.
    '''
    J_out = np.zeros((NX + 2, 3))

    B_center = interpolate_to_center(B)
    G_cross_B = cross_product(G, B_center)

    for ii in range(3):
        J_out[:, ii] = J[:, ii] + 0.5 * dt * (L * E[:, ii] + G_cross_B[:, ii])
    return J_out
Esempio n. 10
0
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 
Esempio n. 11
0
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