diff --git a/.gitignore b/.gitignore index 1b30639..d8b3dc5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ __pycache__/ *.jpg *.png *.svg +*.hdf5 # C extensions *.so diff --git a/README.md b/README.md index 0efb47c..f6629f6 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,12 @@ To run the unit tests, enter the following commands $ cd path/to/the/DG_Maxwell/repo $ pytest --verbose -r P --color=yes ``` - +* The parameters of the simulation are stored in params.py in + the app folder, These can be changed accordingly. + +* The images of the wave are stored in the `1D_wave_images` folder. ## Maintainers - Aman Abhishek Tiwari - ![aman@quazartech.com](aman@quazartech.com) -- Balavarun P - ![Github Profile](https://github.com/balavarun5) +- Balavarun P - ![f2013462@pilani.bits-pilani.ac.in](f2013462@pilani.bits-pilani.ac.in) - Manichandra Morumpudi - ![mani@quazartech.com](mani@quazartech.com) diff --git a/dg_maxwell/lagrange.py b/dg_maxwell/lagrange.py index 3aec79f..db9cf6b 100644 --- a/dg_maxwell/lagrange.py +++ b/dg_maxwell/lagrange.py @@ -11,10 +11,12 @@ def LGL_points(N): ''' - Calculates : math:`N` Legendre-Gauss-Lobatto (LGL) points. - LGL points are the roots of the polynomial - :math:`(1 - \\xi^2) P_{n - 1}(\\xi)` = 0` - Where :math:`P_{n}(\\xi)` are the Legendre polynomials. + Calculates : math: `N` Legendre-Gauss-Lobatto (LGL) points. + LGL points are the roots of the polynomial + + :math: `(1 - \\xi ** 2) P_{n - 1}'(\\xi) = 0` + + Where :math: `P_{n}(\\xi)` are the Legendre polynomials. This function finds the roots of the above polynomial. Parameters @@ -25,13 +27,13 @@ def LGL_points(N): Returns ------- - + lgl : arrayfire.Array [N 1 1 1] The Lagrange-Gauss-Lobatto Nodes. **See:** `document`_ .. _document: https://goo.gl/KdG2Sv - + ''' xi = np.poly1d([1, 0]) legendre_N_minus_1 = N * (xi * sp.legendre(N - 1) - sp.legendre(N)) @@ -49,29 +51,25 @@ def lobatto_weights(n): Parameters ---------- - n : int Lobatto weights for n quadrature points. Returns ------- - Lobatto_weights : arrayfire.Array An array of lobatto weight functions for the given x points and index. - - + **See:** Gauss-Lobatto weights Wikipedia `link`_. .. _link: https://goo.gl/kYqTyK - - **Examples** lobatto_weight_function(4) returns the Gauss-Lobatto weights which are to be used with the Lobatto nodes 'LGL_points(4)' to integrate using Lobatto quadrature. + ''' xi_LGL = LGL_points(n) @@ -85,27 +83,28 @@ def lobatto_weights(n): def gauss_nodes(n): ''' - Calculates :math:`N` Gaussian nodes used for Integration by - Gaussian quadrature. - - Gaussian node :math:`x_i` is the :math:`i^{th}` root of :math:`P_n(\\xi)`. - Where, :math:`P_{n}(\\xi)` are the Legendre polynomials. + Calculates :math: `N` Gaussian nodes used for Integration by + Gaussia quadrature. + Gaussian node :math: `x_i` is the `i^{th}` root of + :math: `P_n(\\xi)` + Where :math: `P_{n}(\\xi)` are the Legendre polynomials. Parameters ---------- - + n : int The number of Gaussian nodes required. Returns ------- - + gauss_nodes : numpy.ndarray - The Gauss nodes :math:`x_i`. + The Gauss nodes :math: `x_i`. **See:** A Wikipedia article about the Gauss-Legendre quadrature `here`_ .. _here: https://goo.gl/9gqLpe + ''' legendre = sp.legendre(n) gauss_nodes = legendre.r @@ -124,7 +123,6 @@ def gaussian_weights(N): Where :math:`x_i` are the Gaussian nodes and :math:`P_{n}(\\xi)` are the Legendre polynomials. - Parameters ---------- @@ -149,14 +147,14 @@ def gaussian_weights(N): return gaussian_weight -def lagrange_polynomials(x): +def lagrange_polynomials(x): ''' A function to get the analytical form and the coefficients of Lagrange basis polynomials evaluated using x nodes. It calculates the Lagrange basis polynomials using the formula: - .. math:: \\\\ + .. math:: \\ L_i = \\prod_{m = 0, m \\notin i}^{N - 1}\\frac{(x - x_m)}{(x_i - x_m)} Parameters @@ -181,18 +179,19 @@ def lagrange_polynomials(x): coefficients of the Lagrange basis polynomials such that :math:`i^{th}` lagrange polynomial will be the :math:`i^{th}` row of the matrix. + **Examples** lagrange_polynomials(4)[0] gives the lagrange polynomials obtained using 4 LGL points in poly1d form lagrange_polynomials(4)[0][2] is :math: `L_2(\\xi)` - lagrange_polynomials(4)[1] gives the coefficients of the above mentioned lagrange basis polynomials in a 2D array. lagrange_polynomials(4)[1][2] gives the coefficients of :math:`L_2(\\xi)` in the form [a^2_3, a^2_2, a^2_1, a^2_0] + ''' X = np.array(x) lagrange_basis_poly = [] @@ -213,6 +212,7 @@ def lagrange_polynomials(x): def lagrange_function_value(lagrange_coeff_array): ''' + Funtion to calculate the value of lagrange basis functions over LGL nodes. @@ -230,8 +230,7 @@ def lagrange_function_value(lagrange_coeff_array): The value of lagrange basis functions calculated over the LGL nodes. - Examples - -------- + **Examples** lagrange_function_value(4) gives the value of the four Lagrange basis functions evaluated over 4 LGL points @@ -241,7 +240,7 @@ def lagrange_function_value(lagrange_coeff_array): Also the value lagrange basis functions at LGL points has the property, L_i(xi_k) = 0 for i != k - = 1 for i = k + = 1 for i = k It follows then that lagrange_function_value returns an identity matrix. @@ -259,11 +258,9 @@ def lagrange_function_value(lagrange_coeff_array): def product_lagrange_poly(x): ''' Used to obtain the coefficients of the product of Lagrange polynomials. - A matrix involves integrals of the product of the Lagrange polynomials. The Integrate() function requires the coefficients of the integrand to compute the integral. - This function takes the poly1d form of the Lagrange basis polynomials, multiplies them and stores the coefficients in a 2D array. @@ -279,18 +276,15 @@ def product_lagrange_poly(x): lagrange_product_coeffs : arrayfire.Array [N_LGL**2 N_LGL*2-1 1 1] Contains the coefficients of the product of the Lagrange polynomials. - Examples -------- product_lagrange_poly(xi_LGL)[0] gives the coefficients of the product `L_0(\\xi) * L_0(\\xi)`. - product_lagrange_poly(xi_LGL)[1] gives the coefficients of the product `L_0(\\xi) * L_1(\\xi)`. - ''' - poly1d_list = lagrange_polynomials(params.xi_LGL)[0] + poly1d_list = lagrange_polynomials(params.xi_LGL)[0] lagrange_product_coeffs = np.zeros([params.N_LGL ** 2, params.N_LGL * 2 - 1]) for i in range (params.N_LGL): @@ -307,7 +301,6 @@ def Integrate(integrand_coeffs): Performs integration according to the given quadrature method by taking in the coefficients of the polynomial and the number of quadrature points. - The number of quadrature points and the quadrature scheme are set in params.py module. @@ -326,6 +319,7 @@ def Integrate(integrand_coeffs): ''' if (params.scheme == 'gauss_quadrature'): + integrand = integrand_coeffs gaussian_nodes = params.gauss_points Gauss_weights = params.gauss_weights @@ -336,7 +330,6 @@ def Integrate(integrand_coeffs): weights_tile = af.transpose(af.tile(Gauss_weights, 1, integrand.shape[1])) nodes_weight = nodes_power * weights_tile - value_at_gauss_nodes = af.matmul(integrand, nodes_weight) Integral = af.sum(value_at_gauss_nodes, 1) @@ -360,11 +353,12 @@ def Integrate(integrand_coeffs): return Integral + + def wave_equation_lagrange_basis_single_element(u, element_no): ''' Calculates the function which describes the amplitude of the wave in a particular element. - Using the value of the amplitude at the LGL points, A function which describes this behaviour is obtained by expressing it as a linear combination of the Lagrange basis polynomials. @@ -380,11 +374,9 @@ def wave_equation_lagrange_basis_single_element(u, element_no): u : arrayfire.Array [N_LGL N_Elements 1 1] The amplitude of the wave at the LGL points for a single element. - element_no : int The element for which the analytical form of the wave equation is required. - Returns ------- @@ -426,7 +418,7 @@ def wave_equation_lagrange(u): for i in range(0, params.N_Elements): element_wave_equation = wave_equation_lagrange_basis_single_element(u, i) - wave_equation_lagrange_basis.append(element_wave_equation) + wave_equation_lagrange_basis.append(element_wave_equation) return wave_equation_lagrange_basis @@ -437,7 +429,7 @@ def differential_lagrange_poly1d(): Returns ------- - + diff_lagrange_poly1d : list [N_LGL] Contains the differential of the Lagrange basis polynomials in numpy.poly1d form. @@ -448,4 +440,4 @@ def differential_lagrange_poly1d(): test_diff = np.poly1d.deriv(params.lagrange_poly1d_list[i]) diff_lagrange_poly1d.append(test_diff) - return diff_lagrange_poly1d + return diff_lagrange_poly1d diff --git a/dg_maxwell/params.py b/dg_maxwell/params.py index ec3dea1..ef162d0 100644 --- a/dg_maxwell/params.py +++ b/dg_maxwell/params.py @@ -8,15 +8,17 @@ from dg_maxwell import lagrange from dg_maxwell import utils from dg_maxwell import isoparam +from dg_maxwell import wave_equation + # The domain of the function. -x_nodes = af.np_to_af_array(np.array([-1., 1.])) +x_nodes = af.np_to_af_array(np.array([0, 1.])) # The number of LGL points into which an element is split. N_LGL = 8 # Number of elements the domain is to be divided into. -N_Elements = 10 +N_Elements = 8 # The scheme to be used for integration. Values are either # 'gauss_quadrature' or 'lobatto_quadrature' @@ -26,38 +28,40 @@ volume_integral_scheme = 'lobatto_quadrature' # The number quadrature points to be used for integration. -N_quad = 8 +N_quad = 8 # Wave speed. c = 1 -# The total time for which the wave is to be evolved by the simulation. -total_time = 10 +# The total time for which the wave is to be evolved by the simulation. +total_time = 20.01 # The c_lax to be used in the Lax-Friedrichs flux. -c_lax = 1 +c_lax = c # Array containing the LGL points in xi space. xi_LGL = lagrange.LGL_points(N_LGL) + # N_Gauss number of Gauss nodes. -gauss_points = af.np_to_af_array(lagrange.gauss_nodes(N_quad)) +gauss_points = af.np_to_af_array(lagrange.gauss_nodes(N_quad)) # The Gaussian weights. -gauss_weights = lagrange.gaussian_weights(N_quad) +gauss_weights = lagrange.gaussian_weights(N_quad) # The lobatto nodes to be used for integration. -lobatto_quadrature_nodes = lagrange.LGL_points(N_quad) +lobatto_quadrature_nodes = lagrange.LGL_points(N_quad) # The lobatto weights to be used for integration. lobatto_weights_quadrature = lagrange.lobatto_weights\ (N_quad) # A list of the Lagrange polynomials in poly1d form. -lagrange_product = lagrange.product_lagrange_poly(xi_LGL) +lagrange_product = lagrange.product_lagrange_poly(xi_LGL) # An array containing the coefficients of the lagrange basis polynomials. -lagrange_coeffs = af.np_to_af_array(lagrange.lagrange_polynomials(xi_LGL)[1]) +lagrange_coeffs = af.np_to_af_array(\ + lagrange.lagrange_polynomials(xi_LGL)[1]) # Refer corresponding functions. lagrange_basis_value = lagrange.lagrange_function_value(lagrange_coeffs) @@ -75,14 +79,15 @@ # lobatto quadrature points, The integration can be vectorized # and in this case the coefficients of the differential of the # Lagrange polynomials is required -volume_integrand_8_LGL = np.zeros(([N_LGL, N_LGL - 1])) +volume_integrand_N_LGL = np.zeros(([N_LGL, N_LGL - 1])) for i in range(N_LGL): - volume_integrand_8_LGL[i] = (differential_lagrange_polynomial[i]).c + volume_integrand_N_LGL[i] = (differential_lagrange_polynomial[i]).c -volume_integrand_8_LGL= af.np_to_af_array(volume_integrand_8_LGL) +volume_integrand_N_LGL= af.np_to_af_array(volume_integrand_N_LGL) # Obtaining an array consisting of the LGL points mapped onto the elements. + element_size = af.sum((x_nodes[1] - x_nodes[0]) / N_Elements) elements_xi_LGL = af.constant(0, N_Elements, N_LGL) elements = utils.linspace(af.sum(x_nodes[0]), @@ -95,21 +100,40 @@ af.sum(x_nodes[1]), N_Elements + 1) element_array = af.transpose(af.interop.np_to_af_array(np_element_array)) -element_LGL = isoparam.isoparam_1D(af.transpose(element_array), - xi_LGL) +element_LGL = wave_equation.mapping_xi_to_x(af.transpose(element_array),\ + xi_LGL) # The minimum distance between 2 mapped LGL points. delta_x = af.min((element_LGL - af.shift(element_LGL, 1, 0))[1:, :]) +# dx_dxi for elements of equal size. +dx_dxi = af.mean(wave_equation.dx_dxi_numerical((element_mesh_nodes[0 : 2]),\ + xi_LGL)) + + # The value of time-step. -delta_t = delta_x / (20 * c) +delta_t = delta_x / (4 * c) # Array of timesteps seperated by delta_t. time = utils.linspace(0, int(total_time / delta_t) * delta_t, int(total_time / delta_t)) + +# The wave to be advected is either a sin or a Gaussian wave. +# This parameter can take values 'sin' or 'gaussian'. +wave = 'sin' + +# Initializing the amplitudes. Change u_init to required initial conditions. +if (wave=='sin'): + u_init = af.sin(2 * np.pi * element_LGL) + +if (wave=='gaussian'): + u_init = np.e ** (-(element_LGL) ** 2 / 0.4 ** 2) + + + # Initializing the amplitudes. Change u_init to required initial conditions. -u_init = np.e ** (-(element_LGL) ** 2 / 0.4 ** 2) +u_init = af.sin(2 * np.pi * element_LGL) u = af.constant(0, N_LGL, N_Elements, time.shape[0],\ dtype = af.Dtype.f64) u[:, :, 0] = u_init diff --git a/dg_maxwell/tests/test_waveEqn.py b/dg_maxwell/tests/test_waveEqn.py index 5af8263..d55deff 100644 --- a/dg_maxwell/tests/test_waveEqn.py +++ b/dg_maxwell/tests/test_waveEqn.py @@ -207,7 +207,7 @@ def test_A_matrix(): ''' threshold = 1e-8 - reference_A_matrix = 0.1 * af.interop.np_to_af_array(np.array([\ + reference_A_matrix = params.dx_dxi * af.interop.np_to_af_array(np.array([\ [0.03333333333332194, 0.005783175201965206, -0.007358427761753982, \ 0.008091331778355441, -0.008091331778233877, 0.007358427761705623, \ @@ -262,6 +262,7 @@ def test_volume_integral_flux(): ''' threshold = 8e-9 params.c = 1 + wave_equation.change_parameters(8, 10, 'gaussian') referenceFluxIntegral = af.transpose(af.interop.np_to_af_array(np.array ([ @@ -298,7 +299,7 @@ def test_volume_integral_flux(): ]))) - numerical_flux = wave_equation.volume_integral_flux(params.u[:, :, 0]) + numerical_flux = wave_equation.volume_integral_flux(params.u[:, :, 0], 0) assert (af.max(af.abs(numerical_flux - referenceFluxIntegral)) < threshold) def test_lax_friedrichs_flux(): @@ -323,6 +324,7 @@ def test_surface_term(): threshold = 1e-13 params.c = 1 + wave_equation.change_parameters(8, 10, 'gaussian') analytical_f_i = (params.u[-1, :, 0]) analytical_f_i_minus1 = (af.shift(params.u[-1, :, 0], 0, 1)) @@ -350,13 +352,15 @@ def test_b_vector(): threshold = 1e-13 params.c = 1 + wave_equation.change_parameters(8, 10, 'gaussian') + u_n_A_matrix = af.blas.matmul(wave_equation.A_matrix(),\ params.u[:, :, 0]) - volume_integral_flux = wave_equation.volume_integral_flux(params.u[:, :, 0]) + volume_integral_flux = wave_equation.volume_integral_flux(params.u[:, :, 0], 0) surface_term = test_surface_term() b_vector_analytical = u_n_A_matrix + (volume_integral_flux -\ (surface_term)) * params.delta_t - b_vector_array = wave_equation.b_vector(params.u[:, :, 0]) + b_vector_array = wave_equation.b_vector(params.u[:, :, 0], 0) assert (b_vector_analytical - b_vector_array) < threshold diff --git a/dg_maxwell/wave_equation.py b/dg_maxwell/wave_equation.py index 188a3f9..b8c070f 100644 --- a/dg_maxwell/wave_equation.py +++ b/dg_maxwell/wave_equation.py @@ -3,11 +3,14 @@ import os +import os + import arrayfire as af af.set_backend('cpu') import numpy as np from matplotlib import pyplot as plt from tqdm import trange +import h5py from dg_maxwell import params from dg_maxwell import lagrange @@ -42,11 +45,43 @@ plt.rcParams['ytick.direction' ] = 'in' -def dx_dxi_numerical(x_nodes, xi): +def mapping_xi_to_x(x_nodes, xi): + ''' + Maps points in :math: `\\xi` space to :math:`x` space using the formula + :math: `x = \\frac{1 - \\xi}{2} x_0 + \\frac{1 + \\xi}{2} x_1` + + Parameters + ---------- + + x_nodes : arrayfire.Array [2 1 1 1] + Element nodes. + + xi : arrayfire.Array [N 1 1 1] + Value of :math: `\\xi`coordinate for which the corresponding + :math: `x` coordinate is to be found. + + Returns + ------- + + x : arrayfire.Array + :math: `x` value in the element corresponding to :math:`\\xi`. ''' - Differential :math:`\\frac{dx}{d \\xi}` calculated by central - differential method about xi using the isoparam.isoparam_1D function. + N_0 = (1 - xi) / 2 + N_1 = (1 + xi) / 2 + + N0_x0 = af.broadcast(utils.multiply, N_0, x_nodes[0]) + N1_x1 = af.broadcast(utils.multiply, N_1, x_nodes[1]) + + x = N0_x0 + N1_x1 + + return x + +def dx_dxi_numerical(x_nodes, xi): + ''' + Differential :math: `\\frac{dx}{d \\xi}` calculated by central + differential method about xi using the mapping_xi_to_x function. + Parameters ---------- @@ -54,16 +89,17 @@ def dx_dxi_numerical(x_nodes, xi): Contains the nodes of elements. xi : arrayfire.Array [N_LGL 1 1 1] - Values of :math:`\\xi` + Values of :math: `\\xi` Returns ------- + dx_dxi : arrayfire.Array [N_Elements 1 1 1] - :math:`\\frac{dx}{d \\xi}`. + :math:`\\frac{dx}{d \\xi}`. ''' dxi = 1e-7 - x2 = isoparam.isoparam_1D(x_nodes, xi + dxi) - x1 = isoparam.isoparam_1D(x_nodes, xi - dxi) + x2 = mapping_xi_to_x(x_nodes, xi + dxi) + x1 = mapping_xi_to_x(x_nodes, xi - dxi) dx_dxi = (x2 - x1) / (2 * dxi) @@ -73,21 +109,23 @@ def dx_dxi_numerical(x_nodes, xi): def dx_dxi_analytical(x_nodes, xi): ''' The analytical result for :math:`\\frac{dx}{d \\xi}` for a 1D element is - :math:`\\frac{x_1 - x_0}{2}` - + :math: `\\frac{x_1 - x_0}{2}` + Parameters ---------- + x_nodes : arrayfire.Array [N_Elements 1 1 1] Contains the nodes of elements. xi : arrayfire.Array [N_LGL 1 1 1] - Values of :math:`\\xi`. + Values of :math: `\\xi`. Returns ------- + analytical_dx_dxi : arrayfire.Array [N_Elements 1 1 1] - The analytical solution of :math:`\\frac{dx}{d\\xi}` - for an element. + The analytical solution of :math: + `\\frac{dx}{d\\xi}` for an element. ''' analytical_dx_dxi = (x_nodes[1] - x_nodes[0]) / 2 @@ -97,24 +135,24 @@ def dx_dxi_analytical(x_nodes, xi): def A_matrix(): ''' + Calculates A matrix whose elements :math:`A_{p i}` are given by - :math:`A_{p i} = \\int^1_{-1} L_p(\\xi)L_i(\\xi) \\frac{dx}{d\\xi}` + :math: `A_{p i} &= \\int^1_{-1} L_p(\\xi)L_i(\\xi) \\frac{dx}{d\\xi}` The integrals are computed using the Integrate() function. - - Since elements are taken to be of equal size, :math:`\\frac{dx}{d\\xi}` + Since elements are taken to be of equal size, :math: `\\frac {dx}{dxi} is same everywhere - Returns ------- + A_matrix : arrayfire.Array [N_LGL N_LGL 1 1] The value of integral of product of lagrange basis functions obtained by LGL points, using the Integrate() function + ''' int_Li_Lp = lagrange.Integrate(params.lagrange_product) - dx_dxi = af.mean(dx_dxi_numerical((params.element_mesh_nodes[0 : 2]),\ - params.xi_LGL)) + dx_dxi = params.dx_dxi A_matrix_flat = dx_dxi * int_Li_Lp A_matrix = af.moddims(A_matrix_flat, params.N_LGL, params.N_LGL) @@ -124,110 +162,167 @@ def A_matrix(): def flux_x(u): ''' + A function which returns the value of flux for a given wave function u. :math:`f(u) = c u^k` Parameters ---------- + u : list [N_Elements] The analytical form of the wave equation for each element arranged in a list of numpy.poly1d polynomials. Returns ------- - flux : list [N_Elements] + + flux : list [N_Elements] The analytical value of the flux for each element arranged in a list of numpy.poly1d polynomials. + ''' flux = params.c * u return flux -def volume_integral_flux(u_n): +def volume_integral_flux(u_n, t_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 8 Lobatto quadrature points + 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 1 1] Amplitude of the wave at the mapped LGL nodes of each element. Returns ------- + flux_integral : arrayfire.Array [N_LGL N_Elements 1 1] Value of the volume integral flux. It contains the integral of all N_LGL * N_Element integrands. + ''' + # The coefficients of dLp / d\xi + diff_lag_coeff = params.volume_integrand_N_LGL + + 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) + + + if(params.volume_integral_scheme == 'lobatto_quadrature'\ and params.N_quad == params.N_LGL): + print('option1') + # Flux using u_n, reordered to 1 X N_LGL X N_Elements array. + F_u_n = af.reorder(flux_x(u_n), 2, 0, 1) - integrand = params.volume_integrand_8_LGL - lobatto_nodes = params.lobatto_quadrature_nodes - Lobatto_weights = params.lobatto_weights_quadrature - - nodes_tile = af.transpose(af.tile(lobatto_nodes, 1, integrand.shape[1])) - power = af.flip(af.range(integrand.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, integrand.shape[1])) - nodes_weight = nodes_power * weights_tile + # Multiplying with dLp / d\xi + integral_expansion = af.broadcast(utils.multiply,\ + dLp_dxi, F_u_n) - value_at_lobatto_nodes = af.matmul(integrand, nodes_weight) - F_u_n = af.reorder(u_n, 2, 0, 1) - integral_expansion = af.broadcast(utils.multiply, value_at_lobatto_nodes, F_u_n) + # Using the quadrature rule. flux_integral = af.sum(integral_expansion, 1) flux_integral = af.reorder(flux_integral, 0, 2, 1) + elif(params.volume_integral_scheme == 'analytical'): + print('option2') + + # u_n calculated using af.sin + analytical_u_n = analytical_u_LGL(t_n) + F_u_n = af.reorder(analytical_u_n, 2, 0, 1) + + + # 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, 1) + + else: - analytical_form_flux = flux_x(lagrange.wave_equation_lagrange(u_n)) + print('option3') + # Obtaining the u_n in polynomial form using the value at LGL points. + analytical_form_flux = flux_x(lagrange.\ + wave_equation_lagrange(u_n)) + + # dLp/ d\xi in poly1d form differential_lagrange_poly = params.differential_lagrange_polynomial - integrand = np.zeros(([params.N_LGL * params.N_Elements, 2 * params.N_LGL - 2])) + + # The coefficients of the integrand dLp / dxi * F(u) + # arranged in a (N_LGL * N_Elements) X (N_LGL - 2) array + integrand_coeff = np.zeros(([params.N_LGL * params.N_Elements,\ + 2 * params.N_LGL - 2])) for i in range(params.N_LGL): for j in range(params.N_Elements): - integrand[i + params.N_LGL * j] = (analytical_form_flux[j] *\ + # Coefficients of the integrand. + integrand_coeff[i + params.N_LGL * j] = (analytical_form_flux[j] *\ differential_lagrange_poly[i]).c - integrand = af.np_to_af_array(integrand) - flux_integral = lagrange.Integrate(integrand) - flux_integral = af.moddims(flux_integral, params.N_LGL, params.N_Elements) + integrand_coeff = af.np_to_af_array(integrand_coeff) + flux_integral = lagrange.Integrate(integrand_coeff) + flux_integral = af.moddims(flux_integral, params.N_LGL,\ + params.N_Elements) * params.c + + + return flux_integral def lax_friedrichs_flux(u_n): ''' + Calculates the lax-friedrichs_flux :math:`f_i` using. - - .. math:: f_i = \\frac{F(u^{i + 1}_0) + F(u^i_{N_{LGL} - 1})}{2} \\\\ - - \\frac{\Delta x}{2\Delta t} (u^{i + 1}_0 - u^i_{N_{LGL} - 1}) + + :math:`f_i = \\frac{F(u^{i + 1}_0) + F(u^i_{N_{LGL} - 1})}{2} - \\frac + {\Delta x}{2\Delta t} (u^{i + 1}_0 - u^i_{N_{LGL} - 1})` The algorithm used is explained in this `document`_ - + .. _document: `https://goo.gl/sNsXXK` + Parameters ---------- + u_n : arrayfire.Array [N_LGL N_Elements 1 1] Amplitude of the wave at the mapped LGL nodes of each element. Returns ------- + boundary_flux : arrayfire.Array [1 N_Elements 1 1] Contains the value of the flux at the boundary elements. Periodic boundary conditions are used. - ''' + ''' u_iplus1_0 = af.shift(u_n[0, :], 0, -1) u_i_N_LGL = u_n[-1, :] @@ -238,11 +333,35 @@ def lax_friedrichs_flux(u_n): - params.c_lax * (u_iplus1_0 - u_i_N_LGL) / 2 - return boundary_flux + return boundary_flux + +def analytical_u_LGL(t_n): + ''' + Calculates the analytical u at the LGL points. + + Parameters + ---------- + + t_n : int + The timestep at which the analytical u is to be calculated. + + Returns + ------- + + u_t_n : arrayfire.Array [N_LGL N_Elements 1 1] + The value of u at the mapped LGL points in each element. + + ''' + + time = t_n * params.delta_t + u_t_n = af.sin(2 * np.pi * (params.element_LGL - params.c * time)) + + return u_t_n 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 @@ -265,6 +384,7 @@ def surface_term(u_n): **See:** `PDF`_ describing the algorithm to obtain the surface term. .. _PDF: https://goo.gl/Nhhgzx + ''' L_p_minus1 = params.lagrange_basis_value[:, 0] @@ -277,70 +397,296 @@ def surface_term(u_n): return surface_term -def b_vector(u_n): +def b_vector(u_n, t_n): ''' + Calculates the b vector for N_Elements number of elements. Parameters ---------- + u_n : arrayfire.Array [N_LGL N_Elements 1 1] Amplitude of the wave at the mapped LGL nodes of each element. Returns ------- + b_vector_array : arrayfire.Array [N_LGL N_Elements 1 1] Contains the b vector(of shape [N_LGL 1 1 1]) for each element. **See:** `Report`_ for the b-vector can be found here + .. _Report: https://goo.gl/sNsXXK ''' - volume_integral = volume_integral_flux(u_n) + volume_integral = volume_integral_flux(u_n, t_n) Surface_term = surface_term(u_n) - b_vector_array = params.delta_t * (volume_integral - Surface_term) + b_vector_array = (volume_integral - Surface_term) return b_vector_array +def RK4_timestepping(A_inverse, u, t_n, delta_t): + ''' + ''' + + k1 = af.matmul(A_inverse, b_vector(u , t_n)) + k2 = af.matmul(A_inverse, b_vector(u + k1 * delta_t / 2, t_n)) + k3 = af.matmul(A_inverse, b_vector(u + k2 * delta_t / 2, t_n)) + k4 = af.matmul(A_inverse, b_vector(u + k3 * delta_t , t_n)) + + delta_u = delta_t * (k1 + 2 * k2 + 2 * k3 + k4) / 6 + + return delta_u + +def RK6_timestepping(A_inverse, u, t_n, delta_t): + ''' + ''' + + k1 = af.matmul(A_inverse, b_vector(u, t_n)) + k2 = af.matmul(A_inverse, b_vector(u + 0.25 * k1 * delta_t, t_n)) + k3 = af.matmul(A_inverse, b_vector(u + (3 / 32) * (k1 + 3 * k2) * delta_t, t_n)) + k4 = af.matmul(A_inverse, b_vector(u + (12 / 2197) * (161 * k1 - 600 * k2 + 608 * k3) * delta_t, t_n)) + k5 = af.matmul(A_inverse, b_vector(u + (1 / 4104) * (8341 * k1 - 32832 * k2 + 29440 * k3 - 845 * k4) * delta_t, t_n)) + k6 = af.matmul(A_inverse, b_vector(u + (-(8/27) * k1 + 2 * k2- (3544 / 2565) *\ + k3 + (1859 / 4104) * k4 - (11 / 40) * k5) * delta_t, t_n)) + + delta_u = delta_t * 1 / 5 * ((16 / 27) * k1 + (6656 / 2565) * k3 + (28561 / 11286) * k4 - (9 / 10) * k5 + (2 / 11) * k6) + + return delta_u + def time_evolution(): ''' + Solves the wave equation - :math:`u^{t_n + 1} = b(t_n) \\times A` - iterated over time steps t_n and then plots :math:`x` against the amplitude - of the wave. The images are then stored in Wave folder. + :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 + ------- + + L1_norm : float64 + The L1 norm of error after one full advection. + ''' + # 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) + A_inverse = af.inverse(A_matrix()) + element_LGL = params.element_LGL delta_t = params.delta_t - amplitude = params.u + u = params.u_init time = params.time - - for t_n in trange(0, time.shape[0] - 1): + + element_boundaries = af.np_to_af_array(params.np_element_array) + + for t_n in trange(0, time.shape[0]): + + # Storing u at timesteps which are multiples of 20. + if (t_n % 50) == 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[:, :] + + # # Implementing second order time-stepping. + # u_n_plus_half = u + af.matmul(A_inverse, b_vector(u, t_n))\ + # * delta_t / 2 + + # u += af.matmul(A_inverse, b_vector(u_n_plus_half, t_n + 0.5))\ + # * delta_t + + # Implementing RK 4 scheme + #u += RK4_timestepping(A_inverse, u, t_n, delta_t) + + # Implementing RK 6 scheme + u += RK6_timestepping(A_inverse, u, t_n, delta_t) + + u_analytical = analytical_u_LGL(t_n + 1) + + + + return (af.abs(u - u_analytical)) + + +def change_parameters(LGL, Elements, wave='sin'): + ''' + + Changes the parameters of the simulation. Used only for convergence tests. + + Parameters + ---------- + LGL : int + The new N_LGL. + + Elements : int + The new N_Elements. + + ''' + # The domain of the function. + params.x_nodes = af.np_to_af_array(np.array([-1., 1.])) + + # The number of LGL points into which an element is split. + params.N_LGL = LGL + + # Number of elements the domain is to be divided into. + params.N_Elements = Elements + + # The number quadrature points to be used for integration. + params.N_quad = LGL + 1 + + # Array containing the LGL points in xi space. + params.xi_LGL = lagrange.LGL_points(params.N_LGL) + + # N_Gauss number of Gauss nodes. + params.gauss_points = af.np_to_af_array(lagrange.gauss_nodes\ + (params.N_quad)) + + # The Gaussian weights. + params.gauss_weights = lagrange.gaussian_weights(params.N_quad) + + # The lobatto nodes to be used for integration. + params.lobatto_quadrature_nodes = lagrange.LGL_points(params.N_quad) + + # The lobatto weights to be used for integration. + params.lobatto_weights_quadrature = lagrange.lobatto_weights\ + (params.N_quad) + + # A list of the Lagrange polynomials in poly1d form. + params.lagrange_product = lagrange.product_lagrange_poly(params.xi_LGL) + + # An array containing the coefficients of the lagrange basis polynomials. + params.lagrange_coeffs = af.np_to_af_array(\ + lagrange.lagrange_polynomials(params.xi_LGL)[1]) + + # Refer corresponding functions. + params.lagrange_basis_value = lagrange.lagrange_function_value\ + (params.lagrange_coeffs) + + # A list of the Lagrange polynomials in poly1d form. + params.lagrange_poly1d_list = lagrange.lagrange_polynomials(params.xi_LGL)[0] + + + # list containing the poly1d forms of the differential of Lagrange + # basis polynomials. + params.differential_lagrange_polynomial = lagrange.differential_lagrange_poly1d() + + + # While evaluating the volume integral using N_LGL + # lobatto quadrature points, The integration can be vectorized + # and in this case the coefficients of the differential of the + # Lagrange polynomials is required + params.volume_integrand_N_LGL = np.zeros(([params.N_LGL, params.N_LGL - 1])) + + for i in range(params.N_LGL): + params.volume_integrand_N_LGL[i] = (params.differential_lagrange_polynomial[i]).c + + params.volume_integrand_N_LGL= af.np_to_af_array(params.volume_integrand_N_LGL) + + # Obtaining an array consisting of the LGL points mapped onto the elements. + + params.element_size = af.sum((params.x_nodes[1] - params.x_nodes[0])\ + / params.N_Elements) + params.elements_xi_LGL = af.constant(0, params.N_Elements, params.N_LGL) + params.elements = utils.linspace(af.sum(params.x_nodes[0]), + af.sum(params.x_nodes[1] - params.element_size),\ + params.N_Elements) + + params.np_element_array = np.concatenate((af.transpose(params.elements), + af.transpose(params.elements +\ + params.element_size))) + + params.element_mesh_nodes = utils.linspace(af.sum(params.x_nodes[0]), + af.sum(params.x_nodes[1]),\ + params.N_Elements + 1) + + params.element_array = af.transpose(af.np_to_af_array\ + (params.np_element_array)) + params.element_LGL = mapping_xi_to_x(af.transpose\ + (params.element_array), params.xi_LGL) + + # The minimum distance between 2 mapped LGL points. + params.delta_x = af.min((params.element_LGL - af.shift(params.element_LGL, 1, 0))[1:, :]) + + # dx_dxi for elements of equal size. + params. dx_dxi = af.mean(dx_dxi_numerical((params.element_mesh_nodes[0 : 2]),\ + params.xi_LGL)) + + + # The value of time-step. + params.delta_t = params.delta_x / (4 * params.c) + + # Array of timesteps seperated by delta_t. + params.time = utils.linspace(0, int(params.total_time / params.delta_t) * params.delta_t, + int(params.total_time / params.delta_t)) + + # Initializing the amplitudes. Change u_init to required initial conditions. + if (wave=='sin'): + params.u_init = af.sin(2 * np.pi * params.element_LGL) - amplitude[:, :, t_n + 1] = amplitude[:, :, t_n]\ - + af.blas.matmul(A_inverse,\ - b_vector(amplitude[:, :, t_n])) + if (wave=='gaussian'): + params.u_init = np.e ** (-(params.element_LGL) ** 2 / 0.4 ** 2) - print('u calculated!') - - results_directory = 'results/1D_Wave_images' + params.u = af.constant(0, params.N_LGL, params.N_Elements, params.time.shape[0],\ + dtype = af.Dtype.f64) + params.u[:, :, 0] = params.u_init + + return + +def L1_norm_error(mod_diff_u): + ''' + ''' + wave_equation_coeffs = np.zeros([params.N_Elements, params.N_LGL]) + + for i in range(0, params.N_Elements): + wave_equation_coeffs[i, :] = lagrange.wave_equation_lagrange(mod_diff_u)[i].c + + L1_norm = af.sum(lagrange.Integrate(af.np_to_af_array\ + (wave_equation_coeffs))) * params.dx_dxi + + return L1_norm + + +def convergence_test(): + ''' + + Used to obtain plots of L1 norm versus parameters (Number of elements + or N_LGL). - if not os.path.exists(results_directory): - os.makedirs(results_directory) + ''' + L1_norm_option_1 = np.zeros([10]) + N_lgl = (np.arange(10) + 3).astype(float) + + for i in range(0, 10): + change_parameters(i + 3, 64) + u_diff = time_evolution() + L1_norm_option_1[i] = L1_norm_error(u_diff) + +# L1_norm_option_1 = np.array([7.86867473e-03, 1.62010118e-04, 6.09539662e-06, 3.33291808e-07,\ +# 1.60409746e-07, 6.26348505e-07, 3.40978741e-07, 3.06485997e-07,\ +# 1.97638417e-07, 2.09291363e-08]) + + + print(L1_norm_option_1) + normalization = 0.00786 / (3 ** (-3)) + plt.loglog(N_lgl, L1_norm_option_1, marker='o', label='L1 norm') + plt.xlabel('No. of LGL points') + plt.ylabel('L1 norm of error') + plt.title('L1 norm after 10 full advections') + plt.loglog(N_lgl, normalization * N_lgl **(-N_lgl), color='black', linestyle='--', label='$N_{LGL}^{-N_{LGL}}$') + plt.legend(loc='best') + + plt.show() - for t_n in trange(0, time.shape[0] - 1): - - if t_n % 100 == 0: - - fig = plt.figure() - x = params.element_LGL - y = amplitude[:, :, t_n] - - plt.plot(x, y) - plt.xlabel('x') - plt.ylabel('Amplitude') - plt.title('Time = %f' % (t_n * delta_t)) - fig.savefig('results/1D_Wave_images/%04d' %(t_n / 100) + '.png') - plt.close('all') - return diff --git a/examples/lagrange_interpolation_test.ipynb b/examples/lagrange_interpolation_test.ipynb new file mode 100644 index 0000000..db1927a --- /dev/null +++ b/examples/lagrange_interpolation_test.ipynb @@ -0,0 +1,286 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys\n", + "sys.path.insert(0, os.path.abspath('../'))\n", + "\n", + "from scipy import integrate\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "\n", + "from dg_maxwell import lagrange\n", + "from dg_maxwell import isoparam\n", + "from dg_maxwell import wave_equation\n", + "\n", + "plt.rcParams['figure.figsize'] = 12, 7.5\n", + "plt.rcParams['lines.linewidth'] = 1.5\n", + "plt.rcParams['font.family'] = 'serif'\n", + "plt.rcParams['font.weight'] = 'bold'\n", + "plt.rcParams['font.size'] = 20 \n", + "plt.rcParams['font.sans-serif'] = 'serif'\n", + "plt.rcParams['text.usetex'] = True\n", + "plt.rcParams['axes.linewidth'] = 1.5\n", + "plt.rcParams['axes.titlesize'] = 'medium'\n", + "plt.rcParams['axes.labelsize'] = 'medium'\n", + "\n", + "plt.rcParams['xtick.major.size'] = 8\n", + "plt.rcParams['xtick.minor.size'] = 4\n", + "plt.rcParams['xtick.major.pad'] = 8\n", + "plt.rcParams['xtick.minor.pad'] = 8\n", + "plt.rcParams['xtick.color'] = 'k'\n", + "plt.rcParams['xtick.labelsize'] = 'medium'\n", + "plt.rcParams['xtick.direction'] = 'in' \n", + "\n", + "plt.rcParams['ytick.major.size'] = 8\n", + "plt.rcParams['ytick.minor.size'] = 4\n", + "plt.rcParams['ytick.major.pad'] = 8\n", + "plt.rcParams['ytick.minor.pad'] = 8\n", + "plt.rcParams['ytick.color'] = 'k'\n", + "plt.rcParams['ytick.labelsize'] = 'medium'\n", + "plt.rcParams['ytick.direction'] = 'in'\n", + "plt.rcParams['text.usetex'] = True\n", + "plt.rcParams['text.latex.unicode'] = True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [], + "source": [ + "def find_L1_norm(epsilon, dx_dxi, x):\n", + " '''\n", + " '''\n", + " lagrange_basis, temp = lagrange.lagrange_polynomials(x)\n", + " \n", + " epsilon_interpol = 0.\n", + " for i in np.arange(x.shape[0]):\n", + " epsilon_interpol += lagrange_basis[i] * epsilon[i]\n", + " \n", + " return integrate.quad(epsilon_interpol * dx_dxi, -1, 1)[0]\n", + "\n", + "\n", + "def integrate_quad(function, order, scheme = 'gauss_legendre'):\n", + " '''\n", + " '''\n", + " if scheme == 'gauss_legendre':\n", + " nodes = np.array(lagrange.gauss_nodes(order))\n", + " weights = np.array(lagrange.gaussian_weights(order))\n", + "\n", + " elif scheme == 'gauss_lobatto':\n", + " nodes = np.array(lagrange.LGL_points(order))\n", + " weights = np.array(lagrange.lobatto_weights(order))\n", + " \n", + " else:\n", + " return\n", + "\n", + " integral = 0.\n", + "\n", + " for node, weight in zip(nodes, weights):\n", + " integral += weight * function(node)\n", + " \n", + " return integral" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [], + "source": [ + "def test_function(x):\n", + " '''\n", + " The test wave function.\n", + " '''\n", + " return np.sin(2 * np.pi * x)\n", + "\n", + "def int_sin2pix_dLdxi(x_nodes, xi_LGL, lagrange_basis_order):\n", + " '''\n", + " '''\n", + " L_i, temp = lagrange.lagrange_polynomials(xi_LGL)\n", + " \n", + " def sin2pix_dLdxi(xi):\n", + " x = (((x_nodes[1] - x_nodes[0]) * xi + (x_nodes[1] + x_nodes[0]))) / 2\n", + " return np.sin(2 * np.pi * x) * L_i[lagrange_basis_order].deriv()(xi)\n", + " \n", + " return integrate.quad(sin2pix_dLdxi, -1, 1)[0]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "M = 8 # Number of elements\n", + "x_nodes = np.linspace(0, 1, M + 1) # x nodes dividing the elements" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "x_nodes = np.array([[0. , 0.125],\n", + " [0.125, 0.25],\n", + " [0.25 , 0.375],\n", + " [0.375, 0.5],\n", + " [0.5 , 0.625],\n", + " [0.625, 0.75],\n", + " [0.75 , 0.875],\n", + " [0.875, 1.]])\n", + "\n", + "dx_dxi = (x_nodes[0][1] - x_nodes[0][0]) / 2" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "# The test points at which the analytical and interpolated wave functions\n", + "# will be compared.\n", + "xi_check = np.linspace(-.9, .9, 8)\n", + "L1_norm = [] # Stores the L1 norm of the interpolated wave function\n", + "\n", + "for N in np.arange(3, 31):\n", + "# N = 8\n", + " xi_LGL = np.array(lagrange.LGL_points(int(N)))\n", + " x_LGL = [] # x_nodes calculated at the LGL points for an element\n", + " x_check = [] # x coordinates at which the analytical and interpolated functions are compared\n", + "\n", + " # Test function calculated at the LGL points, to be used for finding the lagrange interpolation.\n", + " test_func_LGL = []\n", + "\n", + " lagrange_basis, temp = lagrange.lagrange_polynomials(xi_LGL)\n", + " test_func_intepol_poly = 0. # Stores the Lagrange interpolation function for an element.\n", + "\n", + " # Stores the value of the inerpolated function at the xi_check points for each elements.\n", + " test_function_interpol = []\n", + "\n", + " L1_norm.append(0.)\n", + " L1 = 0.\n", + " # This loop loops over all the elements and finds the interpolation function using\n", + " # Lagrange basis polynomials and test_func_LGL. It then uses the interpolation function\n", + " # to calculate the value of the polynomial at the xi_check points for each element.\n", + " for node in x_nodes:\n", + " test_func_intepol_poly = 0.\n", + " x_LGL.append(isoparam.isoparam_1D(node, xi_LGL))\n", + " x_check.append(isoparam.isoparam_1D(node, xi_check))\n", + " test_func_LGL.append(test_function(x_LGL[-1]))\n", + "\n", + " for i in np.arange(len(test_func_LGL[-1])):\n", + " test_func_intepol_poly += lagrange_basis[i] * test_func_LGL[-1][i]\n", + " test_function_interpol.append(test_func_intepol_poly(xi_check))\n", + "\n", + " L1_norm[-1] += find_L1_norm(np.abs(test_function_interpol[-1] - test_function(x_check[-1])),\n", + " dx_dxi, xi_check)" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": {}, + "outputs": [], + "source": [ + "# The test points at which the analytical and interpolated wave functions\n", + "# will be compared.\n", + "xi_check = np.linspace(-.9, .9, 8)\n", + "error = [] # Stores the error of the interpolated wave function\n", + "\n", + "for N in np.arange(3, 31):\n", + "# N = 8\n", + " xi_LGL = np.array(lagrange.LGL_points(int(N)))\n", + " x_LGL = [] # x_nodes calculated at the LGL points for an element\n", + " x_check = [] # x coordinates at which the analytical and interpolated functions are compared\n", + "\n", + " # Test function calculated at the LGL points, to be used for finding the lagrange interpolation.\n", + " test_func_LGL = []\n", + "\n", + " lagrange_basis, temp = lagrange.lagrange_polynomials(xi_LGL)\n", + " test_func_intepol_poly = 0. # Stores the Lagrange interpolation function for an element.\n", + "\n", + " # Stores the value of the inerpolated function at the xi_check points for each elements.\n", + " test_function_interpol = []\n", + "\n", + " error.append(0.)\n", + " # This loop loops over all the elements and finds the interpolation function using\n", + " # Lagrange basis polynomials and test_func_LGL. It then uses the interpolation function\n", + " # to calculate the value of the polynomial at the xi_check points for each element.\n", + " for node in x_nodes:\n", + " test_func_intepol_poly = 0.\n", + " x_LGL.append(isoparam.isoparam_1D(node, xi_LGL))\n", + " x_check.append(isoparam.isoparam_1D(node, xi_check))\n", + " test_func_LGL.append(test_function(x_LGL[-1]))\n", + "\n", + " for i in np.arange(len(test_func_LGL[-1])):\n", + " test_func_intepol_poly += lagrange_basis[i] * test_func_LGL[-1][i]\n", + " \n", + " error[-1] += abs(integrate_quad(test_func_intepol_poly * lagrange_basis[0].deriv(), 9)\n", + " - int_sin2pix_dLdxi(node, xi_LGL, 0))\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.title(r'Error for $\\int sin(2 \\pi x) \\frac{d L_0}{d\\xi} d\\xi$ vs $N_{LGL}$')\n", + "plt.xlabel(r'$N_{LGL}$')\n", + "plt.ylabel(r'Error')\n", + "plt.semilogy(np.arange(3, 31), error, '-o')\n", + "plt.savefig('error_int_fxi_dLi_dxi.png')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/volume_integral_error_analysis.ipynb b/examples/volume_integral_error_analysis.ipynb new file mode 100644 index 0000000..d14020c --- /dev/null +++ b/examples/volume_integral_error_analysis.ipynb @@ -0,0 +1,513 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys\n", + "sys.path.insert(0, os.path.abspath('../'))\n", + "\n", + "from scipy import integrate\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "import arrayfire as af\n", + "af.set_backend('cpu')\n", + "\n", + "from dg_maxwell import lagrange\n", + "from dg_maxwell import isoparam\n", + "from dg_maxwell import wave_equation\n", + "from dg_maxwell import params\n", + "from dg_maxwell import utils\n", + "\n", + "plt.rcParams['figure.figsize'] = 12, 7.5\n", + "plt.rcParams['lines.linewidth'] = 1.5\n", + "plt.rcParams['font.family'] = 'serif'\n", + "plt.rcParams['font.weight'] = 'bold'\n", + "plt.rcParams['font.size'] = 20 \n", + "plt.rcParams['font.sans-serif'] = 'serif'\n", + "plt.rcParams['text.usetex'] = True\n", + "plt.rcParams['axes.linewidth'] = 1.5\n", + "plt.rcParams['axes.titlesize'] = 'medium'\n", + "plt.rcParams['axes.labelsize'] = 'medium'\n", + "\n", + "plt.rcParams['xtick.major.size'] = 8\n", + "plt.rcParams['xtick.minor.size'] = 4\n", + "plt.rcParams['xtick.major.pad'] = 8\n", + "plt.rcParams['xtick.minor.pad'] = 8\n", + "plt.rcParams['xtick.color'] = 'k'\n", + "plt.rcParams['xtick.labelsize'] = 'medium'\n", + "plt.rcParams['xtick.direction'] = 'in' \n", + "\n", + "plt.rcParams['ytick.major.size'] = 8\n", + "plt.rcParams['ytick.minor.size'] = 4\n", + "plt.rcParams['ytick.major.pad'] = 8\n", + "plt.rcParams['ytick.minor.pad'] = 8\n", + "plt.rcParams['ytick.color'] = 'k'\n", + "plt.rcParams['ytick.labelsize'] = 'medium'\n", + "plt.rcParams['ytick.direction'] = 'in'\n", + "plt.rcParams['text.usetex'] = True\n", + "plt.rcParams['text.latex.unicode'] = True" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def find_L1_norm(epsilon, dx_dxi, x):\n", + " '''\n", + " '''\n", + " lagrange_basis, temp = lagrange.lagrange_polynomials(x)\n", + " \n", + " epsilon_interpol = 0.\n", + " for i in np.arange(x.shape[0]):\n", + " epsilon_interpol += lagrange_basis[i] * epsilon[i]\n", + " \n", + " return integrate.quad(epsilon_interpol * dx_dxi, -1, 1)[0]\n", + "\n", + "\n", + "def integrate_quad(function, order, scheme = 'gauss_legendre'):\n", + " '''\n", + " '''\n", + " if scheme == 'gauss_legendre':\n", + " nodes = np.array(lagrange.gauss_nodes(order))\n", + " weights = np.array(lagrange.gaussian_weights(order))\n", + "\n", + " elif scheme == 'gauss_lobatto':\n", + " nodes = np.array(lagrange.LGL_points(order))\n", + " weights = np.array(lagrange.lobatto_weights(order))\n", + " \n", + " else:\n", + " return\n", + "\n", + " integral = 0.\n", + "\n", + " for node, weight in zip(nodes, weights):\n", + " integral += weight * function(node)\n", + " \n", + " return integral" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def test_function(x):\n", + " '''\n", + " The test wave function.\n", + " '''\n", + " return np.sin(2 * np.pi * x)\n", + "\n", + "def int_sin2pix_dLdxi(x_nodes, xi_LGL, lagrange_basis_order):\n", + " '''\n", + " '''\n", + " L_i, temp = lagrange.lagrange_polynomials(xi_LGL)\n", + " \n", + " def sin2pix_dLdxi(xi):\n", + " x = (((x_nodes[1] - x_nodes[0]) * xi + (x_nodes[1] + x_nodes[0]))) / 2\n", + " return np.sin(2 * np.pi * x) * L_i[lagrange_basis_order].deriv()(xi)\n", + " \n", + " return integrate.quad(sin2pix_dLdxi, -1, 1)[0]\n", + "\n", + "def int_exp_dLdxi(x_nodes, xi_LGL, lagrange_basis_order):\n", + " '''\n", + " '''\n", + " L_i, temp = lagrange.lagrange_polynomials(xi_LGL)\n", + " \n", + " def exp_dLdxi(xi):\n", + " x = (((x_nodes[1] - x_nodes[0]) * xi + (x_nodes[1] + x_nodes[0]))) / 2\n", + " return np.e ** (-x ** 2 / 0.4 ** 2) * L_i[lagrange_basis_order].deriv()(xi)\n", + " \n", + " return integrate.quad(exp_dLdxi, -1, 1)[0]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def change_parameters(N_LGL = 8, N_Elements = 8, N_quad = 8, wave = 'sin'):\n", + " '''\n", + " '''\n", + " # The domain of the function.\n", + " params.x_nodes = af.np_to_af_array(np.array([0, 1.]))\n", + "\n", + " # The number of LGL points into which an element is split.\n", + " params.N_LGL = N_LGL\n", + "\n", + " # Number of elements the domain is to be divided into.\n", + " params.N_Elements = N_Elements\n", + "\n", + " # The number quadrature points to be used for integration.\n", + " params.N_quad = N_quad\n", + "\n", + " # Array containing the LGL points in xi space.\n", + " params.xi_LGL = lagrange.LGL_points(params.N_LGL)\n", + "\n", + " # N_Gauss number of Gauss nodes.\n", + " params.gauss_points = af.np_to_af_array(lagrange.gauss_nodes\\\n", + " (params.N_quad))\n", + "\n", + " # The Gaussian weights.\n", + " params.gauss_weights = lagrange.gaussian_weights(params.N_quad)\n", + "\n", + " # The lobatto nodes to be used for integration.\n", + " params.lobatto_quadrature_nodes = lagrange.LGL_points(params.N_quad)\n", + "\n", + " # The lobatto weights to be used for integration.\n", + " params.lobatto_weights_quadrature = lagrange.lobatto_weights\\\n", + " (params.N_quad)\n", + "\n", + " # A list of the Lagrange polynomials in poly1d form.\n", + " params.lagrange_product = lagrange.product_lagrange_poly(params.xi_LGL)\n", + "\n", + " # An array containing the coefficients of the lagrange basis polynomials.\n", + " params.lagrange_coeffs = af.np_to_af_array(\\\n", + " lagrange.lagrange_polynomials(params.xi_LGL)[1])\n", + "\n", + " # Refer corresponding functions.\n", + " params.lagrange_basis_value = lagrange.lagrange_function_value\\\n", + " (params.lagrange_coeffs)\n", + "\n", + " # A list of the Lagrange polynomials in poly1d form.\n", + " params.lagrange_poly1d_list = lagrange.lagrange_polynomials(params.xi_LGL)[0]\n", + "\n", + "\n", + " # list containing the poly1d forms of the differential of Lagrange\n", + " # basis polynomials.\n", + " params.differential_lagrange_polynomial = lagrange.differential_lagrange_poly1d()\n", + "\n", + "\n", + " # While evaluating the volume integral using N_LGL\n", + " # lobatto quadrature points, The integration can be vectorized\n", + " # and in this case the coefficients of the differential of the\n", + " # Lagrange polynomials is required\n", + " params.volume_integrand_N_LGL = np.zeros(([params.N_LGL, params.N_LGL - 1]))\n", + "\n", + " for i in range(params.N_LGL):\n", + " params.volume_integrand_N_LGL[i] = (params.differential_lagrange_polynomial[i]).c\n", + "\n", + " params.volume_integrand_N_LGL= af.np_to_af_array(params.volume_integrand_N_LGL)\n", + "\n", + " # Obtaining an array consisting of the LGL points mapped onto the elements.\n", + "\n", + " params.element_size = af.sum((params.x_nodes[1] - params.x_nodes[0])\\\n", + " / params.N_Elements)\n", + " params.elements_xi_LGL = af.constant(0, params.N_Elements, params.N_LGL)\n", + " params.elements = utils.linspace(af.sum(params.x_nodes[0]),\n", + " af.sum(params.x_nodes[1] - params.element_size),\\\n", + " params.N_Elements)\n", + "\n", + " params.np_element_array = np.concatenate((af.transpose(params.elements),\n", + " af.transpose(params.elements +\\\n", + " params.element_size)))\n", + "\n", + " params.element_mesh_nodes = utils.linspace(af.sum(params.x_nodes[0]),\n", + " af.sum(params.x_nodes[1]),\\\n", + " params.N_Elements + 1)\n", + "\n", + " params.element_array = af.transpose(af.np_to_af_array\\\n", + " (params.np_element_array))\n", + " params.element_LGL = wave_equation.mapping_xi_to_x(af.transpose\\\n", + " (params.element_array), params.xi_LGL)\n", + "\n", + " # The minimum distance between 2 mapped LGL points.\n", + " params.delta_x = af.min((params.element_LGL - af.shift(params.element_LGL, 1, 0))[1:, :])\n", + "\n", + " # dx_dxi for elements of equal size.\n", + " params. dx_dxi = af.mean(wave_equation.dx_dxi_numerical((params.element_mesh_nodes[0 : 2]),\\\n", + " params.xi_LGL))\n", + "\n", + "\n", + " # The value of time-step.\n", + " params.delta_t = params.delta_x / (4 * params.c)\n", + "\n", + " # Array of timesteps seperated by delta_t.\n", + " params.time = utils.linspace(0, int(params.total_time / params.delta_t) * params.delta_t,\n", + " int(params.total_time / params.delta_t))\n", + "\n", + " if (wave =='sin'):\n", + " params.u_init = af.sin(2 * np.pi * params.element_LGL)\n", + " \n", + " if (wave =='gaussian'):\n", + " params.u_init = np.e ** (-(params.element_LGL) ** 2 / 0.4 ** 2)\n", + " \n", + " params.u = af.constant(0, params.N_LGL, params.N_Elements, params.time.shape[0],\\\n", + " dtype = af.Dtype.f64)\n", + " params.u[:, :, 0] = params.u_init\n", + " \n", + " return\n" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option1\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n", + "option3\n" + ] + } + ], + "source": [ + "p = 0\n", + "error = []\n", + "N_LGL = 8\n", + "N_quad = 8\n", + "\n", + "for N_quad in np.arange(3, 31):\n", + " change_parameters(int(N_LGL), N_quad = int(N_quad), wave = 'gaussian')\n", + " volume_integral_flux = wave_equation.volume_integral_flux(params.u_init, 0)\n", + " volume_integral_flux_analytical = np.zeros([params.N_LGL, params.N_Elements])\n", + "\n", + " for p in np.arange(N_LGL):\n", + " for element_idx, element in enumerate(params.element_array):\n", + " volume_integral_flux_analytical[p][element_idx] = int_exp_dLdxi(np.array(element)[0],\n", + " params.xi_LGL, p)\n", + "\n", + " volume_integral_flux_analytical = af.np_to_af_array(volume_integral_flux_analytical)\n", + "\n", + " error.append(np.array(af.sum(af.abs(volume_integral_flux_analytical - volume_integral_flux), 1))[p])" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.semilogy(np.arange(3, 31), error, '-o')\n", + "plt.title(r'Error for $\\int e^{\\frac{-x^2}{0.4^2}} \\frac{d L_0}{d\\xi} d\\xi$ vs $N_{quad}(N_{LGL} = 8)$')\n", + "# plt.title(r'Error for $\\int e^{\\frac{-x^2}{0.4^2}} \\frac{d L_0}{d\\xi} d\\xi$ vs $N_{LGL} (N_{quad} = 8)$')\n", + "# plt.title(r'Error for $\\int \\sin(2 \\pi x) \\frac{d L_0}{d\\xi} d\\xi$ vs $N_{quad}$')\n", + "plt.xlabel(r'$N_{quad}$')\n", + "plt.ylabel(r'Error')\n", + "plt.savefig('error_int_fxi_dLi_dxi.png')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[8 8 1 1]\n", + " -0.0140 -0.7170 -1.0000 -0.6972 0.0140 0.7170 1.0000 0.6972 \n", + " -0.0826 -0.0555 0.0042 0.0614 0.0826 0.0555 -0.0042 -0.0614 \n", + " -0.1322 -0.0784 0.0214 0.1086 0.1322 0.0784 -0.0214 -0.1086 \n", + " -0.1542 -0.0741 0.0495 0.1440 0.1542 0.0741 -0.0495 -0.1440 \n", + " -0.1440 -0.0495 0.0741 0.1542 0.1440 0.0495 -0.0741 -0.1542 \n", + " -0.1086 -0.0214 0.0784 0.1322 0.1086 0.0214 -0.0784 -0.1322 \n", + " -0.0614 -0.0042 0.0555 0.0826 0.0614 0.0042 -0.0555 -0.0826 \n", + " 0.6972 1.0000 0.7170 0.0140 -0.6972 -1.0000 -0.7170 -0.0140 \n", + "\n", + "\n" + ] + } + ], + "source": [ + "N_LGL = 8\n", + "volume_integral_flux_analytical = np.zeros([params.N_LGL, params.N_Elements])\n", + "\n", + "for p in np.arange(N_LGL):\n", + " for element_idx, element in enumerate(params.element_array):\n", + " volume_integral_flux_analytical[p][element_idx] = int_sin2pix_dLdxi(np.array(element)[0],\n", + " params.xi_LGL, p)\n", + "\n", + "volume_integral_flux_analytical = af.np_to_af_array(volume_integral_flux_analytical)\n", + "print(volume_integral_flux_analytical)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.0000 0.1250 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.1250 0.2500 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.2500 0.3750 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.3750 0.5000 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.5000 0.6250 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.6250 0.7500 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.7500 0.8750 \n", + "\n", + "arrayfire.Array()\n", + "Type: double\n", + "\n", + "[1 2 1 1]\n", + " 0.8750 1.0000 \n", + "\n" + ] + } + ], + "source": [ + "for element in params.element_array:\n", + " print(element)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-0.01402496723026015" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "analytical_integral = 0.\n", + "p = 0\n", + "\n", + "\n", + "int_sin2pix_dLdxi(np.array(params.element_array[0])[0],\n", + " params.xi_LGL, p)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 0. 0.125]\n" + ] + } + ], + "source": [ + "print(np.array(params.element_array[0])[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..03505e1 --- /dev/null +++ b/main.py @@ -0,0 +1,5 @@ +from dg_maxwell import params +from dg_maxwell import wave_equation + +if __name__ == '__main__': + print(wave_equation.convergence_test()) diff --git a/post_processing.py b/post_processing.py new file mode 100644 index 0000000..f28d87a --- /dev/null +++ b/post_processing.py @@ -0,0 +1,68 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os + +from matplotlib import pyplot as pl +import arrayfire as af +import numpy as np +import h5py + +from dg_maxwell import params + +pl.rcParams['figure.figsize' ] = 9.6, 6. +pl.rcParams['figure.dpi' ] = 300 +pl.rcParams['image.cmap' ] = 'jet' +pl.rcParams['lines.linewidth' ] = 1.5 +pl.rcParams['font.family' ] = 'serif' +pl.rcParams['font.weight' ] = 'bold' +pl.rcParams['font.size' ] = 20 +pl.rcParams['font.sans-serif' ] = 'serif' +pl.rcParams['text.usetex' ] = True +pl.rcParams['axes.linewidth' ] = 1.5 +pl.rcParams['axes.titlesize' ] = 'medium' +pl.rcParams['axes.labelsize' ] = 'medium' +pl.rcParams['xtick.major.size'] = 8 +pl.rcParams['xtick.minor.size'] = 4 +pl.rcParams['xtick.major.pad' ] = 8 +pl.rcParams['xtick.minor.pad' ] = 8 +pl.rcParams['xtick.color' ] = 'k' +pl.rcParams['xtick.labelsize' ] = 'medium' +pl.rcParams['xtick.direction' ] = 'in' +pl.rcParams['ytick.major.size'] = 8 +pl.rcParams['ytick.minor.size'] = 4 +pl.rcParams['ytick.major.pad' ] = 8 +pl.rcParams['ytick.minor.pad' ] = 8 +pl.rcParams['ytick.color' ] = 'k' +pl.rcParams['ytick.labelsize' ] = 'medium' +pl.rcParams['ytick.direction' ] = 'in' + + + + + + +# Creating a folder to store hdf5 files. If it doesn't exist. +results_directory = 'results/1D_Wave_images' + +if not os.path.exists(results_directory): + os.makedirs(results_directory) + + +N = os.system('cd results/hdf5 && find -maxdepth 1 -type f | wc -l') + + +for i in range(0,int(186)): + fig = pl.figure() + h5py_data = h5py.File('results/hdf5/dump_timestep_%06d'%(50 * i) + '.hdf5', 'r') + u_LGL = (h5py_data['u_i'][:]) + pl.plot(params.element_LGL, u_LGL) + pl.xlabel('x') + pl.ylabel('u') + pl.xlim(-1, 1) + pl.ylim(-2, 2) + pl.title('Time = %f' % (i * 4 * params.delta_t)) + fig.savefig('results/1D_Wave_images/%04d' %(i) + '.png') + pl.close('all') + + diff --git a/requirements.txt b/requirements.txt index e224414..f7b66f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,5 @@ matplotlib tqdm scipy gmshtranslator +h5py +