Ejemplo n.º 1
0
    def __init__(self, state, accretion=True, accumulation=True):
        super().__init__(state)

        # obtain our fields
        self.water_c = state.fields('cloud_liquid_mixing_ratio')
        self.rain = state.fields('rain_mixing_ratio')

        # declare function space
        Vt = self.water_c.function_space()

        # define some parameters as attributes
        dt = state.dt
        k_1 = Constant(0.001)  # accretion rate in 1/s
        k_2 = Constant(2.2)  # accumulation rate in 1/s
        a = Constant(0.001)  # min cloud conc in kg/kg
        b = Constant(0.875)  # power for rain in accumulation

        # make default rates to be zero
        accr_rate = Constant(0.0)
        accu_rate = Constant(0.0)

        if accretion:
            accr_rate = k_1 * (self.water_c - a)
        if accumulation:
            accu_rate = k_2 * self.water_c * self.rain ** b

        # make coalescence rate function, that needs to be the same for all updates in one time step
        coalesce_rate = Function(Vt)

        # adjust coalesce rate using min_value so negative cloud concentration doesn't occur
        self.lim_coalesce_rate = Interpolator(conditional(self.rain < 0.0,  # if rain is negative do only accretion
                                                          conditional(accr_rate < 0.0,
                                                                      0.0,
                                                                      min_value(accr_rate, self.water_c / dt)),
                                                          # don't turn rain back into cloud
                                                          conditional(accr_rate + accu_rate < 0.0,
                                                                      0.0,
                                                                      # if accretion rate is negative do only accumulation
                                                                      conditional(accr_rate < 0.0,
                                                                                  min_value(accu_rate, self.water_c / dt),
                                                                                  min_value(accr_rate + accu_rate, self.water_c / dt)))),
                                              coalesce_rate)

        # tell the prognostic fields what to update to
        self.water_c_new = Interpolator(self.water_c - dt * coalesce_rate, Vt)
        self.rain_new = Interpolator(self.rain + dt * coalesce_rate, Vt)
Ejemplo n.º 2
0
def PeriodicIntervalMesh(ncells, length, comm=COMM_WORLD):
    """Generate a periodic mesh of an interval.

    :arg ncells: The number of cells over the interval.
    :arg length: The length the interval.
    :kwarg comm: Optional communicator to build the mesh on (defaults to
        COMM_WORLD).
    """

    if ncells < 3:
        raise ValueError("1D periodic meshes with fewer than 3 \
cells are not currently supported")

    m = CircleManifoldMesh(ncells, comm=comm)
    coord_fs = VectorFunctionSpace(m, 'DG', 1, dim=1)
    old_coordinates = m.coordinates
    new_coordinates = Function(coord_fs)

    periodic_kernel = """double Y,pi;
            Y = 0.5*(old_coords[0][1]-old_coords[1][1]);
            pi=3.141592653589793;
            for(int i=0;i<2;i++){
            new_coords[i][0] = atan2(old_coords[i][1],old_coords[i][0])/pi/2;
            if(new_coords[i][0]<0.) new_coords[i][0] += 1;
            if(new_coords[i][0]==0 && Y<0.) new_coords[i][0] = 1.0;
            new_coords[i][0] *= L[0];
            }"""

    cL = Constant(length)

    par_loop(
        periodic_kernel, dx, {
            "new_coords": (new_coordinates, WRITE),
            "old_coords": (old_coordinates, READ),
            "L": (cL, READ)
        })

    return mesh.Mesh(new_coordinates)
Ejemplo n.º 3
0
    def __init__(self, *args, **kwargs):
        """
        Initialiser for TimeMixin.

        Add M/dt to a and L.
        """
        super().__init__(*args, **kwargs)

        self._add_function('T')
        self._add_function('a')

        self._add_function('T_')
        self._add_function('C')
        self._add_function('_delT')

        self.max_t = 1e-9
        self.dt = 1e-10
        self._dt_invc = Constant(0)
        self.steps = self.max_t / self.dt

        self.steady_state = True

        self.a += self._M()
Ejemplo n.º 4
0
    def setup(self, **kwargs):
        for name, field in kwargs.items():
            if name in self._fields.keys():
                self._fields[name].assign(field)
            else:
                self._fields[name] = utilities.copy(field)

        # Create homogeneous BCs for the Dirichlet part of the boundary
        u = self._fields.get('velocity', self._fields.get('u'))
        V = u.function_space()
        # NOTE: This will have to change when we do Stokes!
        bcs = firedrake.DirichletBC(V, Constant((0, 0)), self._dirichlet_ids)
        if not self._dirichlet_ids:
            bcs = None

        # Find the numeric IDs for the ice front
        boundary_ids = u.ufl_domain().exterior_facets.unique_markers
        ice_front_ids_comp = set(self._dirichlet_ids + self._side_wall_ids)
        ice_front_ids = list(set(boundary_ids) - ice_front_ids_comp)

        # Create the action and scale functionals
        _kwargs = {
            'side_wall_ids': self._side_wall_ids,
            'ice_front_ids': ice_front_ids
        }
        action = self._model.action(**self._fields, **_kwargs)
        scale = self._model.scale(**self._fields, **_kwargs)

        # Set up a minimization problem and solver
        quadrature_degree = self._model.quadrature_degree(**self._fields)
        params = {'quadrature_degree': quadrature_degree}
        problem = MinimizationProblem(action, scale, u, bcs, params)
        self._solver = NewtonSolver(problem,
                                    self._tolerance,
                                    solver_parameters=self._solver_parameters,
                                    max_iterations=self._max_iterations)
Ejemplo n.º 5
0
def test_vector_diffusion(tmpdir, DG, tracer_setup):

    setup = tracer_setup(tmpdir, geometry="slice", blob=True)
    state = setup.state
    f_init = setup.f_init
    tmax = setup.tmax
    tol = 3.e-2
    kappa = 1.

    f_end_expr = (1 / (1 + 4 * tmax)) * f_init**(1 / (1 + 4 * tmax))

    kappa = Constant([[kappa, 0.], [0., kappa]])
    if DG:
        V = VectorFunctionSpace(state.mesh, "DG", 1)
    else:
        V = state.spaces("HDiv", "CG", 1)
    f_init = as_vector([f_init, 0.])
    f_end_expr = as_vector([f_end_expr, 0.])

    mu = 5.

    diffusion_params = DiffusionParameters(kappa=kappa, mu=mu)
    eqn = DiffusionEquation(state,
                            V,
                            "f",
                            diffusion_parameters=diffusion_params)

    if DG:
        state.fields("f").interpolate(f_init)
    else:
        state.fields("f").project(f_init)

    diffusion_scheme = [(eqn, BackwardEuler(state))]

    f_end = run(state, diffusion_scheme, tmax)
    assert errornorm(f_end_expr, f_end) < tol
Ejemplo n.º 6
0
def setup_saturated(dirname, recovered):

    # set up grid and time stepping parameters
    dt = 1.
    tmax = 3.
    deltax = 400.
    L = 2000.
    H = 10000.

    nlayers = int(H / deltax)
    ncolumns = int(L / deltax)

    m = PeriodicIntervalMesh(ncolumns, L)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H / nlayers)

    # option to easily change between recovered and not if necessary
    # default should be to use lowest order set of spaces
    degree = 0 if recovered else 1

    output = OutputParameters(dirname=dirname + '/saturated_balance',
                              dumpfreq=1,
                              dumplist=['u'])
    parameters = CompressibleParameters()
    diagnostic_fields = [Theta_e()]

    state = State(mesh,
                  dt=dt,
                  output=output,
                  parameters=parameters,
                  diagnostic_fields=diagnostic_fields)

    tracers = [WaterVapour(), CloudWater()]

    if recovered:
        u_transport_option = "vector_advection_form"
    else:
        u_transport_option = "vector_invariant_form"
    eqns = CompressibleEulerEquations(state,
                                      "CG",
                                      degree,
                                      u_transport_option=u_transport_option,
                                      active_tracers=tracers)

    # Initial conditions
    u0 = state.fields("u")
    rho0 = state.fields("rho")
    theta0 = state.fields("theta")
    water_v0 = state.fields("vapour_mixing_ratio")
    water_c0 = state.fields("cloud_liquid_mixing_ratio")
    moisture = ['vapour_mixing_ratio', 'cloud_liquid_mixing_ratio']

    # spaces
    Vu = u0.function_space()
    Vt = theta0.function_space()
    Vr = rho0.function_space()

    # Isentropic background state
    Tsurf = Constant(300.)
    total_water = Constant(0.02)
    theta_e = Function(Vt).interpolate(Tsurf)
    water_t = Function(Vt).interpolate(total_water)

    # Calculate hydrostatic exner
    saturated_hydrostatic_balance(state, theta_e, water_t)
    water_c0.assign(water_t - water_v0)

    state.set_reference_profiles([('rho', rho0), ('theta', theta0)])

    # Set up transport schemes
    if recovered:
        VDG1 = state.spaces("DG1_equispaced")
        VCG1 = FunctionSpace(mesh, "CG", 1)
        Vt_brok = FunctionSpace(mesh, BrokenElement(Vt.ufl_element()))
        Vu_DG1 = VectorFunctionSpace(mesh, VDG1.ufl_element())
        Vu_CG1 = VectorFunctionSpace(mesh, "CG", 1)

        u_opts = RecoveredOptions(embedding_space=Vu_DG1,
                                  recovered_space=Vu_CG1,
                                  broken_space=Vu,
                                  boundary_method=Boundary_Method.dynamics)
        rho_opts = RecoveredOptions(embedding_space=VDG1,
                                    recovered_space=VCG1,
                                    broken_space=Vr,
                                    boundary_method=Boundary_Method.dynamics)
        theta_opts = RecoveredOptions(embedding_space=VDG1,
                                      recovered_space=VCG1,
                                      broken_space=Vt_brok)
        wv_opts = RecoveredOptions(embedding_space=VDG1,
                                   recovered_space=VCG1,
                                   broken_space=Vt_brok)
        wc_opts = RecoveredOptions(embedding_space=VDG1,
                                   recovered_space=VCG1,
                                   broken_space=Vt_brok)
    else:

        rho_opts = None
        theta_opts = EmbeddedDGOptions()
        wv_opts = EmbeddedDGOptions()
        wc_opts = EmbeddedDGOptions()

    transported_fields = [
        SSPRK3(state, 'rho', options=rho_opts),
        SSPRK3(state, 'theta', options=theta_opts),
        SSPRK3(state, 'vapour_mixing_ratio', options=wv_opts),
        SSPRK3(state, 'cloud_liquid_mixing_ratio', options=wc_opts)
    ]

    if recovered:
        transported_fields.append(SSPRK3(state, 'u', options=u_opts))
    else:
        transported_fields.append(ImplicitMidpoint(state, 'u'))

    linear_solver = CompressibleSolver(state, eqns, moisture=moisture)

    # add physics
    physics_list = [Condensation(state)]

    # build time stepper
    stepper = CrankNicolson(state,
                            eqns,
                            transported_fields,
                            linear_solver=linear_solver,
                            physics_list=physics_list)

    return stepper, tmax
