Exemple #1
0
def mms_source(sim, strong_residual, manufactured_solution):

    V = sim.solution.function_space()

    _r = strong_residual(sim, solution=manufactured_solution(sim))

    if type(sim.solution.function_space().ufl_element()) is fe.FiniteElement:

        r = (_r, )

    else:

        r = _r

    psi = fe.TestFunctions(V)

    s = fe.inner(psi[0], r[0])

    if len(r) > 1:

        for psi_i, r_i in zip(psi[1:], r[1:]):

            s += fe.inner(psi_i, r_i)

    return s
Exemple #2
0
    def equation(z):
        Z = z.function_space()
        φ, v = firedrake.TestFunctions(Z)
        h, q = firedrake.split(z)
        F_h, F_q = _fluxes(h, q, g)

        mesh = Z.mesh()
        n = firedrake.FacetNormal(mesh)
        c = abs(inner(q / h, n)) + sqrt(g * h)

        sources = -inner(g * h * grad(b), v) * dx

        fluxes = (forms.cell_flux(F_h, φ) + forms.central_facet_flux(F_h, φ) +
                  forms.lax_friedrichs_facet_flux(h, c, φ) +
                  forms.cell_flux(F_q, v) + forms.central_facet_flux(F_q, v) +
                  forms.lax_friedrichs_facet_flux(q, c, v))

        boundary_ids = set(mesh.exterior_facets.unique_markers)
        wall_ids = tuple(boundary_ids - set(outflow_ids) - set(inflow_ids))
        q_wall = q - 2 * inner(q, n) * n
        boundary_fluxes = (
            _boundary_flux(z, h, q, g, outflow_ids) +
            _boundary_flux(z, h_in, q_in, g, inflow_ids) +
            _boundary_flux(z, h, q_wall, g, wall_ids) +
            forms.lax_friedrichs_boundary_flux(h, h, c, φ, outflow_ids) +
            forms.lax_friedrichs_boundary_flux(q, q, c, v, outflow_ids) +
            forms.lax_friedrichs_boundary_flux(h, h_in, c, φ, inflow_ids) +
            forms.lax_friedrichs_boundary_flux(q, q_in, c, v, inflow_ids) +
            forms.lax_friedrichs_boundary_flux(h, h, c, φ, wall_ids) +
            forms.lax_friedrichs_boundary_flux(q, q_wall, c, v, wall_ids))

        return sources - fluxes - boundary_fluxes
Exemple #3
0
def variational_form_residual(sim, solution):

    Gr = sim.grashof_number

    Pr = sim.prandtl_number

    ihat, jhat = sapphire.simulation.unit_vectors(sim.mesh)

    sim.gravity_direction = fe.Constant(-jhat)

    ghat = sim.gravity_direction

    p, u, T = fe.split(solution)

    psi_p, psi_u, psi_T = fe.TestFunctions(solution.function_space())

    mass = psi_p * div(u)

    momentum = dot(psi_u, grad(u)*u + Gr*T*ghat) \
        - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u)))

    energy = psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. / Pr * grad(T))

    dx = fe.dx(degree=sim.quadrature_degree)

    return (mass + momentum + energy) * dx
Exemple #4
0
 def energy(self):
     
     Re = self.reynolds_number
     
     Pr = self.prandtl_number
     
     Ste = self.stefan_number
     
     _, u, T = fe.split(self.solution)
     
     phil = self.liquid_volume_fraction(temperature = T)
     
     rho_sl = self.density_solid_to_liquid_ratio
     
     c_sl = self.heat_capacity_solid_to_liquid_ratio
     
     C = phase_dependent_material_property(rho_sl*c_sl)(phil)
     
     k_sl = self.thermal_conductivity_solid_to_liquid_ratio
     
     k = phase_dependent_material_property(k_sl)(phil)
     
     CT_t, phil_t = self.extra_time_discrete_terms()
     
     _, _, psi_T = fe.TestFunctions(self.solution_space)
     
     dx = fe.dx(degree = self.quadrature_degree)
     
     return (psi_T*(CT_t + 1./Ste*phil_t + dot(u, grad(C*T))) \
         + 1./(Re*Pr)*dot(grad(psi_T), k*grad(T)))*dx
