Пример #1
0
def df_wrap(val, description, degree, sim):
    """
    Wrap numbers as dolfin.Constant and strings as
    dolfin.Expression C++ code. Lists must be ndim
    long and contain either numbers or strings
    """
    if isinstance(val, (int, float)):
        # A real number
        return dolfin.Constant(val)
    elif isinstance(val, str):
        # A C++ code string
        return OcellarisCppExpression(sim, val, description, degree)
    elif isinstance(val, (list, tuple)):
        D = sim.ndim
        L = len(val)
        if L != D:
            raise OcellarisError(
                'Invalid length of list',
                'BC list in "%r" must be length %d, is %d.' %
                (description, D, L),
            )

        if all(isinstance(v, str) for v in val):
            # A list of C++ code strings
            return OcellarisCppExpression(sim, val, description, degree)
        else:
            # A mix of constants and (possibly) C++ strings
            val = [
                df_wrap(v, description + ' item %d' % i, degree, sim)
                for i, v in enumerate(val)
            ]
            return dolfin.as_vector(val)
Пример #2
0
 def get_variable(self, name):
     if not name == self.var_name:
         raise OcellarisError(
             'FreeSurfaceZone field does not define %r' % name,
             'This FreeSurfaceZone field defines %r' % self.var_name,
         )
     return self.function
Пример #3
0
    def update(self, timestep_number, t, dt):
        """
        Update the mesh position according to the calculated fluid velocities
        """
        sim = self.simulation
        hfunc = sim.data['height_function']
        xpos = sim.data['height_function_x']
        Nx = xpos.size
        old_h = hfunc.vector().get_local()

        with dolfin.Timer('Ocellaris update height and colour'):
            # Get updated mesh velocity in each xpos
            all_vel = numpy.zeros_like(xpos)
            vel = numpy.zeros(1, float)
            pos = numpy.zeros(2, float)
            uvert = sim.data['u1']
            for i, x in enumerate(xpos):
                pos[:] = (x, old_h[i])
                try:
                    uvert.eval(vel, pos)
                except Exception:
                    raise OcellarisError(
                        'Error in u1 eval in height function',
                        'Position %r outside the domain?' % (pos, ),
                    )
                all_vel[i] = vel[0]

            # Get height function max and min
            hmin = self.hmin.run(xpos=xpos)
            hmax = self.hmax.run(xpos=xpos)
            assert hmin.shape == xpos.shape
            assert hmax.shape == xpos.shape

            # Update the height function
            dt = sim.input.get_value('time/dt', required_type='float')
            old_h[:Nx] += dt * all_vel
            old_h[:Nx] = numpy.clip(old_h[:Nx], hmin, hmax)

            hfunc.vector().set_local(old_h)
            hfunc.vector().apply('insert')

            self._update_colour()

        # Report on the new height function
        self.simulation.reporting.report_timestep_value(
            'min(h)', old_h[:Nx].min())
        self.simulation.reporting.report_timestep_value(
            'max(h)', old_h[:Nx].max())
        self.simulation.hooks.run_custom_hook('MultiPhaseModelUpdated')
Пример #4
0
    def __init__(self, simulation, var_name, inp_dict, subdomains,
                 subdomain_id):
        """
        Free slip boundary condition
        """
        if var_name[-1] in '0123456789':
            raise OcellarisError(
                'Error in FreeSlip boundary conditions',
                'You must give the name of a vector field, like "u", '
                'not a component. You gave %r' % var_name,
            )

        # Store the boundary condition for use in the solver
        bc = FreeSlipBC(simulation, subdomain_id)
        bcs = simulation.data['slip_bcs']
        bcs.setdefault(var_name, []).append(bc)

        simulation.log.info('    FreeSlipBC for %s' % var_name)
Пример #5
0
    def __init__(self, simulation, var_name, inp_dict, subdomains, subdomain_id):
        """
        Open outlet boundary condition
        """
        self.simulation = simulation
        hydrostatic = inp_dict.get_value('hydrostatic', False, 'bool')

        if not var_name == 'u':
            raise OcellarisError(
                'Error in boundary condition',
                'OpenOutletBoundary should be applied to ' '"u" only, p should be left out',
            )

        # Store the boundary condition for use in the solver
        bc = OcellarisOutletBC(self.simulation, subdomain_id)
        bc.hydrostatic = hydrostatic
        self.simulation.data['outlet_bcs'].append(bc)

        self.simulation.log.info('    OpenOutletBoundary for %s' % var_name)
