예제 #1
0
    def __init__(self,
                 F_fluid,
                 F_solid,
                 w,
                 wdot,
                 bcs_mesh,
                 bcs,
                 dF_fluid_u,
                 dF_fluid_udot,
                 dF_solid_u,
                 dF_solid_udot,
                 update=None,
                 report=None,
                 *args,
                 **kwargs):
        self.F_fluid_form = F_fluid
        self.dF_fluid_u_form = dF_fluid_u
        self.dF_fluid_udot_form = dF_fluid_udot
        self.F_solid_form = F_solid
        self.dF_solid_u_form = dF_solid_u
        self.dF_solid_udot_form = dF_solid_udot
        self.bcs_mesh = bcs_mesh
        self.bcs = bcs
        self.w = w
        self.wdot = wdot
        self.a = df.Constant(1.0)
        self.dF_fluid_form = self.dF_fluid_u_form + self.a * self.dF_fluid_udot_form
        self.dF_solid_form = self.dF_solid_u_form + self.a * self.dF_solid_udot_form

        self.report = report
        self.update = update

        self.form_compiler_parameters = {"optimize": True}
        if "form_compiler_parameters" in kwargs:
            self.form_compiler_parameters = kwargs["form_compiler_parameters"]

        self.assembler = df.SystemAssembler(
            self.dF_fluid_form + self.dF_solid_form,
            self.F_fluid_form + self.F_solid_form, self.bcs + self.bcs_mesh)

        self.x = df.as_backend_type(w.vector())
        self.x_petsc = self.x.vec()
        self.xdot = df.as_backend_type(wdot.vector())
        self.xdot_petsc = self.xdot.vec()
        self.b_petsc = self.x_petsc.duplicate()

        # We need to initialise the matrix size and local-to-global maps
        self.A_dolfin = df.PETScMatrix()
        self.assembler.init_global_tensor(
            self.A_dolfin, df.Form(self.dF_fluid_form + self.dF_fluid_form))
        self.A_petsc = self.A_dolfin.mat()
        self.xx = self.A_petsc.createVecRight()
        self.xxdot = self.A_petsc.createVecRight()

        self.A1_dolfin = df.PETScMatrix()
        self.assembler.init_global_tensor(
            self.A1_dolfin, df.Form(self.dF_fluid_form + self.dF_fluid_form))
        self.A2_dolfin = df.PETScMatrix()
        self.assembler.init_global_tensor(
            self.A2_dolfin, df.Form(self.dF_fluid_form + self.dF_fluid_form))
예제 #2
0
    def _construct(self, field_inp):
        # Read the input
        self.name = field_inp.get_value('name', required_type='string')
        self.var_name = field_inp.get_value('variable_name', 'phi', 'string')
        self.stationary = field_inp.get_value('stationary', False, 'bool')
        self.radius = field_inp.get_value('radius', required_type='float')
        self.plot = field_inp.get_value('plot', False, 'bool')

        # Show the input
        sim = self.simulation
        sim.log.info('Creating a free surface zone field %r' % self.name)
        sim.log.info('    Variable: %r' % self.var_name)
        sim.log.info('    Stationary: %r' % self.stationary)

        # Create the level set model
        mesh = sim.data['mesh']
        func_name = '%s_%s' % (self.name, self.var_name)
        self.V = dolfin.FunctionSpace(mesh, 'DG', 0)
        self.function = dolfin.Function(self.V)
        self.function.rename(func_name, func_name)
        sim.data[func_name] = self.function

        # Get the level set view
        level_set_view = sim.multi_phase_model.get_level_set_view()
        level_set_view.add_update_callback(self.update)

        # Form to compute the cell average distance to the free surface
        v = dolfin.TestFunction(self.V)
        ls = level_set_view.level_set_function
        cv = dolfin.CellVolume(self.V.mesh())
        self.dist_form = dolfin.Form(ls * v / cv * dolfin.dx)

        if self.plot:
            sim.io.add_extra_output_function(self.function)