def compute_inf_sup_constant(spaces, b, inner_products):
    V, Q = spaces
    m, n = inner_products

    Z = V * Q
    u, p = firedrake.TrialFunctions(Z)
    v, q = firedrake.TestFunctions(Z)

    A = assemble(-(m(u, v) + b(v, p) + b(u, q)), mat_type='aij').M.handle
    M = assemble(n(p, q), mat_type='aij').M.handle

    opts = PETSc.Options()
    opts.setValue('eps_gen_hermitian', None)
    opts.setValue('eps_target_real', None)
    opts.setValue('eps_smallest_magnitude', None)
    opts.setValue('st_type', 'sinvert')
    opts.setValue('st_ksp_type', 'preonly')
    opts.setValue('st_pc_type', 'lu')
    opts.setValue('st_pc_factor_mat_solver_type', 'mumps')
    opts.setValue('eps_tol', 1e-8)

    num_values = 1
    eigensolver = SLEPc.EPS().create(comm=firedrake.COMM_WORLD)
    eigensolver.setDimensions(num_values)
    eigensolver.setOperators(A, M)
    eigensolver.setFromOptions()
    eigensolver.solve()

    Vr, Vi = A.getVecs()
    λ = eigensolver.getEigenpair(0, Vr, Vi)
    return λ
def heat_driven_cavity_variational_form_residual(sim, solution):

    mass = sapphire.simulations.convection_coupled_phasechange.\
        mass(sim, solution)

    stabilization = sapphire.simulations.convection_coupled_phasechange.\
        stabilization(sim, solution)

    p, u, T = fe.split(solution)

    b = sapphire_ice.simulation.water_buoyancy(sim=sim, temperature=T)

    Pr = sim.prandtl_number

    _, psi_u, psi_T = fe.TestFunctions(sim.function_space)

    inner, dot, grad, div, sym = \
        fe.inner, fe.dot, fe.grad, fe.div, fe.sym

    momentum = dot(psi_u, grad(u)*u + b) \
        - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u)))

    energy = psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. / Pr * grad(T))

    return mass + momentum + energy + stabilization
Exemple #7
0
def energy(sim, solution):

    Pr = sim.prandtl_number

    Ste = sim.stefan_number

    _, u, T = fe.split(solution)

    phil = liquid_volume_fraction(sim=sim, temperature=T)

    rho_sl = sim.density_solid_to_liquid_ratio

    c_sl = sim.heat_capacity_solid_to_liquid_ratio

    C = phase_dependent_material_property(rho_sl * c_sl)(phil)

    k_sl = sim.thermal_conductivity_solid_to_liquid_ratio

    k = phase_dependent_material_property(k_sl)(phil)

    _, CT_t, phil_t = time_discrete_terms(sim=sim)

    _, _, psi_T = fe.TestFunctions(solution.function_space())

    return psi_T*(CT_t + 1./Ste*phil_t + dot(u, grad(C*T))) \
        + 1./Pr*dot(grad(psi_T), k*grad(T))
Exemple #8
0
    def __init__(self, *args, meshsize, **kwargs):

        self.hot_wall_temperature = fe.Constant(1.)

        self.cold_wall_temperature = fe.Constant(-0.01)

        self.topwall_heatflux = fe.Constant(0.)

        super().__init__(
            *args,
            mesh=fe.UnitSquareMesh(meshsize, meshsize),
            initial_values=initial_values,
            dirichlet_boundary_conditions=dirichlet_boundary_conditions,
            **kwargs)

        q = self.topwall_heatflux

        _, _, psi_T = fe.TestFunctions(self.function_space)

        ds = fe.ds(domain=self.mesh, subdomain_id=4)

        self.variational_form_residual += psi_T * q * ds

        Ra = 3.27e5

        Pr = 56.2

        self.grashof_number = self.grashof_number.assign(Ra / Pr)

        self.prandtl_number = self.prandtl_number.assign(Pr)

        self.stefan_number = self.stefan_number.assign(0.045)

        self.liquidus_temperature = self.liquidus_temperature.assign(0.)
Exemple #9
0
 def get_weak_form(self):
     (v, q) = fd.TestFunctions(self.V)
     (u, p) = fd.split(self.solution)
     F = self.nu * fd.inner(fd.grad(u), fd.grad(v)) * fd.dx \
         - p * fd.div(v) * fd.dx \
         + fd.div(u) * q * fd.dx \
         + fd.inner(fda.Constant((0., 0.)), v) * fd.dx
     return F
Exemple #10
0
 def mass(self):
     
     u, _ = fe.split(self.solution)
     
     _, psi_p = fe.TestFunctions(self.solution_space)
     
     dx = fe.dx(degree = self.quadrature_degree)
     
     return psi_p*div(u)*dx
