Example #1
0
def time_evolution(u=None):
    '''
    Solves the wave equation
    
    .. math:: u^{t_n + 1} = b(t_n) \\times A
    
    iterated over time.shape[0] time steps t_n 

    Second order time stepping is used.
    It increases the accuracy of the wave evolution.

    The second order time-stepping would be
    `U^{n + 1/2} = U^n + dt / 2 (A^{-1} B(U^n))`
    `U^{n + 1}   = U^n + dt     (A^{-1} B(U^{n + 1/2}))`
    
    Returns
    -------
    None
    '''

    # Creating a folder to store hdf5 files. If it doesn't exist.
    results_directory = 'results/hdf5_%02d' % (int(params.N_LGL))

    if not os.path.exists(results_directory):
        os.makedirs(results_directory)

    element_LGL = params.element_LGL
    delta_t = params.delta_t
    shape_u_n = utils.shape(u)
    time = params.time
    A_inverse = af.tile(af.inverse(A_matrix()), d0=1, d1=1, d2=shape_u_n[2])

    element_boundaries = af.np_to_af_array(params.np_element_array)

    for t_n in trange(0, time.shape[0]):
        if (t_n % 20) == 0:
            h5file = h5py.File(
                'results/hdf5_%02d/dump_timestep_%06d' %
                (int(params.N_LGL), int(t_n)) + '.hdf5', 'w')
            dset = h5file.create_dataset('u_i', data=u, dtype='d')

            dset[:, :] = u[:, :]

        # Code for solving 1D Maxwell's Equations
        # Storing u at timesteps which are multiples of 100.
        #if (t_n % 5) == 0:
        #h5file = h5py.File('results/hdf5_%02d/dump_timestep_%06d' \
        #%(int(params.N_LGL), int(t_n)) + '.hdf5', 'w')
        #Ez_dset   = h5file.create_dataset('E_z', data = u[:, :, 0],
        #dtype = 'd')
        #By_dset   = h5file.create_dataset('B_y', data = u[:, :, 1],
        #dtype = 'd')

        #Ez_dset[:, :] = u[:, :, 0]
        #By_dset[:, :] = u[:, :, 1]

        u += RK4_timestepping(A_inverse, u, delta_t)
Example #2
0
def surface_term(u_n):
    '''

    Calculates the surface term,
    :math:`L_p(1) f_i - L_p(-1) f_{i - 1}`
    using the lax_friedrichs_flux function and lagrange_basis_value
    from params module.
    
    Parameters
    ----------
    u_n : arrayfire.Array [N_LGL N_Elements M 1]
          Amplitude of the wave at the mapped LGL nodes of each element.
          This code will work for multiple :math:`M` ``u_n``
          
    Returns
    -------
    surface_term : arrayfire.Array [N_LGL N_Elements M 1]
                   The surface term represented in the form of an array,
                   :math:`L_p (1) f_i - L_p (-1) f_{i - 1}`, where p varies
                   from zero to :math:`N_{LGL}` and i from zero to
                   :math:`N_{Elements}`. p varies along the rows and i along
                   columns.
    
    **See:** `PDF`_ describing the algorithm to obtain the surface term.
    
    .. _PDF: https://goo.gl/Nhhgzx

    '''

    shape_u_n = utils.shape(u_n)

    L_p_minus1 = af.tile(params.lagrange_basis_value[:, 0],
                         d0=1,
                         d1=1,
                         d2=shape_u_n[2])
    L_p_1 = af.tile(params.lagrange_basis_value[:, -1],
                    d0=1,
                    d1=1,
                    d2=shape_u_n[2])
    #[NOTE]: Uncomment to use lax friedrichs flux
    #f_i          = lax_friedrichs_flux(u_n)

    #[NOTE]: Uncomment to use upwind flux for uncoupled advection equations
    f_i = upwind_flux(u_n)

    #[NOTE]: Uncomment to use upwind flux for Maxwell's equations
    #f_i          = upwind_flux_maxwell_eq(u_n)

    f_iminus1 = af.shift(f_i, 0, 1)

    surface_term = utils.matmul_3D(L_p_1, f_i) \
                 - utils.matmul_3D(L_p_minus1, f_iminus1)

    return surface_term
Example #3
0
def lag_interpolation_2d(u_e_ij, Li_Lj_coeffs):
    '''
    Does the lagrange interpolation of a function.
    
    Parameters
    ----------
    
    u_e_ij : af.Array [N_LGL^2 N_elements 1 1]
             Value of the function calculated at the :math:`(\\xi_i, \\eta_j)`
             points in this form
             
             .. math:: \\xi_i = [\\xi_0, \\xi_0, ..., \\xi_0, \\xi_1, \\
                       ... ..., \\xi_N]
             .. math:: \\eta_j = [\\eta_0, \\eta_1, ..., \\eta_N, \\
                       \\eta_0, ... ..., \\eta_N]

    N_LGL : int
            Number of LGL points

    Returns
    -------
    interpolated_f : af.Array [N_LGL N_LGL N_elements 1]
                     Interpolation polynomials for ``N_elements`` elements.
    '''
    Li_xi_Lj_eta_coeffs = af.tile(Li_Lj_coeffs,
                                  d0=1,
                                  d1=1,
                                  d2=1,
                                  d3=utils.shape(u_e_ij)[1])
    u_e_ij = af.reorder(u_e_ij, 2, 3, 0, 1)

    f_ij_Li_Lj_coeffs = af.broadcast(utils.multiply, u_e_ij,
                                     Li_xi_Lj_eta_coeffs)
    interpolated_f = af.reorder(af.sum(f_ij_Li_Lj_coeffs, 2), 0, 1, 3, 2)

    return interpolated_f