Ejemplo n.º 7
0
def pml(mesh,
        scatterer_bdy_id,
        outer_bdy_id,
        wave_number,
        options_prefix=None,
        solver_parameters=None,
        inner_region=None,
        fspace=None,
        tfspace=None,
        true_sol_grad=None,
        pml_type=None,
        delta=None,
        quad_const=None,
        speed=None,
        pml_min=None,
        pml_max=None):
    """
        For unlisted arg descriptions, see run_method

        :arg inner_region: boundary id of non-pml region
        :arg pml_type: Type of pml function, either 'quadratic' or 'bdy_integral'
        :arg delta: For :arg:`pml_type` of 'bdy_integral', added to denominator
                    to prevent 1 / 0 at edge of boundary
        :arg quad_const: For :arg:`pml_type` of 'quadratic', a scaling constant
        :arg speed: Speed of sound
        :arg pml_min: A list, *pml_min[i]* is where to begin pml layer in direction
                      *i*
        :arg pml_max: A list, *pml_max[i]* is where to end pml layer in direction *i*
    """
    # Handle defauls
    if pml_type is None:
        pml_type = 'bdy_integral'
    if delta is None:
        delta = 1e-3
    if quad_const is None:
        quad_const = 1.0
    if speed is None:
        speed = 340.0

    pml_types = ['bdy_integral', 'quadratic']
    if pml_type not in pml_types:
        raise ValueError("PML type of %s is not one of %s" %
                         (pml_type, pml_types))

    xx = SpatialCoordinate(mesh)
    # {{{ create sigma functions for PML
    sigma = None
    if pml_type == 'bdy_integral':
        sigma = [
            Constant(speed) / (Constant(delta + extent) - abs(coord))
            for extent, coord in zip(pml_max, xx)
        ]
    elif pml_type == 'quadratic':
        sigma = [
            Constant(quad_const) * (abs(coord) - Constant(min_))**2
            for min_, coord in zip(pml_min, xx)
        ]
    r"""
        Here \kappa is the wave number and c is the speed

        ..math::

        \kappa = \frac{ \omega } { c }
    """
    omega = wave_number * speed

    # {{{ Set up PML functions
    gamma = [
        Constant(1.0) + conditional(
            abs(real(coord)) >= real(min_),
            Constant(1j / omega) * sigma_i, Constant(0.0))
        for min_, coord, sigma_i in zip(pml_min, xx, sigma)
    ]

    kappa = [None] * len(gamma)
    gamma_prod = 1.0
    for i in range(len(gamma)):
        gamma_prod *= gamma[i]
        tensor_i = [Constant(0.0) for _ in range(len(gamma))]
        tensor_i[i] = 1.0
        r"""
            *i*th entry is

            .. math::

            \frac{\prod_{j\neq i} \gamma_j}{ \gamma_i }
        """
        for j in range(len(gamma)):
            if j != i:
                tensor_i[i] *= gamma[j]
            else:
                tensor_i[i] /= gamma[j]
        kappa[i] = tensor_i

    kappa = as_tensor(kappa)

    # }}}

    p = TrialFunction(fspace)
    q = TestFunction(fspace)

    k = wave_number  # Just easier to look at
    a = (inner(dot(grad(p), kappa), grad(q)) -
         Constant(k**2) * gamma_prod * inner(p, q)) * dx

    n = FacetNormal(mesh)
    L = inner(dot(true_sol_grad, n), q) * ds(scatterer_bdy_id)

    bc = DirichletBC(fspace, Constant(0), outer_bdy_id)

    solution = Function(fspace)

    #solve(a == L, solution, bcs=[bc], options_prefix=options_prefix)
    # Create a solver and return the KSP object with the solution so that can get
    # PETSc information
    # Create problem
    problem = vs.LinearVariationalProblem(a, L, solution, [bc], None)
    # Create solver and call solve
    solver = vs.LinearVariationalSolver(problem,
                                        solver_parameters=solver_parameters,
                                        options_prefix=options_prefix)
    solver.solve()

    return solver.snes, solution
Ejemplo n.º 8
0
def nonlocal_integral_eq(
    mesh,
    scatterer_bdy_id,
    outer_bdy_id,
    wave_number,
    options_prefix=None,
    solver_parameters=None,
    fspace=None,
    vfspace=None,
    true_sol_grad_expr=None,
    actx=None,
    dgfspace=None,
    dgvfspace=None,
    meshmode_src_connection=None,
    qbx_kwargs=None,
):
    r"""
        see run_method for descriptions of unlisted args

        args:

        gamma and beta are used to precondition
        with the following equation:

        \Delta u - \kappa^2 \gamma u = 0
        (\partial_n - i\kappa\beta) u |_\Sigma = 0
    """
    # make sure we get outer bdy id as tuple in case it consists of multiple ids
    if isinstance(outer_bdy_id, int):
        outer_bdy_id = [outer_bdy_id]
    outer_bdy_id = tuple(outer_bdy_id)
    # away from the excluded region, but firedrake and meshmode point
    # into
    pyt_inner_normal_sign = -1

    ambient_dim = mesh.geometric_dimension()

    # {{{ Build src and tgt

    # build connection meshmode near src boundary -> src boundary inside meshmode
    from meshmode.discretization.poly_element import \
        InterpolatoryQuadratureSimplexGroupFactory
    from meshmode.discretization.connection import make_face_restriction
    factory = InterpolatoryQuadratureSimplexGroupFactory(
        dgfspace.finat_element.degree)
    src_bdy_connection = make_face_restriction(actx,
                                               meshmode_src_connection.discr,
                                               factory, scatterer_bdy_id)
    # source is a qbx layer potential
    from pytential.qbx import QBXLayerPotentialSource
    disable_refinement = (fspace.mesh().geometric_dimension() == 3)
    qbx = QBXLayerPotentialSource(src_bdy_connection.to_discr,
                                  **qbx_kwargs,
                                  _disable_refinement=disable_refinement)

    # get target indices and point-set
    target_indices, target = get_target_points_and_indices(
        fspace, outer_bdy_id)

    # }}}

    # build the operations
    from pytential import bind, sym
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * sym.grad(
        ambient_dim,
        sym.D(HelmholtzKernel(ambient_dim),
              sym.var("u"),
              k=sym.var("k"),
              qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
    """
    op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D(
        HelmholtzKernel(ambient_dim),
        sym.var("u"),
        k=sym.var("k"),
        qbx_forced_limit=None))

    # bind the operations
    pyt_grad_op = bind((qbx, target), grad_op)
    pyt_op = bind((qbx, target), op)

    # }}}

    class MatrixFreeB(object):
        def __init__(self, A, pyt_grad_op, pyt_op, actx, kappa):
            """
            :arg kappa: The wave number
            """

            self.actx = actx
            self.k = kappa
            self.pyt_op = pyt_op
            self.pyt_grad_op = pyt_grad_op
            self.A = A
            self.meshmode_src_connection = meshmode_src_connection

            # {{{ Create some functions needed for multing
            self.x_fntn = Function(fspace)

            # CG
            self.potential_int = Function(fspace)
            self.potential_int.dat.data[:] = 0.0
            self.grad_potential_int = Function(vfspace)
            self.grad_potential_int.dat.data[:] = 0.0
            self.pyt_result = Function(fspace)

            self.n = FacetNormal(mesh)
            self.v = TestFunction(fspace)

            # some meshmode ones
            self.x_mm_fntn = self.meshmode_src_connection.discr.empty(
                self.actx, dtype='c')

            # }}}

        def mult(self, mat, x, y):
            # Copy function data into the fivredrake function
            self.x_fntn.dat.data[:] = x[:]
            # Transfer the function to meshmode
            self.meshmode_src_connection.from_firedrake(project(
                self.x_fntn, dgfspace),
                                                        out=self.x_mm_fntn)
            # Restrict to boundary
            x_mm_fntn_on_bdy = src_bdy_connection(self.x_mm_fntn)

            # Apply the operation
            potential_int_mm = self.pyt_op(self.actx,
                                           u=x_mm_fntn_on_bdy,
                                           k=self.k)
            grad_potential_int_mm = self.pyt_grad_op(self.actx,
                                                     u=x_mm_fntn_on_bdy,
                                                     k=self.k)
            # Store in firedrake
            self.potential_int.dat.data[target_indices] = potential_int_mm.get(
            )
            for dim in range(grad_potential_int_mm.shape[0]):
                self.grad_potential_int.dat.data[
                    target_indices, dim] = grad_potential_int_mm[dim].get()

            # Integrate the potential
            r"""
            Compute the inner products using firedrake. Note this
            will be subtracted later, hence appears off by a sign.

            .. math::

                \langle
                    n(x) \cdot \nabla(
                        \int_\Gamma(
                            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                        )d\gamma(y)
                    ), v
                \rangle_\Sigma
                - \langle
                    i \kappa \cdot
                    \int_\Gamma(
                        u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                    )d\gamma(y), v
                \rangle_\Sigma
            """
            self.pyt_result = assemble(
                inner(inner(self.grad_potential_int, self.n), self.v) *
                ds(outer_bdy_id) -
                inner(self.potential_int, self.v) * ds(outer_bdy_id))

            # y <- Ax - evaluated potential
            self.A.mult(x, y)
            with self.pyt_result.dat.vec_ro as ep:
                y.axpy(-1, ep)

    # {{{ Compute normal helmholtz operator
    u = TrialFunction(fspace)
    v = TestFunction(fspace)
    r"""
    .. math::

        \langle
            \nabla u, \nabla v
        \rangle
        - \kappa^2 \cdot \langle
            u, v
        \rangle
        - i \kappa \langle
            u, v
        \rangle_\Sigma
    """
    a = inner(grad(u), grad(v)) * dx \
        - Constant(wave_number**2) * inner(u, v) * dx \
        - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id)

    # get the concrete matrix from a general bilinear form
    A = assemble(a).M.handle
    # }}}

    # {{{ Setup Python matrix
    B = PETSc.Mat().create()

    # build matrix context
    Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, actx, wave_number)

    # set up B as same size as A
    B.setSizes(*A.getSizes())

    B.setType(B.Type.PYTHON)
    B.setPythonContext(Bctx)
    B.setUp()
    # }}}

    # {{{ Create rhs

    # Remember f is \partial_n(true_sol)|_\Gamma
    # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y)

    sigma = sym.make_sym_vector("sigma", ambient_dim)
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * \
        sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim),
                                    sym.n_dot(sigma),
                                    k=sym.var("k"), qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            f(y) H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
        )
    """
    op = 1j * sym.var("k") * pyt_inner_normal_sign * \
        sym.S(HelmholtzKernel(ambient_dim),
              sym.n_dot(sigma),
              k=sym.var("k"),
              qbx_forced_limit=None)

    rhs_grad_op = bind((qbx, target), grad_op)
    rhs_op = bind((qbx, target), op)

    # Transfer to meshmode
    metadata = {'quadrature_degree': 2 * fspace.ufl_element().degree()}
    dg_true_sol_grad = project(true_sol_grad_expr,
                               dgvfspace,
                               form_compiler_parameters=metadata)
    true_sol_grad_mm = meshmode_src_connection.from_firedrake(dg_true_sol_grad,
                                                              actx=actx)
    true_sol_grad_mm = src_bdy_connection(true_sol_grad_mm)
    # Apply the operations
    f_grad_convoluted_mm = rhs_grad_op(actx,
                                       sigma=true_sol_grad_mm,
                                       k=wave_number)
    f_convoluted_mm = rhs_op(actx, sigma=true_sol_grad_mm, k=wave_number)
    # Transfer function back to firedrake
    f_grad_convoluted = Function(vfspace)
    f_convoluted = Function(fspace)
    f_grad_convoluted.dat.data[:] = 0.0
    f_convoluted.dat.data[:] = 0.0

    for dim in range(f_grad_convoluted_mm.shape[0]):
        f_grad_convoluted.dat.data[target_indices,
                                   dim] = f_grad_convoluted_mm[dim].get()
    f_convoluted.dat.data[target_indices] = f_convoluted_mm.get()
    r"""
        \langle
            f, v
        \rangle_\Gamma
        + \langle
            i \kappa \cdot \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y), v
        \rangle_\Sigma
        - \langle
            n(x) \cdot \nabla(
                \int_\Gamma(
                    f(y) H_0^{(1)}(\kappa |x - y|)
                )d\gamma(y)
            ), v
        \rangle_\Sigma
    """
    rhs_form = inner(inner(true_sol_grad_expr, FacetNormal(mesh)),
                     v) * ds(scatterer_bdy_id, metadata=metadata) \
        + inner(f_convoluted, v) * ds(outer_bdy_id) \
        - inner(inner(f_grad_convoluted, FacetNormal(mesh)),
                v) * ds(outer_bdy_id)

    rhs = assemble(rhs_form)

    # {{{ set up a solver:
    solution = Function(fspace, name="Computed Solution")

    #       {{{ Used for preconditioning
    if 'gamma' in solver_parameters or 'beta' in solver_parameters:
        gamma = complex(solver_parameters.pop('gamma', 1.0))

        import cmath
        beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma)))

        p = inner(grad(u), grad(v)) * dx \
            - Constant(wave_number**2 * gamma) * inner(u, v) * dx \
            - Constant(1j * wave_number * beta) * inner(u, v) * ds(outer_bdy_id)
        P = assemble(p).M.handle

    else:
        P = A
    #       }}}

    # Set up options to contain solver parameters:
    ksp = PETSc.KSP().create()
    if solver_parameters['pc_type'] == 'pyamg':
        del solver_parameters['pc_type']  # We are using the AMG preconditioner

        pyamg_tol = solver_parameters.get('pyamg_tol', None)
        if pyamg_tol is not None:
            pyamg_tol = float(pyamg_tol)
        pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None)
        if pyamg_maxiter is not None:
            pyamg_maxiter = int(pyamg_maxiter)
        ksp.setOperators(B)
        ksp.setUp()
        pc = ksp.pc
        pc.setType(pc.Type.PYTHON)
        pc.setPythonContext(
            AMGTransmissionPreconditioner(wave_number,
                                          fspace,
                                          A,
                                          tol=pyamg_tol,
                                          maxiter=pyamg_maxiter,
                                          use_plane_waves=True))
    # Otherwise use regular preconditioner
    else:
        ksp.setOperators(B, P)

    options_manager = OptionsManager(solver_parameters, options_prefix)
    options_manager.set_from_options(ksp)

    import petsc4py.PETSc
    petsc4py.PETSc.Sys.popErrorHandler()
    with rhs.dat.vec_ro as b:
        with solution.dat.vec as x:
            ksp.solve(b, x)
    # }}}

    return ksp, solution