Exemple #11
0
def stabilization(sim, solution):

    p, _, _ = fe.split(solution)

    psi_p, _, _ = fe.TestFunctions(solution.function_space())

    gamma = sim.pressure_penalty_factor

    return gamma * psi_p * p
    def energy(self):

        _, T_t = self.time_discrete_terms()

        _, _, psi_T = fe.TestFunctions(self.solution_space)

        dx = fe.dx(degree=self.quadrature_degree)

        return super().energy() + psi_T * T_t * dx
Exemple #13
0
    def momentum(self):

        u_t = self.time_discrete_terms()

        psi_u, _ = fe.TestFunctions(self.solution_space)

        dx = fe.dx(degree=self.quadrature_degree)

        return super().momentum() + dot(psi_u, u_t) * dx
Exemple #14
0
 def momentum(self):
     
     _, u, T = fe.split(self.solution)
     
     d = self.solid_velocity_relaxation(temperature = T)
     
     _, psi_u, _ = fe.TestFunctions(self.solution_space)
     
     dx = fe.dx(degree = self.quadrature_degree)
     
     return super().momentum() + dot(psi_u, d*u)*dx
Exemple #15
0
def mass(sim, solution):

    _, u, _ = fe.split(solution)

    psi_p, _, _ = fe.TestFunctions(solution.function_space())

    div = fe.div

    mass = psi_p * div(u)

    return mass
Exemple #16
0
def _boundary_flux(z, h_ext, q_ext, g, boundary_ids):
    Z = z.function_space()
    n = firedrake.FacetNormal(Z.mesh())
    φ, v = firedrake.TestFunctions(Z)

    F_hx, F_qx = _fluxes(h_ext, q_ext, g)

    h, q = firedrake.split(z)
    F_h, F_q = _fluxes(h, q, g)

    return 0.5 * (inner(F_hx, φ * n) + inner(F_qx, outer(v, n)) + inner(
        F_h, φ * n) + inner(F_q, outer(v, n))) * ds(boundary_ids)
Exemple #17
0
 def momentum(self):
     
     u, p = fe.split(self.solution)
     
     Re = self.reynolds_number
     
     psi_u, _ = fe.TestFunctions(self.solution_space)
     
     dx = fe.dx(degree = self.quadrature_degree)
     
     return (dot(psi_u, grad(u)*u) - div(psi_u)*p + \
         2./Re*inner(sym(grad(psi_u)), sym(grad(u))))*dx
Exemple #18
0
    def dgls_form(self, problem, mesh, bcs_p):
        rho = problem.rho
        mu = problem.mu
        k = problem.k
        f = problem.f

        q, p = fire.TrialFunctions(self._W)
        w, v = fire.TestFunctions(self._W)

        n = fire.FacetNormal(mesh)
        h = fire.CellDiameter(mesh)

        # Stabilizing parameters
        has_mesh_characteristic_length = True
        delta_0 = fire.Constant(1)
        delta_1 = fire.Constant(-1 / 2)
        delta_2 = fire.Constant(1 / 2)
        delta_3 = fire.Constant(1 / 2)
        eta_p = fire.Constant(100)
        eta_q = fire.Constant(100)
        h_avg = (h("+") + h("-")) / 2.0
        if has_mesh_characteristic_length:
            delta_2 = delta_2 * h * h
            delta_3 = delta_3 * h * h

        kappa = rho * k / mu
        inv_kappa = 1.0 / kappa

        # Classical mixed terms
        a = (dot(inv_kappa * q, w) - div(w) * p - delta_0 * v * div(q)) * dx
        L = -delta_0 * f * v * dx

        # DG terms
        a += jump(w, n) * avg(p) * dS - avg(v) * jump(q, n) * dS

        # Edge stabilizing terms
        a += (eta_q * h_avg) * avg(inv_kappa) * (
            jump(q, n) * jump(w, n)) * dS + (eta_p / h_avg) * avg(kappa) * dot(
                jump(v, n), jump(p, n)) * dS

        # Add the contributions of the pressure boundary conditions to L
        for pboundary, iboundary in bcs_p:
            L -= pboundary * dot(w, n) * ds(iboundary)

        # Stabilizing terms
        a += (delta_1 * inner(kappa * (inv_kappa * q + grad(p)),
                              delta_0 * inv_kappa * w + grad(v)) * dx)
        a += delta_2 * inv_kappa * div(q) * div(w) * dx
        a += delta_3 * inner(kappa * curl(inv_kappa * q), curl(
            inv_kappa * w)) * dx
        L += delta_2 * inv_kappa * f * div(w) * dx

        return a, L
    def momentum(self):

        p, u, T = fe.split(self.solution)

        _, psi_u, _ = fe.TestFunctions(self.solution_space)

        b = self.buoyancy(temperature=T)

        Re = self.reynolds_number

        dx = fe.dx(degree=self.quadrature_degree)

        return (dot(psi_u, grad(u)*u + b) - div(psi_u)*p + \
            2./Re*inner(sym(grad(psi_u)), sym(grad(u))))*dx