예제 #3
0
    def __init__(self, simulation, u_conv):
        """
        Given a velocity in DG, e.g DG2, produce a velocity in DGT0,
        i.e. a constant on each facet
        """
        V = u_conv[0].function_space()
        V_dgt0 = dolfin.FunctionSpace(V.mesh(), 'DGT', 0)
        u = dolfin.TrialFunction(V_dgt0)
        v = dolfin.TestFunction(V_dgt0)

        ndim = simulation.ndim
        w = u_conv
        w_new = dolfin.as_vector(
            [dolfin.Function(V_dgt0) for _ in range(ndim)])

        dot, avg, dS, ds = dolfin.dot, dolfin.avg, dolfin.dS, dolfin.ds
        a = dot(avg(u), avg(v)) * dS + dot(u, v) * ds

        L = []
        for d in range(ndim):
            L.append(avg(w[d]) * avg(v) * dS + w[d] * v * ds)

        self.lhs = [dolfin.Form(Li) for Li in L]
        self.A = dolfin.assemble(a)
        self.solver = dolfin.PETScKrylovSolver('cg')
        self.velocity = simulation.data['u_conv_dgt0'] = w_new
예제 #4
0
    def _interp(self, name, expr=None, func=None):
        """
        Interpolate the expression into the given function
        """
        sim = self.simulation

        # Determine the function space
        V = self.V
        quad_degree = None
        if name == 'c' and self.colour_projection_degree >= 0:
            # Perform projection for c instead of interpolation to better
            # capture the discontinuous nature of the colour field
            if 'Vc' not in sim.data:
                ocellaris_error(
                    'Error in wave field %r input' % self.name,
                    'Cannot specify colour_to_dg_degree when c is '
                    'not used in the multiphase_solver.',
                )
            V = sim.data['Vc']
            quad_degree = self.colour_projection_degree

        # Get the expression
        if expr is None:
            expr = self._get_expression(name, quad_degree)

        # Get the function
        if func is None:
            if name not in self._functions:
                self._functions[name] = dolfin.Function(V)
            func = self._functions[name]

        if quad_degree is not None:
            if self.colour_projection_form is None:
                # Ensure that we can use the DG0 trick of dividing by the mass
                # matrix diagonal to get the projected value
                if V.ufl_element().degree() != 0:
                    ocellaris_error(
                        'Error in wave field %r input' % self.name,
                        'The colour_to_dg_degree projection is '
                        'currently only implemented when c is DG0',
                    )
                v = dolfin.TestFunction(V)
                dx = dolfin.dx(metadata={'quadrature_degree': quad_degree})
                d = dolfin.CellVolume(V.mesh())  # mass matrix diagonal
                form = expr * v / d * dx
                self.colour_projection_form = dolfin.Form(form)

            # Perform projection by assembly (DG0 only!)
            dolfin.assemble(self.colour_projection_form, tensor=func.vector())
        else:
            # Perform standard interpolation
            func.interpolate(expr)
예제 #5
0
    def get_variable(self, name):
        if not name == self.var_name:
            ocellaris_error(
                'Scalar field does not define %r' % name,
                'This scalar field defines %r' % self.var_name,
            )

        if self.func is not None:
            return self.func

        if self.colour_projection_degree >= 0:
            quad_degree = self.colour_projection_degree
            degree = None
        else:
            quad_degree = None
            degree = self.polydeg

        expr = OcellarisCppExpression(
            self.simulation,
            self.cpp_code,
            'Scalar field %r' % self.name,
            degree=degree,
            update=False,
            quad_degree=quad_degree,
        )
        self.func = dolfin.Function(self.V)

        if quad_degree is not None:

            # Ensure that we can use the DG0 trick of dividing by the mass
            # matrix diagonal to get the projected value
            if self.V.ufl_element().degree() != 0:
                ocellaris_error(
                    'Error in wave field %r input' % self.name,
                    'The colour_to_dg_degree projection is '
                    'currently only implemented when c is DG0',
                )
            v = dolfin.TestFunction(self.V)
            dx = dolfin.dx(metadata={'quadrature_degree': quad_degree})
            d = dolfin.CellVolume(self.V.mesh())  # mass matrix diagonal
            form = expr * v / d * dx
            compiled_form = dolfin.Form(form)

            # Perform projection by assembly (DG0 only!)
            dolfin.assemble(compiled_form, tensor=self.func.vector())
        else:
            # Perform standard interpolation
            self.func.interpolate(expr)

        return self.func