Ejemplo n.º 9
0
def heat(butcher_tableau):
    N = 4
    dt = Constant(1.0 / N)
    t = Constant(0.0)

    msh = UnitSquareMesh(N, N)
    deg = 2
    V = FunctionSpace(msh, "CG", deg)

    x, y = SpatialCoordinate(msh)

    uexact = t * (x + y)
    rhs = expand_derivatives(diff(uexact, t)) - div(grad(uexact))

    sols = []

    luparams = {
        "mat_type": "aij",
        "snes_type": "ksponly",
        "ksp_type": "preonly",
        "pc_type": "lu"
    }

    ranaLD = {
        "mat_type": "aij",
        "snes_type": "ksponly",
        "ksp_type": "gmres",
        "ksp_monitor": None,
        "pc_type": "python",
        "pc_python_type": "irksome.RanaLD",
        "aux": {
            "pc_type": "fieldsplit",
            "pc_fieldsplit_type": "multiplicative"
        }
    }
    per_field = {"ksp_type": "preonly", "pc_type": "gamg"}

    for s in range(butcher_tableau.num_stages):
        ranaLD["fieldsplit_%s" % (s, )] = per_field

    ranaDU = {
        "mat_type": "aij",
        "snes_type": "ksponly",
        "ksp_type": "gmres",
        "ksp_monitor": None,
        "pc_type": "python",
        "pc_python_type": "irksome.RanaDU",
        "aux": {
            "pc_type": "fieldsplit",
            "pc_fieldsplit_type": "multiplicative"
        }
    }

    for s in range(butcher_tableau.num_stages):
        ranaLD["fieldsplit_%s" % (s, )] = per_field

    params = [luparams, ranaLD, ranaDU]

    for solver_parameters in params:
        F, u, bc = Fubc(V, uexact, rhs)

        stepper = TimeStepper(F,
                              butcher_tableau,
                              t,
                              dt,
                              u,
                              bcs=bc,
                              solver_parameters=solver_parameters)
        stepper.advance()
        sols.append(u)

    errs = [errornorm(sols[0], uu) for uu in sols[1:]]
    return numpy.max(errs)
Ejemplo n.º 10
0
    def initialize(self, pc):
        """Set up the problem context. Take the original
        mixed problem and reformulate the problem as a
        hybridized mixed system.

        A KSP is created for the Lagrange multiplier system.
        """
        from firedrake import (FunctionSpace, Function, Constant,
                               TrialFunction, TrialFunctions, TestFunction,
                               DirichletBC)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import split_form
        from ufl.algorithms.replace import replace

        # Extract the problem context
        prefix = pc.getOptionsPrefix() + "hybridization_"
        _, P = pc.getOperators()
        self.ctx = P.getPythonContext()

        if not isinstance(self.ctx, ImplicitMatrixContext):
            raise ValueError(
                "The python context must be an ImplicitMatrixContext")

        test, trial = self.ctx.a.arguments()

        V = test.function_space()
        mesh = V.mesh()

        if len(V) != 2:
            raise ValueError("Expecting two function spaces.")

        if all(Vi.ufl_element().value_shape() for Vi in V):
            raise ValueError("Expecting an H(div) x L2 pair of spaces.")

        # Automagically determine which spaces are vector and scalar
        for i, Vi in enumerate(V):
            if Vi.ufl_element().sobolev_space().name == "HDiv":
                self.vidx = i
            else:
                assert Vi.ufl_element().sobolev_space().name == "L2"
                self.pidx = i

        # Create the space of approximate traces.
        W = V[self.vidx]
        if W.ufl_element().family() == "Brezzi-Douglas-Marini":
            tdegree = W.ufl_element().degree()

        else:
            try:
                # If we have a tensor product element
                h_deg, v_deg = W.ufl_element().degree()
                tdegree = (h_deg - 1, v_deg - 1)

            except TypeError:
                tdegree = W.ufl_element().degree() - 1

        TraceSpace = FunctionSpace(mesh, "HDiv Trace", tdegree)

        # Break the function spaces and define fully discontinuous spaces
        broken_elements = ufl.MixedElement(
            [ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
        V_d = FunctionSpace(mesh, broken_elements)

        # Set up the functions for the original, hybridized
        # and schur complement systems
        self.broken_solution = Function(V_d)
        self.broken_residual = Function(V_d)
        self.trace_solution = Function(TraceSpace)
        self.unbroken_solution = Function(V)
        self.unbroken_residual = Function(V)

        shapes = (V[self.vidx].finat_element.space_dimension(),
                  np.prod(V[self.vidx].shape))
        domain = "{[i,j]: 0 <= i < %d and 0 <= j < %d}" % shapes
        instructions = """
        for i, j
            w[i,j] = w[i,j] + 1
        end
        """
        self.weight = Function(V[self.vidx])
        par_loop((domain, instructions),
                 ufl.dx, {"w": (self.weight, INC)},
                 is_loopy_kernel=True)

        instructions = """
        for i, j
            vec_out[i,j] = vec_out[i,j] + vec_in[i,j]/w[i,j]
        end
        """
        self.average_kernel = (domain, instructions)

        # Create the symbolic Schur-reduction:
        # Original mixed operator replaced with "broken"
        # arguments
        arg_map = {test: TestFunction(V_d), trial: TrialFunction(V_d)}
        Atilde = Tensor(replace(self.ctx.a, arg_map))
        gammar = TestFunction(TraceSpace)
        n = ufl.FacetNormal(mesh)
        sigma = TrialFunctions(V_d)[self.vidx]

        if mesh.cell_set._extruded:
            Kform = (gammar('+') * ufl.jump(sigma, n=n) * ufl.dS_h +
                     gammar('+') * ufl.jump(sigma, n=n) * ufl.dS_v)
        else:
            Kform = (gammar('+') * ufl.jump(sigma, n=n) * ufl.dS)

        # Here we deal with boundaries. If there are Neumann
        # conditions (which should be enforced strongly for
        # H(div)xL^2) then we need to add jump terms on the exterior
        # facets. If there are Dirichlet conditions (which should be
        # enforced weakly) then we need to zero out the trace
        # variables there as they are not active (otherwise the hybrid
        # problem is not well-posed).

        # If boundary conditions are contained in the ImplicitMatrixContext:
        if self.ctx.row_bcs:
            # Find all the subdomains with neumann BCS
            # These are Dirichlet BCs on the vidx space
            neumann_subdomains = set()
            for bc in self.ctx.row_bcs:
                if bc.function_space().index == self.pidx:
                    raise NotImplementedError(
                        "Dirichlet conditions for scalar variable not supported. Use a weak bc"
                    )
                if bc.function_space().index != self.vidx:
                    raise NotImplementedError(
                        "Dirichlet bc set on unsupported space.")
                # append the set of sub domains
                subdom = bc.sub_domain
                if isinstance(subdom, str):
                    neumann_subdomains |= set([subdom])
                else:
                    neumann_subdomains |= set(
                        as_tuple(subdom, numbers.Integral))

            # separate out the top and bottom bcs
            extruded_neumann_subdomains = neumann_subdomains & {
                "top", "bottom"
            }
            neumann_subdomains = neumann_subdomains - extruded_neumann_subdomains

            integrand = gammar * ufl.dot(sigma, n)
            measures = []
            trace_subdomains = []
            if mesh.cell_set._extruded:
                ds = ufl.ds_v
                for subdomain in sorted(extruded_neumann_subdomains):
                    measures.append({
                        "top": ufl.ds_t,
                        "bottom": ufl.ds_b
                    }[subdomain])
                trace_subdomains.extend(
                    sorted({"top", "bottom"} - extruded_neumann_subdomains))
            else:
                ds = ufl.ds
            if "on_boundary" in neumann_subdomains:
                measures.append(ds)
            else:
                measures.extend((ds(sd) for sd in sorted(neumann_subdomains)))
                markers = [int(x) for x in mesh.exterior_facets.unique_markers]
                dirichlet_subdomains = set(markers) - neumann_subdomains
                trace_subdomains.extend(sorted(dirichlet_subdomains))

            for measure in measures:
                Kform += integrand * measure

            trace_bcs = [
                DirichletBC(TraceSpace, Constant(0.0), subdomain)
                for subdomain in trace_subdomains
            ]

        else:
            # No bcs were provided, we assume weak Dirichlet conditions.
            # We zero out the contribution of the trace variables on
            # the exterior boundary. Extruded cells will have both
            # horizontal and vertical facets
            trace_subdomains = ["on_boundary"]
            if mesh.cell_set._extruded:
                trace_subdomains.extend(["bottom", "top"])
            trace_bcs = [
                DirichletBC(TraceSpace, Constant(0.0), subdomain)
                for subdomain in trace_subdomains
            ]

        # Make a SLATE tensor from Kform
        K = Tensor(Kform)

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(TraceSpace)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * AssembledVector(self.broken_residual),
            tensor=self.schur_rhs,
            form_compiler_parameters=self.ctx.fc_params)

        mat_type = PETSc.Options().getString(prefix + "mat_type", "aij")

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp,
                                 bcs=trace_bcs,
                                 form_compiler_parameters=self.ctx.fc_params,
                                 mat_type=mat_type,
                                 options_prefix=prefix)
        self._assemble_S = create_assembly_callable(
            schur_comp,
            tensor=self.S,
            bcs=trace_bcs,
            form_compiler_parameters=self.ctx.fc_params,
            mat_type=mat_type)

        with timed_region("HybridOperatorAssembly"):
            self._assemble_S()

        Smat = self.S.petscmat

        nullspace = self.ctx.appctx.get("trace_nullspace", None)
        if nullspace is not None:
            nsp = nullspace(TraceSpace)
            Smat.setNullSpace(nsp.nullspace(comm=pc.comm))

        # Set up the KSP for the system of Lagrange multipliers
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        split_mixed_op = dict(split_form(Atilde.form))
        split_trace_op = dict(split_form(K.form))

        # Generate reconstruction calls
        self._reconstruction_calls(split_mixed_op, split_trace_op)
