Ejemplo n.º 1
0
def collect_moments(pos, vel, Ie, W_elec, idx, ni, nu_plus, nu_minus, rho,
                    J_minus, J_plus, L, G, dt):
    '''
    Moment collection and position advance function.

    INPUT:
        pos    -- Particle positions (x)
        vel    -- Particle 3-velocities
        Ie     -- Particle leftmost to nearest E-node
        W_elec -- Particle TSC weighting across nearest, left, and right nodes
        idx    -- Particle species identifier
        DT     -- Timestep for position advance
        
    OUTPUT:
        pos     -- Advanced particle positions
        Ie      -- Updated leftmost to nearest E-nodes
        W_elec  -- Updated TSC weighting coefficients
        rho     -- Charge  density at +0.5 timestep
        J_plus  -- Current density at +0.5 timestep
        J_minus -- Current density at initial time (J0)
        G       -- "Gamma"  MHD variable for current advance
        L       -- "Lambda" MHD variable for current advance    
    '''
    ni *= 0.0
    rho *= 0.0
    nu_minus *= 0.0
    nu_plus *= 0.0
    J_minus *= 0.0
    J_plus *= 0.0
    L *= 0.0
    G *= 0.0

    deposit_velocity_moments(vel, Ie, W_elec, idx, nu_minus)
    particles.position_update(pos, vel, Ie, W_elec, dt)
    deposit_both_moments(pos, vel, Ie, W_elec, idx, ni, nu_plus)

    if smooth_sources == 1:
        for jj in range(Nj):
            ni[:, jj] = smooth(ni[:, jj])

            for kk in range(3):
                nu_plus[:, jj, kk] = smooth(nu_plus[:, jj, kk])
                nu_minus[:, jj, kk] = smooth(nu_minus[:, jj, kk])

    for jj in range(Nj):
        rho += ni[:, jj] * n_contr[jj] * charge[jj]
        L += ni[:, jj] * n_contr[jj] * charge[jj]**2 / mass[jj]

        for kk in range(3):
            J_minus[:, kk] += nu_minus[:, jj, kk] * n_contr[jj] * charge[jj]
            J_plus[:, kk] += nu_plus[:, jj, kk] * n_contr[jj] * charge[jj]
            G[:, kk] += nu_plus[:, jj,
                                kk] * n_contr[jj] * charge[jj]**2 / mass[jj]

    for ii in range(rho.shape[0]):
        if rho[ii] < min_dens * ne * q:
            rho[ii] = min_dens * ne * q

    return
Ejemplo n.º 2
0
def init_collect_moments(part, DT):
    '''Primary moment collection and position advance function.

    INPUT:
        part    -- Particle array
        weights -- Weights array
        DT      -- Timestep
        init    -- Flag to indicate if this is the first time the function is called**
        
    ** At first initialization of this function, J- is equivalent to J0 and rho_0 is
    initial density (usually not returned)
    
    '''
    size = NX + 2

    rho_0 = np.zeros(size)
    rho = np.zeros(size)
    J_plus = np.zeros((size, 3))
    J_minus = np.zeros((size, 3))
    L = np.zeros(size)
    G = np.zeros((size, 3))

    ni_init, nu_minus = collect_both_moments(part)
    part = particles.position_update(part, DT)
    ni, nu_plus = collect_both_moments(part)

    if smooth_sources == 1:
        for jj in range(Nj):
            ni[:, jj] = smooth(ni[:, jj])

            for kk in range(3):
                nu_plus[:, jj, kk] = smooth(nu_plus[:, jj, kk])
                nu_minus[:, jj, kk] = smooth(nu_minus[:, jj, kk])

    for jj in range(Nj):
        rho_0 += ni_init[:, jj] * charge[jj]
        rho += ni[:, jj] * charge[jj]
        L += ni[:, jj] * charge[jj]**2 / mass[jj]

        for kk in range(3):
            J_minus[:, kk] += nu_minus[:, jj, kk] * charge[jj]
            J_plus[:, kk] += nu_plus[:, jj, kk] * charge[jj]
            G[:, kk] += nu_plus[:, jj, kk] * charge[jj]**2 / mass[jj]

    return part, rho_0, rho, J_plus, J_minus, G, L