Example #4
0
def volume_integral_flux(u_n):
    '''
    Calculates the volume integral of flux in the wave equation.
    :math:`\\int_{-1}^1 f(u) \\frac{d L_p}{d\\xi} d\\xi`
    This will give N values of flux integral as p varies from 0 to N - 1.
    
    This integral is carried out using the analytical form of the integrand
    obtained as a linear combination of Lagrange basis polynomials.
    This integrand is the used in the integrate() function.
    Calculation of volume integral flux using N_LGL Lobatto quadrature points
    can be vectorized and is much faster.
    
    Parameters
    ----------
    u : arrayfire.Array [N_LGL N_Elements M 1]
        Amplitude of the wave at the mapped LGL nodes of each element. This
        function can computer flux for :math:`M` :math:`u`.
            
    Returns
    -------
    flux_integral : arrayfire.Array [N_LGL N_Elements M 1]
                    Value of the volume integral flux. It contains the integral
                    of all N_LGL * N_Element integrands.
    '''
    shape_u_n = utils.shape(u_n)
    
    # The coefficients of dLp / d\xi
    diff_lag_coeff  = params.dl_dxi_coeffs

    lobatto_nodes   = params.lobatto_quadrature_nodes
    Lobatto_weights = params.lobatto_weights_quadrature

    #nodes_tile   = af.transpose(af.tile(lobatto_nodes, 1, diff_lag_coeff.shape[1]))
    #power        = af.flip(af.range(diff_lag_coeff.shape[1]))
    #power_tile   = af.tile(power, 1, params.N_quad)
    #nodes_power  = nodes_tile ** power_tile
    #weights_tile = af.transpose(af.tile(Lobatto_weights, 1, diff_lag_coeff.shape[1]))
    #nodes_weight = nodes_power * weights_tile

    #dLp_dxi      = af.matmul(diff_lag_coeff, nodes_weight)
    
    dLp_dxi      = af.np_to_af_array(params.b_matrix)


    # The first option to calculate the volume integral term, directly uses
    # the Lobatto quadrature instead of using the integrate() function by
    # passing the coefficients of the Lagrange interpolated polynomial.
    if(params.volume_integral_scheme == 'lobatto_quadrature' \
        and params.N_quad == params.N_LGL):

        # Flux using u_n, reordered to 1 X N_LGL X N_Elements array.
        F_u_n                  = af.reorder(flux_x(u_n), 3, 0, 1, 2)
        F_u_n = af.tile(F_u_n, d0 = params.N_LGL)

        # Multiplying with dLp / d\xi
        integral_expansion     = af.broadcast(utils.multiply,
                                            dLp_dxi, F_u_n)

    #     # Using the quadrature rule.
        flux_integral = af.sum(integral_expansion, 1)

        flux_integral = af.reorder(flux_integral, 0, 2, 3, 1)

    # Using the integrate() function to calculate the volume integral term
    # by passing the Lagrange interpolated polynomial.
    else:
        #print('option3')
        analytical_flux_coeffs = af.transpose(af.moddims(u_n,
                                                        d0 = params.N_LGL,
                                                        d1 = params.N_Elements \
                                                            * shape_u_n[2]))
        
        analytical_flux_coeffs = flux_x(
            lagrange.lagrange_interpolation(analytical_flux_coeffs))
        analytical_flux_coeffs = af.transpose(
            af.moddims(af.transpose(analytical_flux_coeffs),
                       d0 = params.N_LGL, d1 = params.N_Elements,
                       d2 = shape_u_n[2]))
        
        analytical_flux_coeffs = af.reorder(analytical_flux_coeffs,
                                            d0 = 3, d1 = 1, d2 = 0, d3 = 2)
        analytical_flux_coeffs = af.tile(analytical_flux_coeffs,
                                         d0 = params.N_LGL)
        analytical_flux_coeffs = af.moddims(
            af.transpose(analytical_flux_coeffs),
            d0 = params.N_LGL,
            d1 = params.N_LGL * params.N_Elements, d2 = 1,
            d3 = shape_u_n[2])
        
        analytical_flux_coeffs = af.moddims(
            analytical_flux_coeffs, d0 = params.N_LGL,
            d1 = params.N_LGL * params.N_Elements * shape_u_n[2],
            d2 = 1, d3 = 1)
        
        analytical_flux_coeffs = af.transpose(analytical_flux_coeffs)

        dl_dxi_coefficients    = af.tile(af.tile(params.dl_dxi_coeffs,
                                                 d0 = params.N_Elements), \
                                         d0 = shape_u_n[2])

        volume_int_coeffs = utils.poly1d_product(dl_dxi_coefficients,
                                                analytical_flux_coeffs)
        
        flux_integral = lagrange.integrate(volume_int_coeffs)
        flux_integral = af.moddims(af.transpose(flux_integral),
                                   d0 = params.N_LGL,
                                   d1 = params.N_Elements,
                                   d2 = shape_u_n[2])

    return flux_integral