예제 #6
0
    def tune_factors(self, setup=False):
        if setup:
            # Incoming wave data
            u_inlet = self.inflow_field.get_variable('uhoriz')
            c_inlet = self.inflow_field.get_variable('c')
            ds_inlet = self.simulation.data['ds'](self.inflow_ds_mark_id)

            # Outlet data
            u_outlet = self._get_expression('uhoriz')
            c_outlet = self._get_expression('c')
            ds_outlet = self.simulation.data['ds'](self.outflow_ds_mark_id)

            # Compute unit fluxes
            self.const_speed_above.assign(dolfin.Constant(1.0))
            self.const_speed_below.assign(dolfin.Constant(1.0))
            self.uf_above = dolfin.assemble(
                (1 - c_outlet) * u_outlet * ds_outlet)
            self.uf_below = dolfin.assemble(c_outlet * u_outlet * ds_outlet)

            self.form_inlet_above = dolfin.Form(
                (1 - c_inlet) * u_inlet * ds_inlet)
            self.form_inlet_below = dolfin.Form(c_inlet * u_inlet * ds_inlet)
            self.form_inlet_total = dolfin.Form(u_inlet * ds_inlet)

            self.form_outlet_above = dolfin.Form(
                (1 - c_outlet) * u_outlet * ds_outlet)
            self.form_outlet_below = dolfin.Form(c_outlet * u_outlet *
                                                 ds_outlet)
            self.form_outlet_total = dolfin.Form(u_outlet * ds_outlet)

        import time

        t1 = time.time()

        # Compute flux above and below at the inlet
        inlet_flux_above = dolfin.assemble(self.form_inlet_above)
        inlet_flux_below = dolfin.assemble(self.form_inlet_below)
        inlet_flux_total = dolfin.assemble(self.form_inlet_total)
        is_motion = abs(inlet_flux_above) > 0 or abs(inlet_flux_below) > 0

        # Estimate the fluxes at the outlet (this gets within 99.99% of total zero flux)
        self.const_speed_above.assign(
            dolfin.Constant(inlet_flux_above / self.uf_above))
        self.const_speed_below.assign(
            dolfin.Constant(inlet_flux_below / self.uf_below))

        if OPTIMISE_FLUXES and is_motion:
            # Use root finding to adjust the speed above the free surface
            # in order to get zero total volume flux in the domain

            def func_to_minimise(vel_above):
                "Compute the difference in total flux"
                # Adjust outflow speed above the FS
                self.const_speed_above.assign(dolfin.Constant(vel_above))
                outlet_flux_total = dolfin.assemble(self.form_outlet_total)
                return (outlet_flux_total - inlet_flux_total)**2

            # Starting values - use the basic unit fluxes
            x1 = inlet_flux_above / self.uf_above
            x0 = x1 * 0.99

            xN, niter = find_root_secant(x0, x1, func_to_minimise)

        if False and abs(inlet_flux_below) > 0:
            outlet_flux_above = dolfin.assemble(self.form_outlet_above)
            outlet_flux_below = dolfin.assemble(self.form_outlet_below)
            outlet_flux_total = dolfin.assemble(self.form_outlet_total)
            print('uf ab & be', self.uf_above, self.uf_below)
            print(
                'flux_above',
                inlet_flux_above,
                outlet_flux_above,
                outlet_flux_above / inlet_flux_above,
            )
            print(
                'flux_below',
                inlet_flux_below,
                outlet_flux_below,
                outlet_flux_below / inlet_flux_below,
            )
            print(
                'flux_total',
                inlet_flux_total,
                outlet_flux_total,
                outlet_flux_total / inlet_flux_total,
            )
            print(xN - x0)
            print(niter)
            print('Took %g seconds' % (time.time() - t1))