Пример #6
0
def get_facet_dof_indices(V):
    """
    Get local indices for the facet dofs for each facet in the cell
    """
    ndim = V.mesh().topology().dim()
    degree = V.ufl_element().degree()
    ndim_deg = (ndim, degree)

    if ndim_deg == (2, 1):
        # Linear triangle
        facet_dof_indices = numpy.zeros((3, 2), dtype=int)
        facet_dof_indices[0, :] = (1, 2)
        facet_dof_indices[1, :] = (0, 2)
        facet_dof_indices[2, :] = (0, 1)
    elif ndim_deg == (2, 2):
        # Quadratic triangle
        facet_dof_indices = numpy.zeros((3, 3), dtype=int)
        facet_dof_indices[0, :] = (1, 2, 3)
        facet_dof_indices[1, :] = (0, 2, 4)
        facet_dof_indices[2, :] = (0, 1, 5)
    elif ndim_deg == (3, 1):
        # Linear tetrahedron
        facet_dof_indices = numpy.zeros((4, 3), dtype=int)
        facet_dof_indices[0, :] = (1, 2, 3)
        facet_dof_indices[1, :] = (0, 2, 3)
        facet_dof_indices[2, :] = (0, 1, 3)
        facet_dof_indices[3, :] = (0, 1, 2)
    elif ndim_deg == (3, 2):
        # Quadratic tetrahedron
        facet_dof_indices = numpy.zeros((4, 6), dtype=int)
        facet_dof_indices[0, :] = (1, 2, 3, 4, 5, 6)
        facet_dof_indices[1, :] = (0, 2, 3, 4, 7, 8)
        facet_dof_indices[2, :] = (0, 1, 3, 5, 7, 9)
        facet_dof_indices[3, :] = (0, 1, 2, 6, 8, 9)
    else:
        raise OcellarisError(
            'Unsupported element ndim=%d degree=%d' % ndim_deg,
            'The boundary condition get_dof_region_marks '
            'code does not support this element',
        )

    return facet_dof_indices
Пример #7
0
    def register_dirichlet_condition(self, var_name, value, subdomains,
                                     subdomain_id):
        """
        Add a Dirichlet condition to this variable
        """
        if not isinstance(value, (float, int)):
            raise OcellarisError(
                'Error in ConstantValue BC for %s' % var_name,
                'The value %r is not a number' % value,
            )
        df_value = dolfin.Constant(value)

        # Store the boundary condition for use in the solver
        bc = OcellarisDirichletBC(self.simulation, self.func_space, df_value,
                                  subdomains, subdomain_id)
        bcs = self.simulation.data['dirichlet_bcs']
        bcs.setdefault(var_name, []).append(bc)

        self.simulation.log.info('    Constant value %r for %s' %
                                 (value, var_name))
Пример #8
0
    def _run_cpp(
        self,
        taylor_arr,
        taylor_arr_old,
        alpha_arrs,
        global_min,
        global_max,
        boundary_dof_type,
        boundary_dof_value,
    ):
        """
        Run the C++ implementation of the HierarchicalTaylor slope limiter

        The C++ versions are probably the best tested since they are fastest
        and hence most used
        """
        funcs = {
            (2, 1): self.cpp_mod.hierarchical_taylor_slope_limiter_dg1_2D,
            (2, 2): self.cpp_mod.hierarchical_taylor_slope_limiter_dg2_2D,
            (3, 1): self.cpp_mod.hierarchical_taylor_slope_limiter_dg1_3D,
            (3, 2): self.cpp_mod.hierarchical_taylor_slope_limiter_dg2_3D,
        }
        key = (self.ndim, self.degree)
        if key not in funcs:
            raise OcellarisError(
                'Unsupported dimension %d with degree %d' % key,
                'Not supported in C++ version of the HierarchalTaylor limiter',
            )

        # Update C++ input
        inp = self.input
        inp.set_global_bounds(global_min, global_max)
        inp.set_limit_cell(self.limit_cell)
        inp.set_boundary_values(boundary_dof_type, boundary_dof_value,
                                self.enforce_boundary_conditions)

        limiter = funcs[key]
        limiter(inp.cpp_obj, taylor_arr, taylor_arr_old, *alpha_arrs)