def setup_balance(dirname):

    # set up grid and time stepping parameters
    dt = 1.
    tmax = 5.
    deltax = 400
    L = 2000.
    H = 10000.

    nlayers = int(H / deltax)
    ncolumns = int(L / deltax)

    m = PeriodicIntervalMesh(ncolumns, L)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H / nlayers)

    fieldlist = ['u', 'rho', 'theta']
    timestepping = TimesteppingParameters(dt=dt, maxk=4, maxi=1)
    output = OutputParameters(dirname=dirname + '/dry_balance',
                              dumpfreq=10,
                              dumplist=['u'])
    parameters = CompressibleParameters()
    diagnostics = Diagnostics(*fieldlist)
    diagnostic_fields = []

    state = State(mesh,
                  vertical_degree=1,
                  horizontal_degree=1,
                  family="CG",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  diagnostics=diagnostics,
                  fieldlist=fieldlist,
                  diagnostic_fields=diagnostic_fields)

    # Initial conditions
    u0 = state.fields("u")
    rho0 = state.fields("rho")
    theta0 = state.fields("theta")

    # spaces
    Vu = u0.function_space()
    Vt = theta0.function_space()
    Vr = rho0.function_space()

    # Isentropic background state
    Tsurf = Constant(300.)
    theta0.interpolate(Tsurf)

    # Calculate hydrostatic Pi
    compressible_hydrostatic_balance(state, theta0, rho0, solve_for_rho=True)

    state.initialise([('u', u0), ('rho', rho0), ('theta', theta0)])
    state.set_reference_profiles([('rho', rho0), ('theta', theta0)])

    # Set up advection schemes
    ueqn = VectorInvariant(state, Vu)
    rhoeqn = AdvectionEquation(state, Vr, equation_form="continuity")
    thetaeqn = EmbeddedDGAdvection(state,
                                   Vt,
                                   equation_form="advective",
                                   options=EmbeddedDGOptions())

    advected_fields = [("u", ThetaMethod(state, u0, ueqn)),
                       ("rho", SSPRK3(state, rho0, rhoeqn)),
                       ("theta", SSPRK3(state, theta0, thetaeqn))]

    # Set up linear solver
    linear_solver = CompressibleSolver(state)

    # Set up forcing
    compressible_forcing = CompressibleForcing(state)

    # build time stepper
    stepper = CrankNicolson(state, advected_fields, linear_solver,
                            compressible_forcing)

    return stepper, tmax
Ejemplo n.º 12
0
def streamplot(function,
               resolution=None,
               min_length=None,
               max_time=None,
               start_width=0.5,
               end_width=1.5,
               tolerance=3e-3,
               loc_tolerance=1e-10,
               seed=None,
               complex_component="real",
               **kwargs):
    r"""Create a streamline plot of a vector field

    Similar to matplotlib :func:`streamplot <matplotlib.pyplot.streamplot>`

    :arg function: the Firedrake :class:`~.Function` to plot
    :arg resolution: minimum spacing between streamlines (defaults to domain size / 20)
    :arg min_length: minimum length of a streamline (defaults to 4x resolution)
    :arg max_time: maximum time to integrate a streamline
    :arg start_width: line width at beginning of streamline
    :arg end_width: line width at end of streamline, to convey direction
    :arg tolerance: dimensionless tolerance for adaptive ODE integration
    :arg loc_tolerance: point location tolerance for :meth:`~firedrake.functions.Function.at`
    :kwarg complex_component: If plotting complex data, which
        component? (``'real'`` or ``'imag'``). Default is ``'real'``.
    :kwarg kwargs: same as for matplotlib :class:`~matplotlib.collections.LineCollection`
    """
    import randomgen

    if function.ufl_shape != (2, ):
        raise ValueError("Streamplot only defined for 2D vector fields!")

    axes = kwargs.pop("axes", None)
    if axes is None:
        figure = plt.figure()
        axes = figure.add_subplot(111)

    mesh = function.ufl_domain()
    if resolution is None:
        coords = toreal(mesh.coordinates.dat.data_ro, "real")
        resolution = (coords.max(axis=0) - coords.min(axis=0)).max() / 20

    if min_length is None:
        min_length = 4 * resolution

    if max_time is None:
        area = assemble(Constant(1) * dx(mesh))
        average_speed = np.sqrt(
            assemble(inner(function, function) * dx) / area)
        max_time = 50 * min_length / average_speed

    streamplotter = Streamplotter(function,
                                  resolution,
                                  min_length,
                                  max_time,
                                  tolerance,
                                  loc_tolerance,
                                  complex_component=complex_component)

    # TODO: better way of seeding start points
    shape = streamplotter._grid.shape
    xmin = streamplotter._grid_point((0, 0))
    xmax = streamplotter._grid_point((shape[0] - 2, shape[1] - 2))
    X, Y = np.meshgrid(np.linspace(xmin[0], xmax[0], shape[0] - 2),
                       np.linspace(xmin[1], xmax[1], shape[1] - 2))
    start_points = np.vstack((X.ravel(), Y.ravel())).T

    # Randomly shuffle the start points
    generator = randomgen.MT19937(seed).generator
    for x in generator.permutation(np.array(start_points)):
        streamplotter.add_streamline(x)

    # Colors are determined by the speed, thicknesses by arc length
    speeds = []
    widths = []
    for streamline in streamplotter.streamlines:
        velocity = toreal(
            np.array(function.at(streamline, tolerance=loc_tolerance)),
            complex_component)
        speed = np.sqrt(np.sum(velocity**2, axis=1))
        speeds.extend(speed[:-1])

        delta = np.sqrt(np.sum(np.diff(streamline, axis=0)**2, axis=1))
        arc_length = np.cumsum(delta)
        length = arc_length[-1]
        s = arc_length / length
        linewidth = (1 - s) * start_width + s * end_width
        widths.extend(linewidth)

    points = []
    for streamline in streamplotter.streamlines:
        pts = streamline.reshape(-1, 1, 2)
        points.extend(np.hstack((pts[:-1], pts[1:])))

    speeds = np.array(speeds)
    widths = np.array(widths)

    points = np.asarray(points)
    vmin = kwargs.pop("vmin", speeds.min())
    vmax = kwargs.pop("vmax", speeds.max())
    norm = kwargs.pop("norm", matplotlib.colors.Normalize(vmin=vmin,
                                                          vmax=vmax))
    cmap = plt.get_cmap(kwargs.pop("cmap", None))

    collection = LineCollection(points, cmap=cmap, norm=norm, linewidth=widths)
    collection.set_array(speeds)
    axes.add_collection(collection)

    _autoscale_view(axes, function.ufl_domain().coordinates.dat.data_ro)
    return collection
Ejemplo n.º 13
0
    def initialize(self, pc):
        from firedrake import TrialFunction, TestFunction, dx, \
            assemble, inner, grad, split, Constant, parameters
        from firedrake.assemble import allocate_matrix, create_assembly_callable
        if pc.getType() != "python":
            raise ValueError("Expecting PC type python")
        prefix = pc.getOptionsPrefix() + "pcd_"

        # we assume P has things stuffed inside of it
        _, P = pc.getOperators()
        context = P.getPythonContext()

        test, trial = context.a.arguments()
        if test.function_space() != trial.function_space():
            raise ValueError("Pressure space test and trial space differ")

        Q = test.function_space()

        p = TrialFunction(Q)
        q = TestFunction(Q)

        mass = p * q * dx

        # Regularisation to avoid having to think about nullspaces.
        stiffness = inner(grad(p), grad(q)) * dx + Constant(1e-6) * p * q * dx

        opts = PETSc.Options()
        # we're inverting Mp and Kp, so default them to assembled.
        # Fp only needs its action, so default it to mat-free.
        # These can of course be overridden.
        # only Fp is referred to in update, so that's the only
        # one we stash.
        default = parameters["default_matrix_type"]
        Mp_mat_type = opts.getString(prefix + "Mp_mat_type", default)
        Kp_mat_type = opts.getString(prefix + "Kp_mat_type", default)
        self.Fp_mat_type = opts.getString(prefix + "Fp_mat_type", "matfree")

        Mp = assemble(mass,
                      form_compiler_parameters=context.fc_params,
                      mat_type=Mp_mat_type,
                      options_prefix=prefix + "Mp_")
        Kp = assemble(stiffness,
                      form_compiler_parameters=context.fc_params,
                      mat_type=Kp_mat_type,
                      options_prefix=prefix + "Kp_")

        Mp.force_evaluation()
        Kp.force_evaluation()

        # FIXME: Should we transfer nullspaces over.  I think not.

        Mksp = PETSc.KSP().create(comm=pc.comm)
        Mksp.incrementTabLevel(1, parent=pc)
        Mksp.setOptionsPrefix(prefix + "Mp_")
        Mksp.setOperators(Mp.petscmat)
        Mksp.setUp()
        Mksp.setFromOptions()
        self.Mksp = Mksp

        Kksp = PETSc.KSP().create(comm=pc.comm)
        Kksp.incrementTabLevel(1, parent=pc)
        Kksp.setOptionsPrefix(prefix + "Kp_")
        Kksp.setOperators(Kp.petscmat)
        Kksp.setUp()
        Kksp.setFromOptions()
        self.Kksp = Kksp

        state = context.appctx["state"]

        Re = context.appctx.get("Re", 1.0)

        velid = context.appctx["velocity_space"]

        u0 = split(state)[velid]
        fp = 1.0 / Re * inner(grad(p), grad(q)) * dx + inner(u0,
                                                             grad(p)) * q * dx

        self.Re = Re
        self.Fp = allocate_matrix(fp,
                                  form_compiler_parameters=context.fc_params,
                                  mat_type=self.Fp_mat_type,
                                  options_prefix=prefix + "Fp_")
        self._assemble_Fp = create_assembly_callable(
            fp,
            tensor=self.Fp,
            form_compiler_parameters=context.fc_params,
            mat_type=self.Fp_mat_type)
        self._assemble_Fp()
        self.Fp.force_evaluation()
        Fpmat = self.Fp.petscmat
        self.workspace = [Fpmat.createVecLeft() for i in (0, 1)]
Ejemplo n.º 14
0
        else:
            # Fixed quadrature with 64 points gives absolute errors below 1e-13
            # for a quantity of order 1e-3.
            v, _ = integrate.fixed_quad(D_integrand, theta_0, angles[ii], n=64)
            val[ii] = -(R / g) * v
    return val


# Get coordinates to pass to Dval function
D0 = Function(W0)
W = VectorFunctionSpace(mesh, D0.ufl_element())
X = interpolate(mesh.coordinates, W)
D0.dat.data[:] = Dval(X.dat.data_ro)

# Adjust mean value of initial D
C = Function(D0.function_space()).assign(Constant(1.0))
area = assemble(C * dx)
Dmean = assemble(D0 * dx) / area
D0 -= Dmean
D0 += Constant(D_mean)

# Dpert
D_p = Function(W0)
Dpert = D_bump * cos(theta) * exp(-(lamda / a)**2) * exp(-(
    (theta_2 - theta) / b)**2)
D_p.interpolate(Dpert)
# Dexpr = Dbar + Dpert
Dexpr = D0 + D_p
bexpr = Constant(0)