Exemple #20
0
def variational_form_residual(sim, solution):

    u, p = fe.split(solution)

    psi_u, psi_p = fe.TestFunctions(solution.function_space())

    mass = psi_p * div(u)

    momentum = dot(psi_u, grad(u)*u) - div(psi_u)*p + \
        2.*inner(sym(grad(psi_u)), sym(grad(u)))

    dx = fe.dx(degree=sim.quadrature_degree)

    return (mass + momentum) * dx
Exemple #21
0
def momentum(sim, solution, buoyancy=linear_boussinesq_buoyancy):

    p, u, T = fe.split(solution)

    u_t, _, _ = time_discrete_terms(sim=sim)

    b = buoyancy(sim=sim, temperature=T)

    d = solid_velocity_relaxation(sim=sim, temperature=T)

    _, psi_u, _ = fe.TestFunctions(solution.function_space())

    return dot(psi_u, u_t + grad(u)*u + b + d*u) \
        - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u)))
    def energy(self):

        Re = self.reynolds_number

        Pr = self.prandtl_number

        _, u, T = fe.split(self.solution)

        _, _, psi_T = fe.TestFunctions(self.solution_space)

        dx = fe.dx(degree=self.quadrature_degree)

        return (psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. /
                                              (Re * Pr) * grad(T))) * dx
Exemple #23
0
    def _set_para_form(self):
        """
        Constructs the bilinear form for the all at once system.
        Specific to the theta-centred Crank-Nicholson method
        """

        M = self.M
        w_all_cpts = fd.split(self.w_all)

        test_fns = fd.TestFunctions(self.W_all)

        dt = fd.Constant(self.dt)
        theta = fd.Constant(self.theta)
        alpha = fd.Constant(self.alpha)
        wMs = w_all_cpts[self.ncpts * (M - 1):]
        if self.circ == "picard":
            if self.ncpts == 1:
                wMkm1s = [self.w_prev]
            else:
                wMkm1s = fd.split(self.w_prev)

        for n in range(M):
            # previous time level
            if n == 0:
                w0ss = fd.split(self.w0)
                if self.circ == "picard":
                    w0s = [
                        w0ss[i] + alpha * (wMs[i] - wMkm1s[i])
                        for i in range(self.ncpts)
                    ]
                else:
                    w0s = w0ss
            else:
                w0s = w_all_cpts[self.ncpts * (n - 1):self.ncpts * n]
            # current time level
            w1s = w_all_cpts[self.ncpts * n:self.ncpts * (n + 1)]
            dws = test_fns[self.ncpts * n:self.ncpts * (n + 1)]
            # time derivative
            if n == 0:
                p_form = (1.0 / dt) * self.form_mass(*w1s, *dws)
            else:
                p_form += (1.0 / dt) * self.form_mass(*w1s, *dws)
            p_form -= (1.0 / dt) * self.form_mass(*w0s, *dws)
            # vector field
            p_form += theta * self.form_function(*w1s, *dws)
            p_form += (1 - theta) * self.form_function(*w0s, *dws)
        self.para_form = p_form
def variational_form_residual(sim, solution):

    u, p = fe.split(sim.solution)

    u_t, _ = sapphire.simulation.time_discrete_terms(
        solutions=sim.solutions, timestep_size=sim.timestep_size)

    psi_u, psi_p = fe.TestFunctions(sim.solution.function_space())

    mass = psi_p * div(u)

    momentum = dot(psi_u, u_t + grad(u)*u) - div(psi_u)*p + \
        2.*inner(sym(grad(psi_u)), sym(grad(u)))

    dx = fe.dx(degree=sim.quadrature_degree)

    return (mass + momentum) * dx