Пример #9
0
    def __init__(self, simulation):
        """
        A simple height function multiphase model
        """
        self.simulation = simulation
        simulation.log.info('Creating height function multiphase model')
        assert simulation.ncpu == 1, 'HeightFunctionMultiphaseModel does not run in parallel'

        # Define colour function
        V = simulation.data['Vc']
        simulation.data['c'] = dolfin.Function(V)

        # The free surface mesh
        xmin = simulation.input.get_value(
            'multiphase_solver/height_function_xmin', required_type='float')
        xmax = simulation.input.get_value(
            'multiphase_solver/height_function_xmax', required_type='float')
        Nx = simulation.input.get_value('multiphase_solver/height_function_Nx',
                                        required_type='float')
        hinit = simulation.input.get_value(
            'multiphase_solver/height_initial_h', required_type='float')
        self.eps = simulation.input.get_value(
            'multiphase_solver/surface_thickness', required_type='float')
        hmin_code = simulation.input.get_value(
            'multiphase_solver/height_min_code', required_type='string')
        hmax_code = simulation.input.get_value(
            'multiphase_solver/height_max_code', required_type='string')

        # Runnable code, must define 'hmin' or 'hmax' arrays given input xpos array
        self.hmin = RunnablePythonString(simulation, hmin_code,
                                         'multiphase_solver/height_min_code',
                                         'hmin')
        self.hmax = RunnablePythonString(simulation, hmax_code,
                                         'multiphase_solver/height_max_code',
                                         'hmax')

        # Store colour function dof coordinates
        mesh = simulation.data['mesh']
        gdim = mesh.geometry().dim()
        self.dof_pos = V.tabulate_dof_coordinates().reshape((-1, gdim))

        # Create dummy dolfin Function to hold the height function (for restart file support)
        if Nx > V.dim():
            raise OcellarisError(
                'height_function_Nx too large',
                'Maximum Nx for this colour function is %d' % V.dim(),
            )
        simulation.data['height_function'] = hfunc = dolfin.Function(V)
        hfunc.vector()[:] = hinit
        hfunc.vector().apply('insert')
        simulation.data['height_function_x'] = numpy.linspace(xmin, xmax, Nx)
        self._update_colour()
        simulation.log.info('    Created surface mesh with %d elements' %
                            (Nx - 1))

        # Get the physical properties
        self.set_physical_properties(read_input=True)

        # Update the rho and nu fields before each time step
        simulation.hooks.add_pre_timestep_hook(
            self.update, 'HeightFunctionMultiphaseModel.update()')
        simulation.hooks.register_custom_hook_point('MultiPhaseModelUpdated')
Пример #10
0
def mark_cell_layers(
    simulation, V, layers=0, dof_region_marks=None, named_boundaries=None
):
    """
    Return all cells on the boundary and all connected cells in a given
    number of layers surrounding the boundary cells. Vertex neighbours
    are used to determine a cells neighbours.

    The initial list of cells is taken from a dictionary mapping dof to
    boundary region number. All cells containing these dofs are taken as
    the zeroth layer. If no such dictionary is provided then
    get_dof_region_marks(simulation, V) is used, hence the cells
    containing the boundary facing facet are used as the zeroth level.

    If named_boundaries is given then only cells on the given named
    boundaries are included in the zeroth layer. The special name 'all'
    will include all boundary regions.

    @return: set of the cell numbers of marked cells
    """
    if named_boundaries is None:
        named_boundaries = ['all']
    if not named_boundaries:
        return set()
    if dof_region_marks is None:
        dof_region_marks = get_dof_region_marks(simulation, V)

    # Get all regions with names
    all_regions = {region.name: region.index for region in simulation.data['boundary']}
    all_idxs = {region.index: region.name for region in simulation.data['boundary']}

    # Verify that the boundary names are unique
    assert len(all_regions) == len(simulation.data['boundary'])
    assert len(all_regions) == len(all_idxs)

    # Check that all named regions correspond to boundary regions
    for rname in named_boundaries:
        if rname != 'all' and rname not in all_regions:
            raise OcellarisError(
                'Unknown boundary region in input',
                '%r is not a boundary region' % rname,
            )

    # Names of all boundary regions
    if 'all' not in named_boundaries:
        to_keep = [mark for mark, name in all_idxs.items() if name in named_boundaries]

        # Remove marks to unwanted bounbdary regions
        drm = {
            dof: [mark for mark in dof_marks if mark in to_keep]
            for dof, dof_marks in dof_region_marks.items()
        }

        # Remove dofs wih empty region mark list
        dof_region_marks = {k: v for k, v in drm.items() if v}

    # Mark initial zeroth layer cells
    mesh = simulation.data['mesh']
    dm = V.dofmap()
    boundary_cells = set()
    for cell in dolfin.cells(mesh):
        dofs = dm.cell_dofs(cell.index())
        for dof in dofs:
            if dof in dof_region_marks:
                boundary_cells.add(cell.index())
                continue

    # Iteratively mark cells adjacent to boundary cells
    for _ in range(layers):
        boundary_cells_old = set(boundary_cells)
        for cell_index in boundary_cells_old:
            vertices = simulation.data['connectivity_CV'](cell_index)
            for vert_index in vertices:
                for nb in simulation.data['connectivity_VC'](vert_index):
                    boundary_cells.add(nb)

    return boundary_cells