# Set up functions
Ejemplo n.º 15
0
def build_initial_conditions(prognostic_variables, simulation_parameters):
    """
    Initialises the prognostic variables based on the
    initial condition string.

    :arg prognostic_variables: a PrognosticVariables object.
    :arg simulation_parameters: a dictionary containing the simulation parameters.
    """

    mesh = simulation_parameters['mesh'][-1]
    ic = simulation_parameters['ic'][-1]
    alphasq = simulation_parameters['alphasq'][-1]
    c0 = simulation_parameters['c0'][-1]
    gamma = simulation_parameters['gamma'][-1]
    x, = SpatialCoordinate(mesh)
    Ld = simulation_parameters['Ld'][-1]
    deltax = Ld / simulation_parameters['resolution'][-1]
    w = simulation_parameters['peak_width'][-1]
    epsilon = 1

    ic_dict = {
        'two_peaks':
        (0.2 * 2 /
         (exp(x - 403. / 15. * 40. / Ld) + exp(-x + 403. / 15. * 40. / Ld)) +
         0.5 * 2 /
         (exp(x - 203. / 15. * 40. / Ld) + exp(-x + 203. / 15. * 40. / Ld))),
        'gaussian':
        0.5 * exp(-((x - 10.) / 2.)**2),
        'gaussian_narrow':
        0.5 * exp(-((x - 10.) / 1.)**2),
        'gaussian_wide':
        0.5 * exp(-((x - 10.) / 3.)**2),
        'peakon':
        conditional(x < Ld / 2., exp((x - Ld / 2) / sqrt(alphasq)),
                    exp(-(x - Ld / 2) / sqrt(alphasq))),
        'one_peak':
        0.5 * 2 /
        (exp(x - 203. / 15. * 40. / Ld) + exp(-x + 203. / 15. * 40. / Ld)),
        'proper_peak':
        0.5 * 2 / (exp(x - Ld / 4) + exp(-x + Ld / 4)),
        'new_peak':
        0.5 * 2 / (exp((x - Ld / 4) / w) + exp((-x + Ld / 4) / w)),
        'flat':
        Constant(2 * pi**2 / (9 * 40**2)),
        'fast_flat':
        Constant(0.1),
        'coshes':
        Constant(2000) * cosh((2000**0.5 / 2) * (x - 0.75))**(-2) +
        Constant(1000) * cosh(1000**0.5 / 2 * (x - 0.25))**(-2),
        'd_peakon':
        exp(-sqrt((x - Ld / 2)**2 + epsilon * deltax**2) / sqrt(alphasq)),
        'zero':
        Constant(0.0),
        'two_peakons':
        conditional(
            x < Ld / 4,
            exp((x - Ld / 4) / sqrt(alphasq)) -
            exp(-(x + Ld / 4) / sqrt(alphasq)),
            conditional(
                x < 3 * Ld / 4,
                exp(-(x - Ld / 4) / sqrt(alphasq)) - exp(
                    (x - 3 * Ld / 4) / sqrt(alphasq)),
                exp((x - 5 * Ld / 4) / sqrt(alphasq)) -
                exp(-(x - 3 * Ld / 4) / sqrt(alphasq)))),
        'twin_peakons':
        conditional(
            x < Ld / 4,
            exp((x - Ld / 4) / sqrt(alphasq)) + 0.5 * exp(
                (x - Ld / 2) / sqrt(alphasq)),
            conditional(
                x < Ld / 2,
                exp(-(x - Ld / 4) / sqrt(alphasq)) + 0.5 * exp(
                    (x - Ld / 2) / sqrt(alphasq)),
                conditional(
                    x < 3 * Ld / 4,
                    exp(-(x - Ld / 4) / sqrt(alphasq)) +
                    0.5 * exp(-(x - Ld / 2) / sqrt(alphasq)),
                    exp((x - 5 * Ld / 4) / sqrt(alphasq)) +
                    0.5 * exp(-(x - Ld / 2) / sqrt(alphasq))))),
        'periodic_peakon': (conditional(
            x < Ld / 2, 0.5 / (1 - exp(-Ld / sqrt(alphasq))) *
            (exp((x - Ld / 2) / sqrt(alphasq)) +
             exp(-Ld / sqrt(alphasq)) * exp(-(x - Ld / 2) / sqrt(alphasq))),
            0.5 / (1 - exp(-Ld / sqrt(alphasq))) *
            (exp(-(x - Ld / 2) / sqrt(alphasq)) +
             exp(-Ld / sqrt(alphasq)) * exp((x - Ld / 2) / sqrt(alphasq))))),
        'cos_bell':
        conditional(x < Ld / 4, (cos(pi * (x - Ld / 8) / (2 * Ld / 8)))**2,
                    0.0),
        'antisymmetric':
        1 / (exp((x - Ld / 4) / Ld) + exp((-x + Ld / 4) / Ld)) - 1 / (exp(
            (Ld - x - Ld / 4) / Ld) + exp((Ld + x + Ld / 4) / Ld))
    }

    ic_expr = ic_dict[ic]

    if prognostic_variables.scheme in ['upwind', 'LASCH']:

        VCG5 = FunctionSpace(mesh, "CG", 5)
        smooth_condition = Function(VCG5).interpolate(ic_expr)
        prognostic_variables.u.project(as_vector([smooth_condition]))

        # need to find initial m by solving helmholtz problem
        CG1 = FunctionSpace(mesh, "CG", 1)
        u0 = prognostic_variables.u
        p = TestFunction(CG1)
        m_CG = Function(CG1)
        ones = Function(prognostic_variables.Vu).project(
            as_vector([Constant(1.)]))

        Lm = (p * m_CG - p * dot(ones, u0) -
              alphasq * p.dx(0) * dot(ones, u0.dx(0))) * dx
        mprob0 = NonlinearVariationalProblem(Lm, m_CG)
        msolver0 = NonlinearVariationalSolver(mprob0,
                                              solver_parameters={
                                                  'ksp_type': 'preonly',
                                                  'pc_type': 'lu'
                                              })
        msolver0.solve()
        prognostic_variables.m.interpolate(m_CG)

        if prognostic_variables.scheme == 'LASCH':
            prognostic_variables.Eu.assign(prognostic_variables.u)
            prognostic_variables.Em.assign(prognostic_variables.m)

    elif prognostic_variables.scheme in ('conforming', 'hydrodynamic', 'test',
                                         'LASCH_hydrodynamic',
                                         'LASCH_hydrodynamic_m',
                                         'no_gradient'):
        if ic == 'peakon':
            Vu = prognostic_variables.Vu
            # delta = Function(Vu)
            # middle_index = int(len(delta.dat.data[:]) / 2)
            # delta.dat.data[middle_index] = 1
            # u0 = prognostic_variables.u
            # phi = TestFunction(Vu)
            #
            # eqn = phi * u0 * dx + alphasq * phi.dx(0) * u0.dx(0) * dx - phi * delta * dx
            # prob = NonlinearVariationalProblem(eqn, u0)
            # solver = NonlinearVariationalSolver(prob)
            # solver.solve()
            # W = MixedFunctionSpace((Vu, Vu))
            # psi, phi = TestFunctions(W)
            # w = Function(W)
            # u, F = w.split()
            # u.interpolate(ic_expr)
            # u, F = split(w)
            #
            # eqn = (psi * u * dx - psi * (0.5 * u * u + F) * dx
            #        + phi * F * dx + alphasq * phi.dx(0) * F.dx(0) * dx
            #        - phi * u * u * dx - 0.5 * alphasq * phi * u.dx(0) * u.dx(0) * dx)
            #
            # u, F = w.split()
            #
            # prob = NonlinearVariationalProblem(eqn, w)
            # solver = NonlinearVariationalSolver(prob)
            # solver.solve()
            # prognostic_variables.u.assign(u)
            prognostic_variables.u.project(ic_expr)
            # prognostic_variables.u.interpolate(ic_expr)
        else:
            VCG5 = FunctionSpace(mesh, "CG", 5)
            smooth_condition = Function(VCG5).interpolate(ic_expr)
            prognostic_variables.u.project(smooth_condition)

        if prognostic_variables.scheme in [
                'LASCH_hydrodynamic', 'LASCH_hydrodynamic_m'
        ]:
            prognostic_variables.Eu.assign(prognostic_variables.u)

    else:
        raise NotImplementedError('Other schemes not yet implemented.')
Ejemplo n.º 16
0
    def initialize(self, pc):
        """Set up the problem context. Take the original
        mixed problem and reformulate the problem as a
        hybridized mixed system.

        A KSP is created for the Lagrange multiplier system.
        """
        from ufl.algorithms.map_integrands import map_integrand_dags
        from firedrake import (FunctionSpace, TrialFunction, TrialFunctions,
                               TestFunction, Function, BrokenElement,
                               MixedElement, FacetNormal, Constant,
                               DirichletBC, Projector)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import ArgumentReplacer, split_form

        # Extract the problem context
        prefix = pc.getOptionsPrefix()
        _, P = pc.getOperators()
        context = P.getPythonContext()
        test, trial = context.a.arguments()

        V = test.function_space()
        if V.mesh().cell_set._extruded:
            # TODO: Merge FIAT branch to support TPC trace elements
            raise NotImplementedError("Not implemented on extruded meshes.")

        # Break the function spaces and define fully discontinuous spaces
        broken_elements = [BrokenElement(Vi.ufl_element()) for Vi in V]
        elem = MixedElement(broken_elements)
        V_d = FunctionSpace(V.mesh(), elem)
        arg_map = {test: TestFunction(V_d), trial: TrialFunction(V_d)}

        # Replace the problems arguments with arguments defined
        # on the new discontinuous spaces
        replacer = ArgumentReplacer(arg_map)
        new_form = map_integrand_dags(replacer, context.a)

        # Create the space of approximate traces.
        # The vector function space will have a non-empty value_shape
        W = next(v for v in V if bool(v.ufl_element().value_shape()))
        if W.ufl_element().family() in ["Raviart-Thomas", "RTCF"]:
            tdegree = W.ufl_element().degree() - 1

        else:
            tdegree = W.ufl_element().degree()

        # NOTE: Once extruded is ready, we will need to be aware of this
        # and construct the appropriate trace space for the HDiv element
        TraceSpace = FunctionSpace(V.mesh(), "HDiv Trace", tdegree)

        # NOTE: For extruded, we will need to add "on_top" and "on_bottom"
        trace_conditions = [
            DirichletBC(TraceSpace, Constant(0.0), "on_boundary")
        ]

        # Set up the functions for the original, hybridized
        # and schur complement systems
        self.broken_solution = Function(V_d)
        self.broken_rhs = Function(V_d)
        self.trace_solution = Function(TraceSpace)
        self.unbroken_solution = Function(V)
        self.unbroken_rhs = Function(V)

        # Create the symbolic Schur-reduction
        Atilde = Tensor(new_form)
        gammar = TestFunction(TraceSpace)
        n = FacetNormal(V.mesh())

        # Vector trial function will have a non-empty ufl_shape
        sigma = next(f for f in TrialFunctions(V_d) if bool(f.ufl_shape))

        # NOTE: Once extruded is ready, this will change slightly
        # to include both horizontal and vertical interior facets
        K = Tensor(gammar('+') * ufl.dot(sigma, n) * ufl.dS)

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(TraceSpace)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * self.broken_rhs,
            tensor=self.schur_rhs,
            form_compiler_parameters=context.fc_params)

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp,
                                 bcs=trace_conditions,
                                 form_compiler_parameters=context.fc_params)
        self._assemble_S = create_assembly_callable(
            schur_comp,
            tensor=self.S,
            bcs=trace_conditions,
            form_compiler_parameters=context.fc_params)

        self._assemble_S()
        self.S.force_evaluation()
        Smat = self.S.petscmat

        # Nullspace for the multiplier problem
        nullsp = P.getNullSpace()
        if nullsp.handle != 0:
            new_vecs = get_trace_nullspace_vecs(K * Atilde.inv, nullsp, V, V_d,
                                                TraceSpace)
            tr_nullsp = PETSc.NullSpace().create(vectors=new_vecs,
                                                 comm=pc.comm)
            Smat.setNullSpace(tr_nullsp)

        # Set up the KSP for the system of Lagrange multipliers
        ksp = PETSc.KSP().create(comm=pc.comm)
        ksp.setOptionsPrefix(prefix + "trace_")
        ksp.setTolerances(rtol=1e-13)
        ksp.setOperators(Smat)
        ksp.setUp()
        ksp.setFromOptions()
        self.ksp = ksp

        # Now we construct the local tensors for the reconstruction stage
        # TODO: Add support for mixed tensors and these variables
        # become unnecessary
        split_forms = split_form(new_form)
        A = Tensor(next(sf.form for sf in split_forms if sf.indices == (0, 0)))
        B = Tensor(next(sf.form for sf in split_forms if sf.indices == (1, 0)))
        C = Tensor(next(sf.form for sf in split_forms if sf.indices == (1, 1)))
        trial = TrialFunction(
            FunctionSpace(V.mesh(), BrokenElement(W.ufl_element())))
        K_local = Tensor(gammar('+') * ufl.dot(trial, n) * ufl.dS)

        # Split functions and reconstruct each bit separately
        sigma_h, u_h = self.broken_solution.split()
        g, f = self.broken_rhs.split()

        # Pressure reconstruction
        M = B * A.inv * B.T + C
        u_sol = M.inv * f + M.inv * (
            B * A.inv * K_local.T * self.trace_solution - B * A.inv * g)
        self._assemble_pressure = create_assembly_callable(
            u_sol, tensor=u_h, form_compiler_parameters=context.fc_params)

        # Velocity reconstruction
        sigma_sol = A.inv * g + A.inv * (B.T * u_h -
                                         K_local.T * self.trace_solution)
        self._assemble_velocity = create_assembly_callable(
            sigma_sol,
            tensor=sigma_h,
            form_compiler_parameters=context.fc_params)

        # Set up the projector for projecting the broken solution
        # into the unbroken finite element spaces
        # NOTE: Tolerance here matters!
        sigma_b, _ = self.broken_solution.split()
        sigma_u, _ = self.unbroken_solution.split()
        self.projector = Projector(sigma_b,
                                   sigma_u,
                                   solver_parameters={
                                       "ksp_type": "cg",
                                       "ksp_rtol": 1e-13
                                   })