Ejemplo n.º 3
0
def animate_moving_weight():
    fig = plt.figure(figsize=(12, 8))
    ax  = fig.add_subplot(1,1,1)
    x   = np.arange(const.NX + 3)
    
    dt       = 0.1
    vel      = np.array([[0.3 * const.dx / dt],
                         [ 0.],
                         [ 0.]])
    
    position = np.array([0.0]) 

    E_nodes = (np.arange(const.NX + 3) - 0.5) #* const.dx
    B_nodes = (np.arange(const.NX + 3) - 1.0) #* const.dx
    
    for ii in range(150):
        dns_test  = np.zeros(const.NX + 3) 

        pos, left_nodes, weights = particles.position_update(position, vel, dt)
        
        for jj in range(3):
                dns_test[left_nodes + jj] = weights[jj]
                
        y = dns_test

        ax.clear()
        ax.plot(x, y)
        ax.set_xlim(-1.5, const.NX + 2)
        ax.set_ylim(0, 1.5)
        ax.text(1, 1.4, 'Total Weight: {}'.format(dns_test.sum()))
        
        ax.scatter(pos/const.dx, 1.0, c='r')
    
        for kk in range(const.NX + 3):
            ax.axvline(E_nodes[kk], linestyle='--', c='r', alpha=0.2)
            ax.axvline(B_nodes[kk], linestyle='--', c='b', alpha=0.2)
            
            ax.axvline(const.xmin/const.dx, linestyle='-', c='k', alpha=0.2)
            ax.axvline(const.xmax/const.dx, linestyle='-', c='k', alpha=0.2)
    
        plt.pause(0.05)
    plt.show()
Ejemplo n.º 4
0
def check_timestep(qq, DT, part, B, E, dns, maxtime, data_dump_iter,
                   plot_dump_iter):
    max_V = np.max(part[3, :])
    #k_wave          = np.pi / dx

    B_cent = np.zeros((NX + 2, 3))

    for ii in range(3):
        B_cent[1:-1, ii] = 0.5 * (B[:-1, ii] + B[1:, ii])

    B_tot = np.sqrt(B_cent[:, 0]**2 + B_cent[:, 1]**2 + B_cent[:, 2]**2)
    high_rat = np.max(charge / mass)

    gyfreq = high_rat * max(abs(B_tot)) / (2 * np.pi)  # Gyrofrequency
    #elecfreq        = high_rat*max(abs(E[:, 0] / max_V))            # Electron acceleration "frequency"
    #dispfreq        = (k_wave ** 2) * np.max(B_tot / (mu0 * dns))   # Dispersion frequency

    ion_ts = orbit_res / gyfreq  # Timestep to resolve gyromotion
    #acc_ts          = orbit_res / elecfreq              # Timestep to resolve electric field acceleration
    #dis_ts          = orbit_res / dispfreq              # Timestep to resolve magnetic field dispersion
    vel_ts = 0.60 * dx / max_V  # Timestep to satisfy CFL condition: Fastest particle doesn't traverse more than 'half' a cell in one time step
    # Slightly larger than half to stop automatically halving DT at start
    DT_new = min(ion_ts, vel_ts)  # Smallest of the two (four, later)

    change_flag = 0
    if DT_new < DT:
        part = particles.position_update(
            part,
            -0.5 * DT)  # Roll back particle position before halving timestep

        change_flag = 1
        DT *= 0.5
        maxtime *= 2
        qq *= 2

        if plot_dump_iter != None:
            plot_dump_iter *= 2

        if data_dump_iter != None:
            data_dump_iter *= 2

    return part, qq, DT, maxtime, data_dump_iter, plot_dump_iter, change_flag