Exemple #25
0
def test_solver_no_flow_region():
    mesh = fd.Mesh("./2D_mesh.msh")
    no_flow = [2]
    no_flow_markers = [1]
    mesh = mark_no_flow_regions(mesh, no_flow, no_flow_markers)
    P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1)
    P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1)
    TH = P2 * P1
    W = fd.FunctionSpace(mesh, TH)
    (v, q) = fd.TestFunctions(W)

    # Stokes 1
    w_sol1 = fd.Function(W)
    nu = fd.Constant(0.05)
    F = NavierStokesBrinkmannForm(W, w_sol1, nu, beta_gls=2.0)

    x, y = fd.SpatialCoordinate(mesh)
    u_mms = fd.as_vector(
        [sin(2.0 * pi * x) * sin(pi * y),
         sin(pi * x) * sin(2.0 * pi * y)])
    p_mms = -0.5 * (u_mms[0]**2 + u_mms[1]**2)
    f_mms_u = (grad(u_mms) * u_mms + grad(p_mms) -
               2.0 * nu * div(sym(grad(u_mms))))
    f_mms_p = div(u_mms)
    F += -inner(f_mms_u, v) * dx - f_mms_p * q * dx
    bc1 = fd.DirichletBC(W.sub(0), u_mms, "on_boundary")
    bc2 = fd.DirichletBC(W.sub(1), p_mms, "on_boundary")
    bc_no_flow = InteriorBC(W.sub(0), fd.Constant((0.0, 0.0)), no_flow_markers)

    solver_parameters = {"ksp_max_it": 500, "ksp_monitor": None}

    problem1 = fd.NonlinearVariationalProblem(F,
                                              w_sol1,
                                              bcs=[bc1, bc2, bc_no_flow])
    solver1 = NavierStokesBrinkmannSolver(
        problem1,
        options_prefix="navier_stokes",
        solver_parameters=solver_parameters,
    )
    solver1.solve()
    u_sol, _ = w_sol1.split()
    u_mms_func = fd.interpolate(u_mms, W.sub(0))
    error = fd.errornorm(u_sol, u_mms_func)
    assert error < 0.07
Exemple #26
0
def run_solver(r):
    mesh = fd.UnitSquareMesh(2**r, 2**r)
    P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1)
    P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1)
    TH = P2 * P1
    W = fd.FunctionSpace(mesh, TH)
    (v, q) = fd.TestFunctions(W)

    # Stokes 1
    w_sol1 = fd.Function(W)
    nu = fd.Constant(0.05)
    F = NavierStokesBrinkmannForm(W, w_sol1, nu, beta_gls=2.0)

    from firedrake import sin, grad, pi, sym, div, inner

    x, y = fd.SpatialCoordinate(mesh)
    u_mms = fd.as_vector(
        [sin(2.0 * pi * x) * sin(pi * y),
         sin(pi * x) * sin(2.0 * pi * y)])
    p_mms = -0.5 * (u_mms[0]**2 + u_mms[1]**2)
    f_mms_u = (grad(u_mms) * u_mms + grad(p_mms) -
               2.0 * nu * div(sym(grad(u_mms))))
    f_mms_p = div(u_mms)
    F += -inner(f_mms_u, v) * dx - f_mms_p * q * dx
    bc1 = fd.DirichletBC(W.sub(0), u_mms, "on_boundary")
    bc2 = fd.DirichletBC(W.sub(1), p_mms, "on_boundary")

    solver_parameters = {"ksp_max_it": 200}

    problem1 = fd.NonlinearVariationalProblem(F, w_sol1, bcs=[bc1, bc2])
    solver1 = NavierStokesBrinkmannSolver(
        problem1,
        options_prefix="navier_stokes",
        solver_parameters=solver_parameters,
    )
    solver1.solve()
    u_sol, _ = w_sol1.split()
    fd.File("test_u_sol.pvd").write(u_sol)
    u_mms_func = fd.interpolate(u_mms, W.sub(0))
    error = fd.errornorm(u_sol, u_mms_func)
    print(f"Error: {error}")
    return error
Exemple #27
0
    def __init__(self, equations, solution, fields, coupling, dt, bcs=None, solver_parameters={}, theta=1.0,
                 predictor_solver_parameters={}, picard_iterations=1, pressure_nullspace=None):
        super().__init__(equations, solution, fields, coupling, dt, bcs=bcs, solver_parameters=solver_parameters)
        self.theta = firedrake.Constant(theta)
        self.theta_p = 1  # should not be used for now - maybe revisit with free surface terms
        self.predictor_solver_parameters = predictor_solver_parameters
        self.picard_iterations = picard_iterations
        self.pressure_nullspace = pressure_nullspace

        self.solution_old = firedrake.Function(self.solution)
        self.solution_lag = firedrake.Function(self.solution)
        self.u_test, self.p_test = firedrake.TestFunctions(self.solution.function_space())

        # the predictor space is the same as the first sub-space of the solution space, but indexed independently
        mesh = self.solution.function_space().mesh()
        self.u_space = firedrake.FunctionSpace(mesh, self.solution.split()[0].ufl_element())
        self.u_star_test = firedrake.TestFunction(self.u_space)
        self.u_star = firedrake.Function(self.u_space)

        self._initialized = False