Ejemplo n.º 17
0
    interpolate,
    max_value,
    assemble,
    norm,
    Constant,
    UnitDiskMesh,
)
import icepack
from icepack.constants import ice_density as ρ_I, glen_flow_law as n, gravity as g
import numpy as np

# Test our numerical solvers against this analytical solution.
R = 100e3  # Radius of ice sheet in meters
R_mesh = R * 0.75  # Radius of mesh (set multiplier to <1 to
# ignore interpolation errors at the edge)
alpha = Constant(R)
T = Constant(254.15)
A = icepack.rate_factor(T)
A0 = 2 * A * (ρ_I * g)**n / (n + 2)


def make_mesh(R_mesh, refinement):
    mesh = UnitDiskMesh(refinement)
    mesh.coordinates.dat.data[:] *= R_mesh
    return mesh


# Pick a geometry in order to have an exact solution. We'll use the
# Bueler profile from section 5.6.3 in Greve and Blatter (2009).
# Citation: Greve, Ralf, and Heinz Blatter. Dynamics of ice sheets
# and glaciers. Springer Science & Business Media, 2009.
    def _setup_solver(self):
        state = self.state      # just cutting down line length a bit
        Dt = state.timestepping.dt
        beta_ = Dt*state.timestepping.alpha
        cp = state.parameters.cp
        mu = state.mu
        Vu = state.spaces("HDiv")
        Vtheta = state.spaces("HDiv_v")
        Vrho = state.spaces("DG")

        # Store time-stepping coefficients as UFL Constants
        dt = Constant(Dt)
        beta = Constant(beta_)
        beta_cp = Constant(beta_ * cp)

        # Split up the rhs vector (symbolically)
        u_in, rho_in, theta_in = split(state.xrhs)

        # Build the reduced function space for u,rho
        M = MixedFunctionSpace((Vu, Vrho))
        w, phi = TestFunctions(M)
        u, rho = TrialFunctions(M)

        n = FacetNormal(state.mesh)

        # Get background fields
        thetabar = state.fields("thetabar")
        rhobar = state.fields("rhobar")
        pibar = thermodynamics.pi(state.parameters, rhobar, thetabar)
        pibar_rho = thermodynamics.pi_rho(state.parameters, rhobar, thetabar)
        pibar_theta = thermodynamics.pi_theta(state.parameters, rhobar, thetabar)

        # Analytical (approximate) elimination of theta
        k = state.k             # Upward pointing unit vector
        theta = -dot(k, u)*dot(k, grad(thetabar))*beta + theta_in

        # Only include theta' (rather than pi') in the vertical
        # component of the gradient

        # the pi prime term (here, bars are for mean and no bars are
        # for linear perturbations)

        pi = pibar_theta*theta + pibar_rho*rho

        # vertical projection
        def V(u):
            return k*inner(u, k)

        # specify degree for some terms as estimated degree is too large
        dxp = dx(degree=(self.quadrature_degree))
        dS_vp = dS_v(degree=(self.quadrature_degree))

        # add effect of density of water upon theta
        if self.moisture is not None:
            water_t = Function(Vtheta).assign(0.0)
            for water in self.moisture:
                water_t += self.state.fields(water)
            theta_w = theta / (1 + water_t)
            thetabar_w = thetabar / (1 + water_t)
        else:
            theta_w = theta
            thetabar_w = thetabar

        eqn = (
            inner(w, (state.h_project(u) - u_in))*dx
            - beta_cp*div(theta_w*V(w))*pibar*dxp
            # following does nothing but is preserved in the comments
            # to remind us why (because V(w) is purely vertical).
            # + beta_cp*jump(theta*V(w), n)*avg(pibar)*dS_v
            - beta_cp*div(thetabar_w*w)*pi*dxp
            + beta_cp*jump(thetabar_w*w, n)*avg(pi)*dS_vp
            + (phi*(rho - rho_in) - beta*inner(grad(phi), u)*rhobar)*dx
            + beta*jump(phi*u, n)*avg(rhobar)*(dS_v + dS_h)
        )

        if mu is not None:
            eqn += dt*mu*inner(w, k)*inner(u, k)*dx
        aeqn = lhs(eqn)
        Leqn = rhs(eqn)

        # Place to put result of u rho solver
        self.urho = Function(M)

        # Boundary conditions (assumes extruded mesh)
        bcs = [DirichletBC(M.sub(0), 0.0, "bottom"),
               DirichletBC(M.sub(0), 0.0, "top")]

        # Solver for u, rho
        urho_problem = LinearVariationalProblem(
            aeqn, Leqn, self.urho, bcs=bcs)

        self.urho_solver = LinearVariationalSolver(urho_problem,
                                                   solver_parameters=self.solver_parameters,
                                                   options_prefix='ImplicitSolver')

        # Reconstruction of theta
        theta = TrialFunction(Vtheta)
        gamma = TestFunction(Vtheta)

        u, rho = self.urho.split()
        self.theta = Function(Vtheta)

        theta_eqn = gamma*(theta - theta_in
                           + dot(k, u)*dot(k, grad(thetabar))*beta)*dx

        theta_problem = LinearVariationalProblem(lhs(theta_eqn),
                                                 rhs(theta_eqn),
                                                 self.theta)
        self.theta_solver = LinearVariationalSolver(theta_problem,
                                                    options_prefix='thetabacksubstitution')
Ejemplo n.º 19
0
                  family="BDM",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  diagnostics=diagnostics,
                  fieldlist=fieldlist)

    # interpolate initial conditions
    u0 = state.fields("u")
    D0 = state.fields("D")
    u_max = 2 * pi * R / (12 * day
                          )  # Maximum amplitude of the zonal wind (m/s)
    uexpr = as_vector([-u_max * x[1] / R, u_max * x[0] / R, 0.0])
    Omega = parameters.Omega
    g = parameters.g
    Dexpr = Constant(1.0)
    bexpr = H - ((R * Omega * u_max + u_max * u_max / 2.0) * (x[2] * x[2] /
                                                              (R * R))) / g
    # Coriolis expression
    fexpr = 2 * Omega * x[2] / R
    V = FunctionSpace(mesh, "CG", 1)
    f = state.fields("coriolis", V)
    f.interpolate(fexpr)  # Coriolis frequency (1/s)
    b = state.fields("topography", D0.function_space())
    b.interpolate(bexpr)

    u0.project(uexpr)
    D0.interpolate(Dexpr)
    state.initialise([('u', u0), ('D', D0)])

    euler_poincare = False
Ejemplo n.º 20
0
if recovered:
    VDG1 = FunctionSpace(mesh, "DG", 1)
    VCG1 = FunctionSpace(mesh, "CG", 1)
    Vt_brok = FunctionSpace(mesh, BrokenElement(Vt.ufl_element()))
    Vu_DG1 = VectorFunctionSpace(mesh, "DG", 1)
    Vu_CG1 = VectorFunctionSpace(mesh, "CG", 1)
    Vu_brok = FunctionSpace(mesh, BrokenElement(Vu.ufl_element()))

    u_spaces = (Vu_DG1, Vu_CG1, Vu_brok)
    rho_spaces = (VDG1, VCG1, Vr)
    theta_spaces = (VDG1, VCG1, Vt_brok)

# Define constant theta_e and water_t
Tsurf = 300.0
theta_b = Function(Vt).interpolate(Constant(Tsurf))

# Calculate hydrostatic fields
compressible_hydrostatic_balance(state, theta_b, rho0, solve_for_rho=True)

# make mean fields
rho_b = Function(Vr).assign(rho0)