Ejemplo n.º 5
0
def check_timestep(qq, DT, pos, vel, Ie, W_elec, B, E, dns, max_inc,
                   part_save_iter, field_save_iter, subcycles):
    max_Vx = vel[0, :].max()
    max_V = vel.max()

    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.qm_ratios.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 * (np.abs(E[:, 0] / max_V)).max()
        freq_ts = const.freq_res / elecfreq
    else:
        freq_ts = ion_ts

    vel_ts = 0.75 * const.dx / max_Vx
    DT_part = min(freq_ts, vel_ts, ion_ts)

    # Reduce timestep
    change_flag = 0
    if DT_part < 0.9 * DT:
        particles.position_update(pos, vel, Ie, W_elec, -0.5 * DT)

        change_flag = 1
        DT *= 0.5
        max_inc *= 2
        qq *= 2
        part_save_iter *= 2
        field_save_iter *= 2
        print('Timestep halved. Syncing particle velocity/position with DT =',
              DT)

    # Increase timestep (only if previously decreased, or everything's even - saves wonky cuts)
    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.position_update(pos, vel, Ie, W_elec, -0.5 * DT)

        change_flag = 1
        DT *= 2.0
        max_inc //= 2
        qq //= 2
        part_save_iter //= 2
        field_save_iter //= 2

        print('Timestep doubled. Syncing particle velocity/position with DT =',
              DT)

    if const.adaptive_subcycling == 1:
        k_max = np.pi / const.dx
        dispfreq = (k_max**
                    2) * (B_tot /
                          (const.mu0 * dns)).max()  # Dispersion frequency
        dt_sc = const.freq_res / dispfreq
        new_subcycles = int(DT / dt_sc + 1)

        if subcycles < 0.75 * new_subcycles:
            subcycles *= 2
            print('Number of subcycles per timestep doubled to', subcycles)

        if subcycles > 3.0 * new_subcycles and subcycles % 2 == 0:
            subcycles //= 2
            print('Number of subcycles per timestep halved to', subcycles)

        if subcycles >= 1000:
            subcycles = 1000
            print('Maxmimum number of subcycles reached - she gon\' blow')

    return qq, DT, max_inc, part_save_iter, field_save_iter, change_flag, subcycles
Ejemplo n.º 6
0
def check_timestep(qq, DT, pos, vel, B, E, dns, max_inc, data_iter, plot_iter,
                   subcycles):
    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"
        freq_ts = const.freq_res / elecfreq
    else:
        freq_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(freq_ts, vel_ts,
                  ion_ts)  # Smallest of the particle conditions

    change_flag = 0
    if DT_part < 0.9 * DT:
        pos, Ie, W_elec = particles.position_update(
            pos, vel,
            -0.5 * DT)  # Roll back particle position before halving timestep

        change_flag = 1
        DT *= 0.5
        max_inc *= 2
        qq *= 2

        if plot_iter != None:
            plot_iter *= 2

        if data_iter != None:
            data_iter *= 2

    elif DT_part >= 4.0 * DT and qq % 2 == 0:
        pos, Ie, W_elec = particles.position_update(
            pos, vel,
            -0.5 * DT)  # Roll back particle position before halving timestep

        change_flag = 2
        DT *= 2.0
        max_inc = (
            max_inc + 1
        ) / 2  # This ensures timesteps aren't cut off by halving (more likely to be added, which isn't bad)
        qq /= 2

        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

    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 > 1000:
            const.adaptive_subcycling = False
            subcycles = 1000
    else:
        pass

    return pos, qq, DT, max_inc, data_iter, plot_iter, change_flag, subcycles