예제 #7
0
    def define_simple_equations(self):
        """
        Setup weak forms for SIMPLE form
        """
        sim = self.simulation
        self.Vuvw = sim.data['uvw_star'].function_space()
        Vp = sim.data['Vp']

        # The trial and test functions in a coupled space (to be split)
        func_spaces = [self.Vuvw, Vp]
        e_mixed = dolfin.MixedElement([fs.ufl_element() for fs in func_spaces])
        Vcoupled = dolfin.FunctionSpace(sim.data['mesh'], e_mixed)
        tests = dolfin.TestFunctions(Vcoupled)
        trials = dolfin.TrialFunctions(Vcoupled)

        # Split into components
        v = dolfin.as_vector(tests[0][:])
        u = dolfin.as_vector(trials[0][:])
        q = tests[-1]
        p = trials[-1]
        lm_trial = lm_test = None

        # Define the full coupled form and split it into subforms depending
        # on the test and trial functions
        eq = define_dg_equations(
            u,
            v,
            p,
            q,
            lm_trial,
            lm_test,
            self.simulation,
            include_hydrostatic_pressure=self.include_hydrostatic_pressure,
            incompressibility_flux_type=self.incompressibility_flux_type,
            use_grad_q_form=self.use_grad_q_form,
            use_grad_p_form=self.use_grad_p_form,
            use_stress_divergence_form=self.use_stress_divergence_form,
        )
        mat, vec = split_form_into_matrix(eq,
                                          Vcoupled,
                                          Vcoupled,
                                          check_zeros=True)

        # Check matrix and vector shapes and that the matrix is a saddle point matrix
        assert mat.shape == (2, 2)
        assert vec.shape == (2, )
        assert mat[
            -1,
            -1] is None, 'Found p-q coupling, this is not a saddle point system!'

        # Store the forms
        self.eqA = mat[0, 0]
        self.eqB = mat[0, 1]
        self.eqC = mat[1, 0]
        self.eqD = vec[0]
        self.eqE = vec[1]

        if self.eqE is None:
            self.eqE = dolfin.TrialFunction(Vp) * dolfin.Constant(
                0) * dolfin.dx

        if self.a_tilde_is_mass:
            # The mass matrix. Consistent with the implementation in define_dg_equations
            rho = sim.multi_phase_model.get_density(0)
            c1 = sim.data['time_coeffs'][0]
            dt = sim.data['dt']
            eqM = rho * c1 / dt * dolfin.dot(u, v) * dolfin.dx
            matM, _vecM = split_form_into_matrix(eqM,
                                                 Vcoupled,
                                                 Vcoupled,
                                                 check_zeros=True)
            self.eqM = dolfin.Form(matM[0, 0])
            self.M = None
예제 #8
0
 def to_dolfin_cpp_form(a):
     if isinstance(a, ufl.form.Form):
         return dolfin.Form(a)
     return a
예제 #9
0
    def define_weak_forms(self):
        sim = self.simulation
        self.Vuvw = sim.data['uvw_star'].function_space()
        Vp = sim.data['Vp']

        # The trial and test functions in a coupled space (to be split)
        func_spaces = [self.Vuvw, Vp]
        e_mixed = dolfin.MixedElement([fs.ufl_element() for fs in func_spaces])
        Vcoupled = dolfin.FunctionSpace(sim.data['mesh'], e_mixed)
        tests = dolfin.TestFunctions(Vcoupled)
        trials = dolfin.TrialFunctions(Vcoupled)

        # Split into components
        v = dolfin.as_vector(tests[0][:])
        u = dolfin.as_vector(trials[0][:])
        q = tests[-1]
        p = trials[-1]
        lm_trial = lm_test = None

        # Define the full coupled form and split it into subforms depending
        # on the test and trial functions
        eq = define_dg_equations(
            u,
            v,
            p,
            q,
            lm_trial,
            lm_test,
            self.simulation,
            include_hydrostatic_pressure=self.hydrostatic_pressure.
            every_timestep,
            incompressibility_flux_type=self.incompressibility_flux_type,
            use_grad_q_form=self.use_grad_q_form,
            use_grad_p_form=self.use_grad_p_form,
            use_stress_divergence_form=self.use_stress_divergence_form,
        )
        sim.log.info('    Splitting coupled form')
        mat, vec = split_form_into_matrix(eq,
                                          Vcoupled,
                                          Vcoupled,
                                          check_zeros=True)

        # Check matrix and vector shapes and that the matrix is a saddle point matrix
        assert mat.shape == (2, 2)
        assert vec.shape == (2, )
        assert mat[
            -1,
            -1] is None, 'Found p-q coupling, this is not a saddle point system!'

        # Compile and store the forms
        sim.log.info('    Compiling IPCS-A forms')
        self.eqA = dolfin.Form(mat[0, 0])
        self.eqB = dolfin.Form(mat[0, 1])
        self.eqC = dolfin.Form(mat[1, 0])
        self.eqD = dolfin.Form(vec[0])
        self.eqE = dolfin.Form(vec[1]) if vec[1] is not None else None

        # The mass matrix. Consistent with the implementation in define_dg_equations
        if self.splitting_approximation == SPLIT_APPROX_MASS_WITH_RHO:
            rho_for_M = sim.multi_phase_model.get_density(0)
        elif self.splitting_approximation == SPLIT_APPROX_MASS_MIN_RHO:
            min_rho, _max_rho = sim.multi_phase_model.get_density_range()
            rho_for_M = dolfin.Constant(min_rho)
        else:
            rho_for_M = None
        if rho_for_M is not None:
            c1 = sim.data['time_coeffs'][0]
            dt = sim.data['dt']
            eqM = rho_for_M * c1 / dt * dolfin.dot(u, v) * dolfin.dx
            matM, _vecM = split_form_into_matrix(eqM,
                                                 Vcoupled,
                                                 Vcoupled,
                                                 check_zeros=True)
            self.eqM = dolfin.Form(matM[0, 0])

        # The mass matrix without density
        if self.make_unscaled_M:
            u2 = dolfin.as_vector(dolfin.TrialFunction(self.Vuvw)[:])
            v2 = dolfin.as_vector(dolfin.TestFunction(self.Vuvw)[:])
            a = dolfin.dot(u2, v2) * dolfin.dx
            Mus = assemble_into(a, None)
            self.M_unscaled_inv = invert_block_diagonal_matrix(self.Vuvw, Mus)