Exemple #28
0
    def __init__(self, equations, solution, fields, coupling, dt, bcs=None, solver_parameters={}):
        """
        :arg equations: the equations to solve
        :type equations: list of :class:`BaseEquation` objects
        :arg solution: :class:`Function` of MixedFunctionSpace where solution will be stored
        :arg fields: Dictionary of fields that are passed to the equation (any functions that are not part of the solution)
        :type fields: dict of :class:`Function` or :class:`Constant` objects
        :arg coupling: for each equation a map (dict) from field name to number of the (different) equation whose trial function
                       should be used for that field name (see example below)
        :type coulings: list of dicts
        :arg float dt: time step in seconds
        :kwarg dict solver_parameters: PETSc solver options


        Example for coupling:
        Suppose we couple three equations:
        0) a scalar adv. diff. equation for a density
        1) a momentum equation to solve for velocity
        2) a contintuity equation with associated trial function of pressure
        Then if equation 0) uses velocity of 1), equation 1) uses pressure of 2) and
        density of 0), and equation 2) is a continuity constraint on velocity of 1), we get:
            coupling = [{'velocity': 1}, {'pressure': 2, 'density': 0}, {'velocity': 1}]
        """

        super(CoupledTimeIntegrator, self).__init__()

        self.equations = equations
        self.solution = solution
        self.test = firedrake.TestFunctions(solution.function_space())
        self.fields = fields
        self.coupling = coupling
        self.dt = dt
        self.dt_const = firedrake.Constant(dt)
        self.bcs = bcs

        # unique identifier used in solver
        self.name = '-'.join([self.__class__.__name__]
                             + [eq.__class__.__name__ for eq in self.equations])

        self.solver_parameters = {}
        self.solver_parameters.update(solver_parameters)
Exemple #29
0
    V = fd.VectorFunctionSpace(mesh, "DQ", order - 1)
    T = fd.TensorFunctionSpace(mesh, "DQ", order - 1)
else:
    RT = fd.FunctionSpace(mesh, "RT", order)
    DG = fd.FunctionSpace(mesh, "DG", order - 1)

    # Others function space
    V = fd.VectorFunctionSpace(mesh, "DG", order - 1)
    T = fd.TensorFunctionSpace(mesh, "DG", order - 1)

W = RT * DG

# test and trial functions on the subspaces of the mixed function spaces as
# follows: ::
u, p = fd.TrialFunctions(W)
v, q = fd.TestFunctions(W)

# -----
# 3.2) material property

# perm lognormal
Kinv = fd.Function(T, name="Kinv")

k = np.random.randn(nx * n, ny * n) + par_a * np.random.randn(1)
kf = ndimage.gaussian_filter(k, sigma)
kl = np.exp(par_b + par_c * kf) * 1e-13  # m²

if verbose:
    fig, axes = plt.subplots(ncols=3)

    img0 = axes[0].imshow(k, interpolation='nearest', cmap='viridis')
    vDG = fd.VectorFunctionSpace(mesh, "DQ", 1)
else:
    DG1 = fd.FunctionSpace(mesh, "DG", order)
    vDG1 = fd.VectorFunctionSpace(mesh, "DG", order)
    Mh = fd.FunctionSpace(mesh, "HDiv Trace", order)

    DG0 = fd.FunctionSpace(mesh, "DG", 0)
    vDG = fd.VectorFunctionSpace(mesh, "DG", 1)

W = DG1 * vDG1 * Mh

# 3.1) Define trial and test functions
w = fd.Function(W)
w.assign(0.0)
ch, qh, lmbd_h = fd.split(w)
wh, vh, mu_h = fd.TestFunctions(W)

# 3.2) Set initial conditions
# ---- previous solution
# concentrations
c0 = fd.Function(DG1, name="c0")
c0.assign(0.0)

# ----------------------
# 3.4) Variational Form
# ----------------------
# coefficients
dt = np.sqrt(tol)
dtc = fd.Constant(dt)
n = fd.FacetNormal(mesh)
h = fd.sqrt(2) * fd.CellVolume(mesh) / fd.CellDiameter(mesh)