Ejemplo n.º 7
0
def check_timestep(qq, DT, pos, vel, B, E, dns, max_inc, part_save_iter, field_save_iter, subcycles):
    max_Vx          = np.max(vel[0, :])
    max_V           = np.max(vel)
    
    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"
        freq_ts         = const.freq_res / elecfreq                            
    else:
        freq_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(freq_ts, vel_ts, ion_ts)                       # Smallest of the particle conditions

    # Reduce timestep
    if DT_part < 0.9*DT:
        pos, Ie, W_elec = particles.position_update(pos, vel, -0.5*DT)
        
        change_flag      = 1
        DT              *= 0.5
        max_inc         *= 2
        qq              *= 2
        part_save_iter  *= 2
        field_save_iter *= 2
    
    # Increase timestep (only if previously decreased, or everything's even - saves wonky cuts)
    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:
        pos, Ie, W_elec = particles.position_update(pos, vel, -0.5*DT)
        
        change_flag       = 2
        DT               *= 2.0
        max_inc         //= 2
        qq              //= 2
        part_save_iter  //= 2
        field_save_iter //= 2
    else:
        change_flag       = 0


    if const.adaptive_subcycling == True:
        k_max           = np.pi / const.dx
        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 and subcycles%2 == 0:                                      
            subcycles //= 2
            print('Number of subcycles per timestep halved to {}'.format(subcycles))
            
        if subcycles > 1000:
            const.adaptive_subcycling = False
            subcycles = 1000
            print('Maxmimum number of subcycles reached - potentially unstable solutions...')


    return pos, qq, DT, max_inc, part_save_iter, field_save_iter, change_flag, subcycles
Ejemplo n.º 8
0
def init_collect_moments(pos, vel, Ie, W_elec, idx, DT):
    '''Moment collection and position advance function. Specifically used at initialization or
    after timestep synchronization.

    INPUT:
        pos    -- Particle positions (x)
        vel    -- Particle 3-velocities
        Ie     -- Particle leftmost to nearest E-node
        W_elec -- Particle TSC weighting across nearest, left, and right nodes
        idx    -- Particle species identifier
        DT     -- Timestep for position advance
        
    OUTPUT:
        pos     -- Advanced particle positions
        Ie      -- Updated leftmost to nearest E-nodes
        W_elec  -- Updated TSC weighting coefficients
        rho_0   -- Charge  density at initial time (p0)
        rho     -- Charge  density at +0.5 timestep
        J_init  -- Current density at initial time (J0)
        J_plus  -- Current density at +0.5 timestep
        G       -- "Gamma"  MHD variable for current advance : Current-like
        L       -- "Lambda" MHD variable for current advance :  Charge-like
    '''
    size = NX + 3

    rho_0 = np.zeros(size)
    rho = np.zeros(size)
    J_plus = np.zeros((size, 3))
    J_init = np.zeros((size, 3))
    L = np.zeros(size)
    G = np.zeros((size, 3))

    ni_init, nu_init = deposit_both_moments(
        pos, vel, Ie, W_elec, idx)  # Collects sim_particles/cell/species
    pos, Ie, W_elec = particles.position_update(pos, vel, DT)
    ni, nu_plus = deposit_both_moments(pos, vel, Ie, W_elec, idx)

    if smooth_sources == 1:
        for jj in range(Nj):
            ni[:, jj] = smooth(ni[:, jj])

            for kk in range(3):
                nu_plus[:, jj, kk] = smooth(nu_plus[:, jj, kk])
                nu_init[:, jj, kk] = smooth(nu_init[:, jj, kk])

    for jj in range(Nj):
        rho_0 += ni_init[:, jj] * n_contr[jj] * charge[jj]
        rho += ni[:, jj] * n_contr[jj] * charge[jj]
        L += ni[:, jj] * n_contr[jj] * charge[jj]**2 / mass[jj]

        for kk in range(3):
            J_init[:, kk] += nu_init[:, jj, kk] * n_contr[jj] * charge[jj]
            J_plus[:, kk] += nu_plus[:, jj, kk] * n_contr[jj] * charge[jj]
            G[:, kk] += nu_plus[:, jj,
                                kk] * n_contr[jj] * charge[jj]**2 / mass[jj]

    for ii in range(size):
        if rho_0[ii] < min_dens * ne * q:
            rho_0[ii] = min_dens * ne * q

        if rho[ii] < min_dens * ne * q:
            rho[ii] = min_dens * ne * q
    return pos, Ie, W_elec, rho_0, rho, J_plus, J_init, G, L