Пример #11
0
    def run(self, use_weak_bcs=None):
        """
        Perform slope limiting of DG Lagrange functions
        """
        # No limiter needed for piecewice constant functions
        if self.degree == 0:
            return
        timer = df.Timer('Ocellaris HierarchalTaylorSlopeLimiter')

        # Update the Taylor function with the current Lagrange values
        lagrange_to_taylor(self.phi, self.taylor)
        taylor_arr = get_local(self.taylor)
        alpha_arrs = [alpha.vector().get_local() for alpha in self.alpha_funcs]

        # Get global bounds, see SlopeLimiterBase.set_initial_field()
        global_min, global_max = self.global_bounds

        # Update previous field values Taylor functions
        if self.phi_old is not None:
            lagrange_to_taylor(self.phi_old, self.taylor_old)
            taylor_arr_old = get_local(self.taylor_old)
        else:
            taylor_arr_old = taylor_arr

        # Get updated boundary conditions
        weak_vals = None
        use_weak_bcs = self.use_weak_bcs if use_weak_bcs is None else use_weak_bcs
        if use_weak_bcs:
            weak_vals = self.phi.vector().get_local()
        boundary_dof_type, boundary_dof_value = self.boundary_conditions.get_bcs(
            weak_vals)

        # Run the limiter implementation
        if self.use_cpp:
            self._run_cpp(
                taylor_arr,
                taylor_arr_old,
                alpha_arrs,
                global_min,
                global_max,
                boundary_dof_type,
                boundary_dof_value,
            )
        elif self.degree == 1 and self.ndim == 2:
            self._run_dg1(
                taylor_arr,
                taylor_arr_old,
                alpha_arrs[0],
                global_min,
                global_max,
                boundary_dof_type,
                boundary_dof_value,
            )
        elif self.degree == 2 and self.ndim == 2:
            self._run_dg2(
                taylor_arr,
                taylor_arr_old,
                alpha_arrs[0],
                alpha_arrs[1],
                global_min,
                global_max,
                boundary_dof_type,
                boundary_dof_value,
            )
        else:
            raise OcellarisError(
                'Unsupported dimension for Python version of the HierarchalTaylor limiter',
                'Only 2D is supported',
            )

        # Update the Lagrange function with the limited Taylor values
        set_local(self.taylor, taylor_arr, apply='insert')
        taylor_to_lagrange(self.taylor, self.phi)

        # Enforce boundary conditions
        if self.enforce_boundary_conditions:
            has_dbc = boundary_dof_type == self.boundary_conditions.BC_TYPE_DIRICHLET
            vals = self.phi.vector().get_local()
            vals[has_dbc] = boundary_dof_value[has_dbc]
            self.phi.vector().set_local(vals)
            self.phi.vector().apply('insert')

        # Update the secondary output arrays, alphas
        for alpha, alpha_arr in zip(self.alpha_funcs, alpha_arrs):
            alpha.vector().set_local(alpha_arr)
            alpha.vector().apply('insert')

        timer.stop()