예제 #10
0
    def define_advection_equation(self):
        """
        Setup the advection equation for the colour function

        This implementation assembles the full LHS and RHS each time they are needed
        """
        sim = self.simulation
        mesh = sim.data['mesh']
        n = dolfin.FacetNormal(mesh)
        dS, dx = dolfin.dS(mesh), dolfin.dx(mesh)

        # Trial and test functions
        Vc = self.Vc
        c = dolfin.TrialFunction(Vc)
        d = dolfin.TestFunction(Vc)

        c1, c2, c3 = self.time_coeffs
        dt = self.dt
        u_conv = self.u_conv

        if not self.colour_is_discontinuous:
            # Continous Galerkin implementation of the advection equation
            # FIXME: add stabilization
            eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx + div(
                c * u_conv) * d * dx

        elif self.velocity_is_trace:
            # Upstream and downstream normal velocities
            w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2

            if self.beta is not None:
                # Define the blended flux
                # The blending factor beta is not DG, so beta('+') == beta('-')
                b = self.beta('+')
                flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD)
            else:
                flux = jump(c * w_nU)

            # Discontinuous Galerkin implementation of the advection equation
            eq = (c1 * c + c2 * self.cp +
                  c3 * self.cpp) / dt * d * dx + flux * jump(d) * dS

            # On each facet either w_nD or w_nU will be 0, the other is multiplied
            # with the appropriate flux, either the value c going out of the domain
            # or the Dirichlet value coming into the domain
            for dbc in self.dirichlet_bcs:
                eq += w_nD * dbc.func() * d * dbc.ds()
                eq += w_nU * c * d * dbc.ds()

        elif self.beta is not None:
            # Upstream and downstream normal velocities
            w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2

            if self.beta is not None:
                # Define the blended flux
                # The blending factor beta is not DG, so beta('+') == beta('-')
                b = self.beta('+')
                flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD)
            else:
                flux = jump(c * w_nU)

            # Discontinuous Galerkin implementation of the advection equation
            eq = ((c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx -
                  dot(c * u_conv, grad(d)) * dx + flux * jump(d) * dS)

            # Enforce Dirichlet BCs weakly
            for dbc in self.dirichlet_bcs:
                eq += w_nD * dbc.func() * d * dbc.ds()
                eq += w_nU * c * d * dbc.ds()

        else:
            # Downstream normal velocities
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2

            # Discontinuous Galerkin implementation of the advection equation
            eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx

            # Convection integrated by parts two times to bring back the original
            # div form (this means we must subtract and add all fluxes)
            eq += div(c * u_conv) * d * dx

            # Replace downwind flux with upwind flux on downwind internal facets
            eq -= jump(w_nD * d) * jump(c) * dS

            # Replace downwind flux with upwind BC flux on downwind external facets
            for dbc in self.dirichlet_bcs:
                # Subtract the "normal" downwind flux
                eq -= w_nD * c * d * dbc.ds()
                # Add the boundary value upwind flux
                eq += w_nD * dbc.func() * d * dbc.ds()

        # Penalty forcing zones
        for fz in self.forcing_zones:
            eq += fz.penalty * fz.beta * (c - fz.target) * d * dx

        a, L = dolfin.system(eq)
        self.form_lhs = dolfin.Form(a)
        self.form_rhs = dolfin.Form(L)
        self.tensor_lhs = None
        self.tensor_rhs = None