# define perturbation
xc = L / 2
zc = 2000.
rc = 2000.
Tdash = 2.0
theta_pert = Function(Vt).interpolate(conditional(sqrt((x[0] - xc) ** 2 + (x[1] - zc) ** 2) > rc,
                                                  0.0, Tdash *
                                                  (cos(pi * sqrt(((x[0] - xc) / rc) ** 2 + ((x[1] - zc) / rc) ** 2) / 2.0))
Ejemplo n.º 21
0
def setup_condens(dirname):

    # declare grid shape, with length L and height H
    L = 1000.
    H = 1000.
    nlayers = int(H / 100.)
    ncolumns = int(L / 100.)

    # make mesh
    m = PeriodicIntervalMesh(ncolumns, L)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=(H / nlayers))
    x = SpatialCoordinate(mesh)

    fieldlist = ['u', 'rho', 'theta']
    timestepping = TimesteppingParameters(dt=1.0, maxk=4, maxi=1)
    output = OutputParameters(dirname=dirname + "/condens",
                              dumpfreq=1,
                              dumplist=['u'],
                              perturbation_fields=['theta', 'rho'])
    parameters = CompressibleParameters()

    state = State(mesh,
                  vertical_degree=1,
                  horizontal_degree=1,
                  family="CG",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  fieldlist=fieldlist,
                  diagnostic_fields=[Sum('water_v', 'water_c')])

    # declare initial fields
    u0 = state.fields("u")
    rho0 = state.fields("rho")
    theta0 = state.fields("theta")

    # spaces
    Vpsi = FunctionSpace(mesh, "CG", 2)
    Vt = theta0.function_space()
    Vr = rho0.function_space()

    # make a gradperp
    gradperp = lambda u: as_vector([-u.dx(1), u.dx(0)])

    # declare tracer field and a background field
    water_v0 = state.fields("water_v", Vt)
    water_c0 = state.fields("water_c", Vt)

    # Isentropic background state
    Tsurf = Constant(300.)

    theta_b = Function(Vt).interpolate(Tsurf)
    rho_b = Function(Vr)

    # Calculate initial rho
    compressible_hydrostatic_balance(state, theta_b, rho_b, solve_for_rho=True)

    # set up water_v
    xc = 500.
    zc = 350.
    rc = 250.
    r = sqrt((x[0] - xc)**2 + (x[1] - zc)**2)
    w_expr = conditional(r > rc, 0., 0.25 * (1. + cos((pi / rc) * r)))

    # set up velocity field
    u_max = 10.0

    psi_expr = ((-u_max * L / pi) * sin(2 * pi * x[0] / L) *
                sin(pi * x[1] / L))

    psi0 = Function(Vpsi).interpolate(psi_expr)
    u0.project(gradperp(psi0))
    theta0.interpolate(theta_b)
    rho0.interpolate(rho_b)
    water_v0.interpolate(w_expr)

    state.initialise([('u', u0), ('rho', rho0), ('theta', theta0),
                      ('water_v', water_v0), ('water_c', water_c0)])
    state.set_reference_profiles([('rho', rho_b), ('theta', theta_b)])

    # set up advection schemes
    rhoeqn = AdvectionEquation(state, Vr, equation_form="continuity")
    thetaeqn = SUPGAdvection(state,
                             Vt,
                             supg_params={"dg_direction": "horizontal"},
                             equation_form="advective")

    # build advection dictionary
    advected_fields = []
    advected_fields.append(("u", NoAdvection(state, u0, None)))
    advected_fields.append(("rho", SSPRK3(state, rho0, rhoeqn)))
    advected_fields.append(("theta", SSPRK3(state, theta0, thetaeqn)))
    advected_fields.append(("water_v", SSPRK3(state, water_v0, thetaeqn)))
    advected_fields.append(("water_c", SSPRK3(state, water_c0, thetaeqn)))

    physics_list = [Condensation(state)]

    # build time stepper
    stepper = AdvectionDiffusion(state,
                                 advected_fields,
                                 physics_list=physics_list)

    return stepper, 5.0
Ejemplo n.º 22
0
def setup_tracer(dirname, hybridization):

    # declare grid shape, with length L and height H
    L = 1000.
    H = 1000.
    nlayers = int(H / 100.)
    ncolumns = int(L / 100.)

    # make mesh
    m = PeriodicIntervalMesh(ncolumns, L)
    mesh = ExtrudedMesh(m, layers=nlayers, layer_height=(H / nlayers))

    fieldlist = ['u', 'rho', 'theta']
    timestepping = TimesteppingParameters(dt=10.0, maxk=4, maxi=1)
    output = OutputParameters(dirname=dirname+"/tracer",
                              dumpfreq=1,
                              dumplist=['u'],
                              perturbation_fields=['theta', 'rho'])
    parameters = CompressibleParameters()

    state = State(mesh, vertical_degree=1, horizontal_degree=1,
                  family="CG",
                  timestepping=timestepping,
                  output=output,
                  parameters=parameters,
                  fieldlist=fieldlist,
                  diagnostic_fields=[Difference('theta', 'tracer')])

    # declare initial fields
    u0 = state.fields("u")
    rho0 = state.fields("rho")
    theta0 = state.fields("theta")

    # spaces
    Vu = u0.function_space()
    Vt = theta0.function_space()
    Vr = rho0.function_space()

    # declare tracer field and a background field
    tracer0 = state.fields("tracer", Vt)

    # Isentropic background state
    Tsurf = Constant(300.)

    theta_b = Function(Vt).interpolate(Tsurf)
    rho_b = Function(Vr)

    # Calculate initial rho
    compressible_hydrostatic_balance(state, theta_b, rho_b,
                                     solve_for_rho=True)

    # set up perturbation to theta
    xc = 500.
    zc = 350.
    rc = 250.
    x = SpatialCoordinate(mesh)
    r = sqrt((x[0]-xc)**2 + (x[1]-zc)**2)
    theta_pert = conditional(r > rc, 0., 0.25*(1. + cos((pi/rc)*r)))

    theta0.interpolate(theta_b + theta_pert)
    rho0.interpolate(rho_b)
    tracer0.interpolate(theta0)

    state.initialise([('u', u0),
                      ('rho', rho0),
                      ('theta', theta0),
                      ('tracer', tracer0)])
    state.set_reference_profiles([('rho', rho_b),
                                  ('theta', theta_b)])

    # set up advection schemes
    ueqn = EulerPoincare(state, Vu)
    rhoeqn = AdvectionEquation(state, Vr, equation_form="continuity")
    thetaeqn = SUPGAdvection(state, Vt,
                             supg_params={"dg_direction": "horizontal"},
                             equation_form="advective")

    # build advection dictionary
    advected_fields = []
    advected_fields.append(("u", ThetaMethod(state, u0, ueqn)))
    advected_fields.append(("rho", SSPRK3(state, rho0, rhoeqn)))
    advected_fields.append(("theta", SSPRK3(state, theta0, thetaeqn)))
    advected_fields.append(("tracer", SSPRK3(state, tracer0, thetaeqn)))

    # Set up linear solver
    if hybridization:
        linear_solver = HybridizedCompressibleSolver(state)
    else:
        linear_solver = CompressibleSolver(state)

    compressible_forcing = CompressibleForcing(state)

    # build time stepper
    stepper = CrankNicolson(state, advected_fields, linear_solver,
                            compressible_forcing)

    return stepper, 100.0
Ejemplo n.º 23
0
#inflow  = DirichletBC(Q, p_in, "x[0] < DOLFIN_EPS" )

inflow = DirichletBC(V, u_in, 1)

outflow = DirichletBC(Q, 0, 2)

bcu = [noslip, inflow]
bcp = [outflow]

# Create functions
u0 = Function(V)
u1 = Function(V)
p1 = Function(Q)

# Define coefficients
k = Constant(dt)
f = Constant((0, 0))

# Tentative velocity step
F1 = (1.0/k)*inner(u - u0, v)*dx + inner(grad(u0)*u0, v)*dx + \
     (1.0/Re)*inner(grad(u), grad(v))*dx - inner(f, v)*dx

a1 = lhs(F1)
L1 = rhs(F1)

# Pressure update
a2 = inner(grad(p), grad(q)) * dx
L2 = -(1 / k) * div(u1) * q * dx

# Velocity update
a3 = inner(u, v) * dx
Ejemplo n.º 24
0
 def compute(self, state):
     u = state.fields("u")
     dt = Constant(state.timestepping.dt)
     return self.field.project(sqrt(dot(u, u))/sqrt(self.area)*dt)
Ejemplo n.º 25
0
    def __init__(self,
                 salinity,
                 temperature,
                 pressure_perturbation,
                 z,
                 GammaTfunc=None,
                 velocity=None,
                 ice_heat_flux=True,
                 HJ99Gamma=False,
                 f=None):
        super().__init__(salinity, temperature, pressure_perturbation, z)

        if velocity is None:
            # Use constant turbulent thermal and salinity exchange values given in Holland and Jenkins 1999.
            gammaT = self.gammaT
            gammaS = self.gammaS

        else:
            u = velocity

            if HJ99Gamma:
                # Holland and Jenkins 1999 turbulent heat/salt exchange velocity as a function
                # of friction velocity. This should be the same as in MITgcm.

                # Holland, D.M. and Jenkins, A., 1999. Modeling thermodynamic ice–ocean interactions at
                # the base of an ice shelf. Journal of Physical Oceanography, 29(8), pp.1787-1800.

                # Calculate friction velocity
                if isinstance(u, float):
                    print("Input velocity:", u)
                    u_bounded = max(u, 1e-3)
                    print("Bounded velocity:", u_bounded)
                else:
                    u_bounded = conditional(u > 1e-3, u, 1e-3)
                u_star = pow(self.C_d * pow(u_bounded, 2), 0.5)

                # Calculate turbulent component of thermal and salinity exchange velocity (Eq 15)
                # N.b MITgcm sets etastar = 1 so that the first part of Gamma_turb term below is constant.
                # eta_star = (1 + (zeta_N * u_star) / (f * L0 * Rc))^-1/2  (Eq 18)
                # In H&J99  eta_star is set to 1 when the Obukhov length is negative (i.e the buoyancy flux
                # is destabilising. This occurs during freezing. (Melting is stabilising)
                # So it does seem a bit odd because then eta_star is tuned to freezing conditions
                # need to work this out...?
                Gamma_Turb = 1.0 / (2.0 * self.zeta_N *
                                    self.eta_star) - 1.0 / self.k
                if f is not None:
                    # Add extra term if using coriolis term
                    # Calculate viscous sublayer thickness (Eq 17)
                    h_nu = 5.0 * self.nu / u_star
                    Gamma_Turb += ln(
                        u_star * self.zeta_N * pow(self.eta_star, 2) /
                        (abs(f) * h_nu)) / self.k

                # Calculate molecular components of thermal exchange velocity (Eq 16)
                GammaT_Mole = 12.5 * pow(self.Pr, 2.0 / 3.0) - 6.0

                # Calculate molecular component of salinity exchange velocity (Eq 16)
                GammaS_Mole = 12.5 * pow(self.Sc, 2.0 / 3.0) - 6.0

                # Calculate thermal and salinity exchange velocity. (Eq 14)
                # Do we need to catch -ve gamma? could have -ve Gamma_Turb?
                gammaT = u_star / (Gamma_Turb + GammaT_Mole)
                gammaS = u_star / (Gamma_Turb + GammaS_Mole)

                # print exchange velocities if testing when input velocity is a float.
                if isinstance(gammaT, float) or isinstance(gammaS, float):
                    print("gammaT = ", gammaT)
                    print("gammaS = ", gammaS)

            else:
                # ISOMIP+ based on Jenkins et al 2010. Measurement of basal rates beneath Ronne Ice Shelf
                u_tidal = 0.01
                u_star = pow(self.C_d * (pow(u, 2) + pow(u_tidal, 2)), 0.5)
                if GammaTfunc is None:
                    gammaT = self.GammaT * u_star
                    gammaS = self.GammaS * u_star
                else:
                    gammaT = GammaTfunc * u_star
                    gammaS = (GammaTfunc / 35.0) * u_star

                # print exchange velocities if testing when input velocity is a float.
                if isinstance(gammaT, float) or isinstance(gammaS, float):
                    print("gammaT = ", gammaT)
                    print("gammaS = ", gammaS)

        b_plus_cPb = (self.b + self.c * self.P_full
                      )  # save calculating this each time...

        # Calculate coefficients in quadratic equation for salinity at ice-ocean boundary.
        # Aa.Sb^2 + Bb.Sb + Cc = 0
        Aa = self.c_p_m * gammaT * self.a
        Bb = -gammaS * self.Lf
        Bb -= self.c_p_m * gammaT * self.T
        Bb += self.c_p_m * gammaT * b_plus_cPb
        Cc = gammaS * self.S * self.Lf

        if ice_heat_flux:
            Aa -= gammaS * self.c_p_i * self.a
            Bb += gammaS * self.S * self.c_p_i * self.a
            Bb -= gammaS * self.c_p_i * b_plus_cPb
            Bb += gammaS * self.c_p_i * self.T_ice
            Cc += gammaS * self.S * self.c_p_i * b_plus_cPb
            Cc -= gammaS * self.S * self.c_p_i * self.T_ice

        S1 = (-Bb + pow(Bb**2 - 4.0 * Aa * Cc, 0.5)) / (2.0 * Aa)
        S2 = (-Bb - pow(Bb**2 - 4.0 * Aa * Cc, 0.5)) / (2.0 * Aa)
        print(type(S1))
        print(S1)
        if isinstance(S1, (float, sympy.core.numbers.Float)):
            # Print statements for testing
            print("S1 = ", S1)
            print("S2 = ", S2)
            if S1 > 0:
                self.Sb = S1
                print("Choose S1")
            else:
                self.Sb = S2
                print("Choose S2")
        elif isinstance(S1, sympy.core.add.Add):
            self.Sb = S2
        else:
            self.Sb = conditional(S1 > 0.0, S1, S2)

        self.Tb = self.a * self.Sb + self.b + self.c * self.P_full
        self.wb = gammaS * (self.S - self.Sb) / self.Sb

        if ice_heat_flux:
            self.Q_ice = -self.rho0 * (self.T_ice -
                                       self.Tb) * self.c_p_i * self.wb
        else:
            if isinstance(S1, float):
                self.Q_ice = 0.0
            else:
                self.Q_ice = Constant(0.0)

        self.Q_mixed = -self.rho0 * self.c_p_m * gammaT * (self.Tb - self.T)
        self.Q_latent = self.Q_ice - self.Q_mixed

        self.QS_mixed = -self.rho0 * gammaS * (self.Sb - self.S)

        self.T_flux_bc = -(self.wb + gammaT) * (self.Tb - self.T)
        self.S_flux_bc = -(self.wb + gammaS) * (self.Sb - self.S)
Ejemplo n.º 26
0
    def __init__(self,
                 mesh,
                 dt,
                 output=None,
                 parameters=None,
                 diagnostics=None,
                 diagnostic_fields=None):

        if output is None:
            raise RuntimeError(
                "You must provide a directory name for dumping results")
        else:
            self.output = output
        self.parameters = parameters

        if diagnostics is not None:
            self.diagnostics = diagnostics
        else:
            self.diagnostics = Diagnostics()
        if diagnostic_fields is not None:
            self.diagnostic_fields = diagnostic_fields
        else:
            self.diagnostic_fields = []

        # The mesh
        self.mesh = mesh

        self.spaces = SpaceCreator(mesh)

        if self.output.dumplist is None:

            self.output.dumplist = []

        self.fields = StateFields(*self.output.dumplist)

        self.dumpdir = None
        self.dumpfile = None
        self.to_pickup = None

        # figure out if we're on a sphere
        try:
            self.on_sphere = (mesh._base_mesh.geometric_dimension() == 3
                              and mesh._base_mesh.topological_dimension() == 2)
        except AttributeError:
            self.on_sphere = (mesh.geometric_dimension() == 3
                              and mesh.topological_dimension() == 2)

        #  build the vertical normal and define perp for 2d geometries
        dim = mesh.topological_dimension()
        if self.on_sphere:
            x = SpatialCoordinate(mesh)
            R = sqrt(inner(x, x))
            self.k = interpolate(x / R, mesh.coordinates.function_space())
            if dim == 2:
                outward_normals = CellNormal(mesh)
                self.perp = lambda u: cross(outward_normals, u)
        else:
            kvec = [0.0] * dim
            kvec[dim - 1] = 1.0
            self.k = Constant(kvec)
            if dim == 2:
                self.perp = lambda u: as_vector([-u[1], u[0]])

        # setup logger
        logger.setLevel(output.log_level)
        set_log_handler(mesh.comm)
        if parameters is not None:
            logger.info("Physical parameters that take non-default values:")
            logger.info(", ".join("%s: %s" % (k, float(v))
                                  for (k, v) in vars(parameters).items()))

        #  Constant to hold current time
        self.t = Constant(0.0)
        if type(dt) is Constant:
            self.dt = dt
        elif type(dt) in (float, int):
            self.dt = Constant(dt)
        else:
            raise TypeError(
                f'dt must be a Constant, float or int, not {type(dt)}')
Ejemplo n.º 27
0
                  fieldlist=fieldlist,
                  diagnostic_fields=diagnostic_fields)

    # Initial conditions
    u0 = state.fields("u")
    rho0 = state.fields("rho")
    theta0 = state.fields("theta")
    water0 = state.fields("water", theta0.function_space())

    # spaces
    Vu = u0.function_space()
    Vt = theta0.function_space()
    Vr = rho0.function_space()

    # Isentropic background state
    Tsurf = Constant(300.)

    theta_b = Function(Vt).interpolate(Tsurf)
    rho_b = Function(Vr)

    # Calculate hydrostatic Pi
    compressible_hydrostatic_balance(state, theta_b, rho_b, solve_for_rho=True)

    x = SpatialCoordinate(mesh)
    a = 5.0e3
    deltaTheta = 1.0e-2
    xc = 0.5 * L
    xr = 4000.
    zc = 3000.
    zr = 2000.
    r = sqrt(((x[0] - xc) / xr)**2 + ((x[1] - zc) / zr)**2)
Ejemplo n.º 28
0
                                  no_normal_flow_bc_ids=[1, 2])

# Initial conditions
u0 = state.fields("u")
rho0 = state.fields("rho")
theta0 = state.fields("theta")

# spaces
Vu = state.spaces("HDiv")
Vt = state.spaces("theta")
Vr = state.spaces("DG")
x, z = SpatialCoordinate(mesh)

# Define constant theta_e and water_t
Tsurf = 300.0
theta_b = Function(Vt).interpolate(Constant(Tsurf))

# Calculate hydrostatic fields
compressible_hydrostatic_balance(state, theta_b, rho0, solve_for_rho=True)

# make mean fields
rho_b = Function(Vr).assign(rho0)

# define perturbation
xc = L / 2
zc = 2000.
rc = 2000.
Tdash = 2.0
r = sqrt((x - xc)**2 + (z - zc)**2)
theta_pert = Function(Vt).interpolate(
    conditional(r > rc, 0.0,
Ejemplo n.º 29
0
    def initialize(self, pc):
        """Set up the problem context. Take the original
        mixed problem and reformulate the problem as a
        hybridized mixed system.

        A KSP is created for the Lagrange multiplier system.
        """
        from firedrake import (FunctionSpace, Function, Constant,
                               TrialFunction, TrialFunctions, TestFunction,
                               DirichletBC, assemble)
        from firedrake.assemble import (allocate_matrix,
                                        create_assembly_callable)
        from firedrake.formmanipulation import split_form
        from ufl.algorithms.replace import replace

        # Extract the problem context
        prefix = pc.getOptionsPrefix() + "hybridization_"
        _, P = pc.getOperators()
        self.cxt = P.getPythonContext()

        if not isinstance(self.cxt, ImplicitMatrixContext):
            raise ValueError("The python context must be an ImplicitMatrixContext")

        test, trial = self.cxt.a.arguments()

        V = test.function_space()
        mesh = V.mesh()

        if len(V) != 2:
            raise ValueError("Expecting two function spaces.")

        if all(Vi.ufl_element().value_shape() for Vi in V):
            raise ValueError("Expecting an H(div) x L2 pair of spaces.")

        # Automagically determine which spaces are vector and scalar
        for i, Vi in enumerate(V):
            if Vi.ufl_element().sobolev_space().name == "HDiv":
                self.vidx = i
            else:
                assert Vi.ufl_element().sobolev_space().name == "L2"
                self.pidx = i

        # Create the space of approximate traces.
        W = V[self.vidx]
        if W.ufl_element().family() == "Brezzi-Douglas-Marini":
            tdegree = W.ufl_element().degree()

        else:
            try:
                # If we have a tensor product element
                h_deg, v_deg = W.ufl_element().degree()
                tdegree = (h_deg - 1, v_deg - 1)

            except TypeError:
                tdegree = W.ufl_element().degree() - 1

        TraceSpace = FunctionSpace(mesh, "HDiv Trace", tdegree)

        # Break the function spaces and define fully discontinuous spaces
        broken_elements = ufl.MixedElement([ufl.BrokenElement(Vi.ufl_element()) for Vi in V])
        V_d = FunctionSpace(mesh, broken_elements)

        # Set up the functions for the original, hybridized
        # and schur complement systems
        self.broken_solution = Function(V_d)
        self.broken_residual = Function(V_d)
        self.trace_solution = Function(TraceSpace)
        self.unbroken_solution = Function(V)
        self.unbroken_residual = Function(V)

        # Set up the KSP for the hdiv residual projection
        hdiv_mass_ksp = PETSc.KSP().create(comm=pc.comm)
        hdiv_mass_ksp.setOptionsPrefix(prefix + "hdiv_residual_")

        # HDiv mass operator
        p = TrialFunction(V[self.vidx])
        q = TestFunction(V[self.vidx])
        mass = ufl.dot(p, q)*ufl.dx
        # TODO: Bcs?
        M = assemble(mass, bcs=None, form_compiler_parameters=self.cxt.fc_params)
        M.force_evaluation()
        Mmat = M.petscmat

        hdiv_mass_ksp.setOperators(Mmat)
        hdiv_mass_ksp.setUp()
        hdiv_mass_ksp.setFromOptions()
        self.hdiv_mass_ksp = hdiv_mass_ksp

        # Storing the result of A.inv * r, where A is the HDiv
        # mass matrix and r is the HDiv residual
        self._primal_r = Function(V[self.vidx])

        tau = TestFunction(V_d[self.vidx])
        self._assemble_broken_r = create_assembly_callable(
            ufl.dot(self._primal_r, tau)*ufl.dx,
            tensor=self.broken_residual.split()[self.vidx],
            form_compiler_parameters=self.cxt.fc_params)

        # Create the symbolic Schur-reduction:
        # Original mixed operator replaced with "broken"
        # arguments
        arg_map = {test: TestFunction(V_d),
                   trial: TrialFunction(V_d)}
        Atilde = Tensor(replace(self.cxt.a, arg_map))
        gammar = TestFunction(TraceSpace)
        n = ufl.FacetNormal(mesh)
        sigma = TrialFunctions(V_d)[self.vidx]

        # We zero out the contribution of the trace variables on the exterior
        # boundary. Extruded cells will have both horizontal and vertical
        # facets
        if mesh.cell_set._extruded:
            trace_bcs = [DirichletBC(TraceSpace, Constant(0.0), "on_boundary"),
                         DirichletBC(TraceSpace, Constant(0.0), "bottom"),
                         DirichletBC(TraceSpace, Constant(0.0), "top")]
            K = Tensor(gammar('+') * ufl.dot(sigma, n) * ufl.dS_h +
                       gammar('+') * ufl.dot(sigma, n) * ufl.dS_v)
        else:
            trace_bcs = [DirichletBC(TraceSpace, Constant(0.0), "on_boundary")]
            K = Tensor(gammar('+') * ufl.dot(sigma, n) * ufl.dS)

        # If boundary conditions are contained in the ImplicitMatrixContext:
        if self.cxt.row_bcs:
            raise NotImplementedError("Strong BCs not currently handled. Try imposing them weakly.")

        # Assemble the Schur complement operator and right-hand side
        self.schur_rhs = Function(TraceSpace)
        self._assemble_Srhs = create_assembly_callable(
            K * Atilde.inv * self.broken_residual,
            tensor=self.schur_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        schur_comp = K * Atilde.inv * K.T
        self.S = allocate_matrix(schur_comp, bcs=trace_bcs,
                                 form_compiler_parameters=self.cxt.fc_params)
        self._assemble_S = create_assembly_callable(schur_comp,
                                                    tensor=self.S,
                                                    bcs=trace_bcs,
                                                    form_compiler_parameters=self.cxt.fc_params)

        self._assemble_S()
        self.S.force_evaluation()
        Smat = self.S.petscmat

        # Nullspace for the multiplier problem
        nullspace = create_schur_nullspace(P, -K * Atilde,
                                           V, V_d, TraceSpace,
                                           pc.comm)
        if nullspace:
            Smat.setNullSpace(nullspace)

        # Set up the KSP for the system of Lagrange multipliers
        trace_ksp = PETSc.KSP().create(comm=pc.comm)
        trace_ksp.setOptionsPrefix(prefix)
        trace_ksp.setOperators(Smat)
        trace_ksp.setUp()
        trace_ksp.setFromOptions()
        self.trace_ksp = trace_ksp

        split_mixed_op = dict(split_form(Atilde.form))
        split_trace_op = dict(split_form(K.form))

        # Generate reconstruction calls
        self._reconstruction_calls(split_mixed_op, split_trace_op)

        # NOTE: The projection stage *might* be replaced by a Fortin
        # operator. We may want to allow the user to specify if they
        # wish to use a Fortin operator over a projection, or vice-versa.
        # In a future add-on, we can add a switch which chooses either
        # the Fortin reconstruction or the usual KSP projection.

        # Set up the projection KSP
        hdiv_projection_ksp = PETSc.KSP().create(comm=pc.comm)
        hdiv_projection_ksp.setOptionsPrefix(prefix + 'hdiv_projection_')

        # Reuse the mass operator from the hdiv_mass_ksp
        hdiv_projection_ksp.setOperators(Mmat)

        # Construct the RHS for the projection stage
        self._projection_rhs = Function(V[self.vidx])
        self._assemble_projection_rhs = create_assembly_callable(
            ufl.dot(self.broken_solution.split()[self.vidx], q)*ufl.dx,
            tensor=self._projection_rhs,
            form_compiler_parameters=self.cxt.fc_params)

        # Finalize ksp setup
        hdiv_projection_ksp.setUp()
        hdiv_projection_ksp.setFromOptions()
        self.hdiv_projection_ksp = hdiv_projection_ksp
Ejemplo n.º 30
0
 def __init__(self, density_field, factor=1.):
     super().__init__(required_fields=(density_field, "u_gradient"))
     self.density_field = density_field
     self.factor = Constant(factor)