Beispiel #1
0
 def __init__(self,
              operator,
              source_basis,
              range_basis,
              product=None,
              copy=True,
              name=None):
     assert isinstance(operator, OperatorInterface)
     assert source_basis is None and issubclass(operator.source.type, NumpyVectorArray) \
         or source_basis in operator.source
     assert range_basis is None and issubclass(operator.range.type, NumpyVectorArray) \
         or range_basis in operator.range
     assert product is None \
         or (isinstance(product, OperatorInterface)
             and range_basis is not None
             and operator.range == product.source
             and product.range == product.source)
     self.build_parameter_type(inherits=(operator, ))
     self.source = NumpyVectorSpace(
         len(source_basis) if source_basis is not None else operator.source.
         dim)
     self.range = NumpyVectorSpace(
         len(range_basis) if range_basis is not None else operator.range.dim
     )
     self.name = name
     self.operator = operator
     self.source_basis = source_basis.copy(
     ) if source_basis is not None and copy else source_basis
     self.range_basis = range_basis.copy(
     ) if range_basis is not None and copy else range_basis
     self.linear = operator.linear
     self.product = product
Beispiel #2
0
 def __init__(self, matrix, name=None):
     assert matrix.ndim <= 2
     if matrix.ndim == 1:
         matrix = np.reshape(matrix, (1, -1))
     self.source = NumpyVectorSpace(matrix.shape[1])
     self.range = NumpyVectorSpace(matrix.shape[0])
     self.name = name
     self._matrix = matrix
     self.sparse = issparse(matrix)
     self.calculate_sid = hasattr(matrix, 'sid')
Beispiel #3
0
 def __init__(self, array, transposed=False, copy=True, name=None):
     self._array = array.copy() if copy else array
     if transposed:
         self.source = array.space
         self.range = NumpyVectorSpace(len(array))
     else:
         self.source = NumpyVectorSpace(len(array))
         self.range = array.space
     self.transposed = transposed
     self.name = name
Beispiel #4
0
 def __init__(self,
              mapping,
              dim_source=1,
              dim_range=1,
              linear=False,
              parameter_type=None,
              name=None):
     self.source = NumpyVectorSpace(dim_source)
     self.range = NumpyVectorSpace(dim_range)
     self.name = name
     self._mapping = mapping
     self.linear = linear
     if parameter_type is not None:
         self.build_parameter_type(parameter_type, local_global=True)
Beispiel #5
0
class VectorOperator(VectorArrayOperator):
    """Wrap a vector as a vector-like |Operator|.

    Given a vector `v` of dimension `d`, this class represents
    the operator ::

        op: R^1 ----> R^d
             x  |---> x⋅v

    In particular ::

        VectorOperator(vector).as_vector() == vector

    Parameters
    ----------
    vector
        |VectorArray| of length 1 containing the vector `v`.
    copy
        If `True`, store a copy of `vector` instead of `vector`
        itself.
    name
        Name of the operator.
    """

    linear = True
    source = NumpyVectorSpace(1)

    def __init__(self, vector, copy=True, name=None):
        assert isinstance(vector, VectorArrayInterface)
        assert len(vector) == 1
        super(VectorOperator, self).__init__(vector,
                                             transposed=False,
                                             copy=copy,
                                             name=name)
Beispiel #6
0
 def projected(self, source_basis, range_basis, product=None, name=None):
     assert source_basis is None or source_basis in self.source
     assert range_basis is None or range_basis in self.range
     assert product is None or product.source == product.range == self.range
     if source_basis is not None and range_basis is not None:
         return NumpyMatrixOperator(np.zeros(
             (len(range_basis), len(source_basis))),
                                    name=self.name + '_projected')
     else:
         new_source = NumpyVectorSpace(
             len(source_basis)) if source_basis is not None else self.source
         new_range = NumpyVectorSpace(
             len(range_basis)) if range_basis is not None else self.source
         return ZeroOperator(new_source,
                             new_range,
                             name=self.name + '_projected')
Beispiel #7
0
 def __init__(self, grid, function):
     assert function.dim_domain == grid.dim_outer
     assert function.shape_range == tuple()
     self.grid = grid
     self.function = function
     self.range = NumpyVectorSpace(grid.size(grid.dim))
     self.build_parameter_type(inherits=(function,))
Beispiel #8
0
 def projected(self, source_basis, range_basis, product=None, name=None):
     assert source_basis is None or source_basis in self.source
     assert range_basis is None or range_basis in self.range
     assert product is None or product.source == product.range == self.range
     if range_basis is not None:
         if product:
             projected_value = NumpyVectorArray(product.apply2(
                 range_basis, self._value, pairwise=False).T,
                                                copy=False)
         else:
             projected_value = NumpyVectorArray(range_basis.dot(
                 self._value, pairwise=False).T,
                                                copy=False)
     else:
         projected_value = self._value
     if source_basis is None:
         return ConstantOperator(projected_value,
                                 self.source,
                                 copy=False,
                                 name=self.name + '_projected')
     else:
         return ConstantOperator(projected_value,
                                 NumpyVectorSpace(len(source_basis)),
                                 copy=False,
                                 name=self.name + '_projected')
Beispiel #9
0
 def __init__(self, grid, boundary_info, velocity_field, lxf_lambda=1.0, name=None):
     self.grid = grid
     self.boundary_info = boundary_info
     self.velocity_field = velocity_field
     self.lxf_lambda = lxf_lambda
     self.name = name
     self.build_parameter_type(inherits=(velocity_field,))
     self.source = self.range = NumpyVectorSpace(grid.size(0))
Beispiel #10
0
 def __init__(self, grid, boundary_info, dirichlet_clear_rows=True, dirichlet_clear_columns=False,
              dirichlet_clear_diag=False, name=None):
     assert grid.reference_element in {square}
     self.source = self.range = NumpyVectorSpace(grid.size(grid.dim))
     self.grid = grid
     self.boundary_info = boundary_info
     self.dirichlet_clear_rows = dirichlet_clear_rows
     self.dirichlet_clear_columns = dirichlet_clear_columns
     self.dirichlet_clear_diag = dirichlet_clear_diag
     self.name = name
Beispiel #11
0
class GenericOperator(OperatorBase):

    source = range = NumpyVectorSpace(10)
    op = NumpyMatrixOperator(np.eye(10) * np.arange(1, 11))
    linear = True

    def apply(self, U, ind=None, mu=None):
        return self.op.apply(U, ind=ind, mu=mu)

    def apply_adjoint(self, U, ind=None, mu=None):
        return self.op.apply_adjoint(U, ind=ind, mu=mu)
Beispiel #12
0
 def __init__(self, grid, function, boundary_info=None, dirichlet_data=None, neumann_data=None, order=2, name=None):
     assert grid.reference_element(0) in {line, triangle}
     assert function.shape_range == tuple()
     self.source = NumpyVectorSpace(grid.size(grid.dim))
     self.grid = grid
     self.boundary_info = boundary_info
     self.function = function
     self.dirichlet_data = dirichlet_data
     self.neumann_data = neumann_data
     self.order = order
     self.name = name
     self.build_parameter_type(inherits=(function, dirichlet_data, neumann_data))
Beispiel #13
0
 def __init__(self, restricted_operator, interpolation_matrix, source_basis_dofs,
              projected_collateral_basis, triangular, name=None):
     self.source = NumpyVectorSpace(len(source_basis_dofs))
     self.range = projected_collateral_basis.space
     self.linear = restricted_operator.linear
     self.build_parameter_type(inherits=(restricted_operator,))
     self.restricted_operator = restricted_operator
     self.interpolation_matrix = interpolation_matrix
     self.source_basis_dofs = source_basis_dofs
     self.projected_collateral_basis = projected_collateral_basis
     self.triangular = triangular
     self.name = name or '{}_projected'.format(restricted_operator.name)
Beispiel #14
0
class VectorFunctional(VectorArrayOperator):
    """Wrap a vector as a linear |Functional|.

    Given a vector `v` of dimension `d`, this class represents
    the functional ::

        f: R^d ----> R^1
            u  |---> (u, v)

    where `( , )` denotes the scalar product given by `product`.

    In particular, if `product` is `None` ::

        VectorFunctional(vector).as_vector() == vector.

    If `product` is not none, we obtain ::

        VectorFunctional(vector).as_vector() == product.apply(vector).

    Parameters
    ----------
    vector
        |VectorArray| of length 1 containing the vector `v`.
    product
        |Operator| representing the scalar product to use.
    copy
        If `True`, store a copy of `vector` instead of `vector`
        itself.
    name
        Name of the operator.
    """

    linear = True
    range = NumpyVectorSpace(1)

    def __init__(self, vector, product=None, copy=True, name=None):
        assert isinstance(vector, VectorArrayInterface)
        assert len(vector) == 1
        assert product is None or isinstance(
            product, OperatorInterface) and vector in product.source
        if product is None:
            super(VectorFunctional, self).__init__(vector,
                                                   transposed=True,
                                                   copy=copy,
                                                   name=name)
        else:
            super(VectorFunctional, self).__init__(product.apply(vector),
                                                   transposed=True,
                                                   copy=False,
                                                   name=name)
Beispiel #15
0
 def __init__(self, grid, function=None, boundary_info=None, dirichlet_data=None, diffusion_function=None,
              diffusion_constant=None, neumann_data=None, order=1, name=None):
     assert function is None or function.shape_range == tuple()
     self.source = NumpyVectorSpace(grid.size(0))
     self.grid = grid
     self.boundary_info = boundary_info
     self.function = function
     self.dirichlet_data = dirichlet_data
     self.diffusion_function = diffusion_function
     self.diffusion_constant = diffusion_constant
     self.neumann_data = neumann_data
     self.order = order
     self.name = name
     self.build_parameter_type(inherits=(function, dirichlet_data, diffusion_function, neumann_data))
Beispiel #16
0
 def __init__(self, grid, boundary_info, diffusion_function=None, diffusion_constant=None, name=None):
     super(DiffusionOperator, self).__init__()
     assert isinstance(grid, AffineGridWithOrthogonalCentersInterface)
     assert diffusion_function is None \
         or (isinstance(diffusion_function, FunctionInterface) and
             diffusion_function.dim_domain == grid.dim_outer and
             diffusion_function.shape_range == tuple())
     self.grid = grid
     self.boundary_info = boundary_info
     self.diffusion_function = diffusion_function
     self.diffusion_constant = diffusion_constant
     self.name = name
     self.source = self.range = NumpyVectorSpace(grid.size(0))
     if diffusion_function is not None:
         self.build_parameter_type(inherits=(diffusion_function,))
Beispiel #17
0
    def __init__(self, grid, boundary_info, numerical_flux, dirichlet_data=None, name=None):
        assert dirichlet_data is None or isinstance(dirichlet_data, FunctionInterface)

        self.grid = grid
        self.boundary_info = boundary_info
        self.numerical_flux = numerical_flux
        self.dirichlet_data = dirichlet_data
        self.name = name
        if (isinstance(dirichlet_data, FunctionInterface) and boundary_info.has_dirichlet
                and not dirichlet_data.parametric):
            self._dirichlet_values = self.dirichlet_data(grid.centers(1)[boundary_info.dirichlet_boundaries(1)])
            self._dirichlet_values = self._dirichlet_values.ravel()
            self._dirichlet_values_flux_shaped = self._dirichlet_values.reshape((-1, 1))
        self.build_parameter_type(inherits=(numerical_flux, dirichlet_data))
        self.source = self.range = NumpyVectorSpace(grid.size(0))
        self.with_arguments = self.with_arguments.union('numerical_flux_{}'.format(arg)
                                                        for arg in numerical_flux.with_arguments)
Beispiel #18
0
 def __init__(self, grid, boundary_info, diffusion_function=None, diffusion_constant=None,
              dirichlet_clear_columns=False, dirichlet_clear_diag=False, name=None):
     assert grid.reference_element(0) in {square}, 'A square grid is expected!'
     assert diffusion_function is None \
         or (isinstance(diffusion_function, FunctionInterface) and
             diffusion_function.dim_domain == grid.dim_outer and
             diffusion_function.shape_range == tuple() or diffusion_function.shape_range == (grid.dim_outer,) * 2)
     self.source = self.range = NumpyVectorSpace(grid.size(grid.dim))
     self.grid = grid
     self.boundary_info = boundary_info
     self.diffusion_constant = diffusion_constant
     self.diffusion_function = diffusion_function
     self.dirichlet_clear_columns = dirichlet_clear_columns
     self.dirichlet_clear_diag = dirichlet_clear_diag
     self.name = name
     if diffusion_function is not None:
         self.build_parameter_type(inherits=(diffusion_function,))
Beispiel #19
0
class MonomOperator(OperatorBase):
    source = range = NumpyVectorSpace(1)
    type_source = type_range = NumpyVectorArray

    def __init__(self, order, monom=None):
        self.monom = monom if monom else Polynomial(np.identity(order + 1)[order])
        assert isinstance(self.monom, Polynomial)
        self.order = order
        self.derivative = self.monom.deriv()
        self.linear = order == 1

    def apply(self, U, ind=None, mu=None):
        return NumpyVectorArray(self.monom(U.data))

    def jacobian(self, U, mu=None):
        return MonomOperator(self.order - 1, self.derivative)

    def apply_inverse(self, U, ind=None, mu=None, options=None):
        return NumpyVectorArray(1. / U.data)
Beispiel #20
0
class InterpolationOperator(NumpyMatrixBasedOperator):
    """Lagrange interpolation operator for continuous finite element spaces.

    Parameters
    ----------
    grid
        The |Grid| on which to interpolate.
    function
        The |Function| to interpolate.
    """

    source = NumpyVectorSpace(1)
    linear = True

    def __init__(self, grid, function):
        assert function.dim_domain == grid.dim_outer
        assert function.shape_range == tuple()
        self.grid = grid
        self.function = function
        self.range = NumpyVectorSpace(grid.size(grid.dim))
        self.build_parameter_type(inherits=(function,))

    def _assemble(self, mu=None):
        return self.function.evaluate(self.grid.centers(self.grid.dim), mu=mu).reshape((-1, 1))
Beispiel #21
0
def reduce_residual(operator, functional=None, RB=None, product=None, extends=None):
    """Generic reduced basis residual reductor.

    Given an operator and a functional, the concatenation of residual operator
    with the Riesz isomorphism is given by::

        riesz_residual.apply(U, mu)
            == product.apply_inverse(operator.apply(U, mu) - functional.as_vector(mu))

    This reductor determines a low-dimensional subspace of image of a reduced
    basis space under `riesz_residual`, computes an orthonormal basis `residual_range`
    of this range spaces and then returns the Petrov-Galerkin projection ::

        projected_riesz_residual
            === riesz_residual.projected(source_basis=RB, range_basis=residual_range)

    of the `riesz_residual` operator. Given an reduced basis coefficient vector `u`,
    the dual norm of the residual can then be computed as ::

        projected_riesz_residual.apply(u, mu).l2_norm()

    Moreover, a `reconstructor` is provided such that ::

        reconstructor.reconstruct(projected_riesz_residual.apply(u, mu))
            == riesz_residual.apply(RB.lincomb(u), mu)

    Parameters
    ----------
    operator
        See definition of `riesz_residual`.
    functional
        See definition of `riesz_residual`. If `None`, zero right-hand side is assumed.
    RB
        |VectorArray| containing a basis of the reduced space onto which to project.
    product
        Scalar product |Operator| w.r.t. which to compute the Riesz representatives.
    extends
        Set by :meth:`~pymor.algorithms.greedy.greedy` to the result of the
        last reduction in case the basis extension was `hierarchic`. Used to prevent
        re-computation of `residual_range` basis vectors already obtained from previous
        reductions.

    Returns
    -------
    projected_riesz_residual
        See above.
    reconstructor
        See above.
    reduction_data
        Additional data produced by the reduction process. (Compare the `extends` parameter.)
    """
    assert functional is None \
        or functional.range == NumpyVectorSpace(1) and functional.source == operator.source and functional.linear
    assert RB is None or RB in operator.source
    assert product is None or product.source == product.range == operator.range
    assert extends is None or len(extends) == 3

    logger = getLogger('pymor.reductors.reduce_residual')

    if RB is None:
        RB = operator.source.empty()

    if extends and isinstance(extends[0], NonProjectedResiudalOperator):
        extends = None
    if extends:
        residual_range = extends[1].RB.copy()
        residual_range_dims = list(extends[2]['residual_range_dims'])
        ind_range = range(extends[0].source.dim, len(RB))
    else:
        residual_range = operator.range.empty()
        residual_range_dims = []
        ind_range = range(-1, len(RB))

    class CollectionError(Exception):
        def __init__(self, op):
            super(CollectionError, self).__init__()
            self.op = op

    def collect_operator_ranges(op, ind, residual_range):
        if isinstance(op, LincombOperator):
            for o in op.operators:
                collect_operator_ranges(o, ind, residual_range)
        elif isinstance(op, EmpiricalInterpolatedOperator):
            if hasattr(op, 'collateral_basis') and ind == -1:
                residual_range.append(op.collateral_basis)
        elif op.linear and not op.parametric:
            if ind >= 0:
                residual_range.append(op.apply(RB, ind=ind))
        else:
            raise CollectionError(op)

    def collect_functional_ranges(op, residual_range):
        if isinstance(op, LincombOperator):
            for o in op.operators:
                collect_functional_ranges(o, residual_range)
        elif op.linear and not op.parametric:
            residual_range.append(op.as_vector())
        else:
            raise CollectionError(op)

    for i in ind_range:
        logger.info('Computing residual range for basis vector {}...'.format(i))
        new_residual_range = operator.range.empty()
        try:
            if i == -1:
                collect_functional_ranges(functional, new_residual_range)
            collect_operator_ranges(operator, i, new_residual_range)
        except CollectionError as e:
            logger.warn('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
            operator = operator.projected(RB, None)
            return (NonProjectedResiudalOperator(operator, functional, product),
                    NonProjectedReconstructor(product),
                    {})

        if product:
            logger.info('Computing Riesz representatives for basis vector {}...'.format(i))
            new_residual_range = product.apply_inverse(new_residual_range)

        gram_schmidt_offset = len(residual_range)
        residual_range.append(new_residual_range)
        logger.info('Orthonormalizing ...')
        gram_schmidt(residual_range, offset=gram_schmidt_offset, product=product, copy=False)
        residual_range_dims.append(len(residual_range))

    logger.info('Projecting ...')
    operator = operator.projected(RB, residual_range, product=None)  # the product always cancels out.
    functional = functional.projected(residual_range, None, product=None)

    return (ResidualOperator(operator, functional),
            GenericRBReconstructor(residual_range),
            {'residual_range_dims': residual_range_dims})
Beispiel #22
0
    def __init__(self,
                 T,
                 initial_data,
                 operator,
                 rhs=None,
                 mass=None,
                 time_stepper=None,
                 num_values=None,
                 products=None,
                 operators=None,
                 functionals=None,
                 vector_operators=None,
                 parameter_space=None,
                 estimator=None,
                 visualizer=None,
                 cache_region='disk',
                 name=None):
        functionals = functionals or {}
        operators = operators or {}
        vector_operators = vector_operators or {}
        if isinstance(initial_data, VectorArrayInterface):
            initial_data = VectorOperator(initial_data, name='initial_data')

        assert isinstance(initial_data, OperatorInterface)
        assert initial_data.source == NumpyVectorSpace(1)
        assert 'initial_data' not in vector_operators or initial_data == vector_operators[
            'initial_data']

        assert isinstance(operator, OperatorInterface)
        assert operator.source == operator.range == initial_data.range
        assert 'operator' not in operators or operator == operators['operator']

        assert rhs is None or isinstance(rhs, OperatorInterface) and rhs.linear
        assert rhs is None or rhs.source == operator.source and rhs.range.dim == 1
        assert 'rhs' not in functionals or rhs == functionals['rhs']

        assert mass is None or isinstance(mass,
                                          OperatorInterface) and mass.linear
        assert mass is None or mass.source == mass.range == operator.source
        assert 'mass' not in operators or mass == operators['mass']

        assert isinstance(time_stepper, TimeStepperInterface)
        assert all(f.source == operator.source for f in functionals.values())

        operators_with_operator_mass = {'operator': operator, 'mass': mass}
        operators_with_operator_mass.update(operators)

        functionals_with_rhs = {'rhs': rhs} if rhs else {}
        functionals_with_rhs.update(functionals)

        vector_operators_with_initial_data = {'initial_data': initial_data}
        vector_operators_with_initial_data.update(vector_operators)

        super(InstationaryDiscretization, self).__init__(
            operators=operators_with_operator_mass,
            functionals=functionals_with_rhs,
            vector_operators=vector_operators_with_initial_data,
            products=products,
            estimator=estimator,
            visualizer=visualizer,
            cache_region=cache_region,
            name=name)
        self.T = T
        self.solution_space = operator.source
        self.initial_data = initial_data
        self.operator = operator
        self.rhs = rhs
        self.mass = mass
        self.time_stepper = time_stepper
        self.num_values = num_values
        self.build_parameter_type(inherits=(initial_data, operator, rhs, mass),
                                  provides={'_t': 0})
        self.parameter_space = parameter_space

        if hasattr(time_stepper, 'nt'):
            self.with_arguments = self.with_arguments.union(
                {'time_stepper_nt'})
Beispiel #23
0
 def __init__(self, grid, name=None):
     self.source = self.range = NumpyVectorSpace(grid.size(0))
     self.grid = grid
     self.name = name
Beispiel #24
0
class L2ProductFunctional(NumpyMatrixBasedOperator):
    """Finite volume |Functional| representing the scalar product with an L2-|Function|.

    Additionally boundary conditions can be enforced by providing `dirichlet_data`
    and `neumann_data` functions.

    Parameters
    ----------
    grid
        |Grid| for which to assemble the functional.
    function
        The |Function| with which to take the scalar product or `None`.
    boundary_info
        |BoundaryInfo| determining the Dirichlet and Neumann boundaries or `None`.
        If `None`, no boundary treatment is performed.
    dirichlet_data
        |Function| providing the Dirichlet boundary values. If `None`,
        constant-zero boundary is assumed.
    diffusion_function
        See :class:`DiffusionOperator`. Has to be specified in case `dirichlet_data`
        is given.
    diffusion_constant
        See :class:`DiffusionOperator`. Has to be specified in case `dirichlet_data`
        is given.
    neumann_data
        |Function| providing the Neumann boundary values. If `None`,
        constant-zero is assumed.
    order
        Order of the Gauss quadrature to use for numerical integration.
    name
        The name of the functional.
    """

    range = NumpyVectorSpace(1)
    sparse = False

    def __init__(self, grid, function=None, boundary_info=None, dirichlet_data=None, diffusion_function=None,
                 diffusion_constant=None, neumann_data=None, order=1, name=None):
        assert function is None or function.shape_range == tuple()
        self.source = NumpyVectorSpace(grid.size(0))
        self.grid = grid
        self.boundary_info = boundary_info
        self.function = function
        self.dirichlet_data = dirichlet_data
        self.diffusion_function = diffusion_function
        self.diffusion_constant = diffusion_constant
        self.neumann_data = neumann_data
        self.order = order
        self.name = name
        self.build_parameter_type(inherits=(function, dirichlet_data, diffusion_function, neumann_data))

    def _assemble(self, mu=None):
        g = self.grid
        bi = self.boundary_info

        if self.function is not None:
            # evaluate function at all quadrature points -> shape = (g.size(0), number of quadrature points, 1)
            F = self.function(g.quadrature_points(0, order=self.order), mu=mu)

            _, w = g.reference_element.quadrature(order=self.order)

            # integrate the products of the function with the shape functions on each element
            # -> shape = (g.size(0), number of shape functions)
            F_INTS = np.einsum('ei,e,i->e', F, g.integration_elements(0), w).ravel()
        else:
            F_INTS = np.zeros(g.size(0))

        if bi is not None and (bi.has_dirichlet and self.dirichlet_data is not None
                               or bi.has_neumann and self.neumann_data):
            centers = g.centers(1)
            superentities = g.superentities(1, 0)
            superentity_indices = g.superentity_indices(1, 0)
            SE_I0 = superentities[:, 0]
            VOLS = g.volumes(1)
            FLUXES = np.zeros(g.size(1))

            if bi.has_dirichlet and self.dirichlet_data is not None:
                dirichlet_mask = bi.dirichlet_mask(1)
                SE_I0_D = SE_I0[dirichlet_mask]
                boundary_normals = g.unit_outer_normals()[SE_I0_D, superentity_indices[:, 0][dirichlet_mask]]
                BOUNDARY_DISTS = np.sum((centers[dirichlet_mask, :] - g.orthogonal_centers()[SE_I0_D, :]) * boundary_normals,
                                        axis=-1)
                DIRICHLET_FLUXES = VOLS[dirichlet_mask] * self.dirichlet_data(centers[dirichlet_mask]) / BOUNDARY_DISTS
                if self.diffusion_function is not None:
                    DIRICHLET_FLUXES *= self.diffusion_function(centers[dirichlet_mask], mu=mu)
                if self.diffusion_constant is not None:
                    DIRICHLET_FLUXES *= self.diffusion_constant
                FLUXES[dirichlet_mask] = DIRICHLET_FLUXES

            if bi.has_neumann and self.neumann_data is not None:
                neumann_mask = bi.neumann_mask(1)
                FLUXES[neumann_mask] -= VOLS[neumann_mask] * self.neumann_data(centers[neumann_mask])

            F_INTS += np.bincount(SE_I0, weights=FLUXES, minlength=len(F_INTS))

        F_INTS /= g.volumes(0)

        return F_INTS.reshape((1, -1))
Beispiel #25
0
 def restricted(self, dofs):
     assert all(0 <= c < self.range.dim for c in dofs)
     source_dofs = self.components[dofs]
     return IdentityOperator(NumpyVectorSpace(
         len(source_dofs))), source_dofs
Beispiel #26
0
 def __init__(self, components, source, name=None):
     assert all(0 <= c < source.dim for c in components)
     self.components = np.array(components)
     self.range = NumpyVectorSpace(len(components))
     self.source = source
     self.name = name
Beispiel #27
0
class L2ProductFunctionalP1(NumpyMatrixBasedOperator):
    """|Functional| representing the scalar product with an L2-|Function| for linear finite elements.

    Boundary treatment can be performed by providing `boundary_info` and `dirichlet_data`,
    in which case the DOFs corresponding to Dirichlet boundaries are set to the values
    provided by `dirichlet_data`. Neumann boundaries are handled by providing a
    `neumann_data` function.

    The current implementation works in one and two dimensions, but can be trivially
    extended to arbitrary dimensions.

    Parameters
    ----------
    grid
        |Grid| for which to assemble the functional.
    function
        The |Function| with which to take the scalar product.
    boundary_info
        |BoundaryInfo| determining the Dirichlet and Neumann boundaries or `None`.
        If `None`, no boundary treatment is performed.
    dirichlet_data
        |Function| providing the Dirichlet boundary values. If `None`,
        constant-zero boundary is assumed.
    neumann_data
        |Function| providing the Neumann boundary values. If `None`,
        constant-zero is assumed.
    order
        Order of the Gauss quadrature to use for numerical integration.
    name
        The name of the functional.
    """

    sparse = False
    range = NumpyVectorSpace(1)

    def __init__(self, grid, function, boundary_info=None, dirichlet_data=None, neumann_data=None, order=2, name=None):
        assert grid.reference_element(0) in {line, triangle}
        assert function.shape_range == tuple()
        self.source = NumpyVectorSpace(grid.size(grid.dim))
        self.grid = grid
        self.boundary_info = boundary_info
        self.function = function
        self.dirichlet_data = dirichlet_data
        self.neumann_data = neumann_data
        self.order = order
        self.name = name
        self.build_parameter_type(inherits=(function, dirichlet_data, neumann_data))

    def _assemble(self, mu=None):
        g = self.grid
        bi = self.boundary_info

        # evaluate function at all quadrature points -> shape = (g.size(0), number of quadrature points)
        F = self.function(g.quadrature_points(0, order=self.order), mu=mu)

        # evaluate the shape functions at the quadrature points on the reference
        # element -> shape = (number of shape functions, number of quadrature points)
        q, w = g.reference_element.quadrature(order=self.order)
        if g.dim == 1:
            SF = np.array((1 - q[..., 0], q[..., 0]))
        elif g.dim == 2:
            SF = np.array(((1 - np.sum(q, axis=-1)),
                           q[..., 0],
                           q[..., 1]))
        else:
            raise NotImplementedError

        # integrate the products of the function with the shape functions on each element
        # -> shape = (g.size(0), number of shape functions)
        SF_INTS = np.einsum('ei,pi,e,i->ep', F, SF, g.integration_elements(0), w).ravel()

        # map local DOFs to global DOFs
        # FIXME This implementation is horrible, find a better way!
        SF_I = g.subentities(0, g.dim).ravel()
        I = np.array(coo_matrix((SF_INTS, (np.zeros_like(SF_I), SF_I)), shape=(1, g.size(g.dim))).todense()).ravel()

        # boundary treatment
        if bi is not None and bi.has_neumann and self.neumann_data is not None:
            NI = bi.neumann_boundaries(1)
            if g.dim == 1:
                I[NI] -= self.neumann_data(g.centers(1)[NI])
            else:
                F = -self.neumann_data(g.quadrature_points(1, order=self.order)[NI], mu=mu)
                q, w = line.quadrature(order=self.order)
                SF = np.squeeze(np.array([1 - q, q]))
                SF_INTS = np.einsum('ei,pi,e,i->ep', F, SF, g.integration_elements(1)[NI], w).ravel()
                SF_I = g.subentities(1, 2)[NI].ravel()
                I += np.array(coo_matrix((SF_INTS, (np.zeros_like(SF_I), SF_I)), shape=(1, g.size(g.dim)))
                                        .todense()).ravel()

        if bi is not None and bi.has_dirichlet:
            DI = bi.dirichlet_boundaries(g.dim)
            if self.dirichlet_data is not None:
                I[DI] = self.dirichlet_data(g.centers(g.dim)[DI], mu=mu)
            else:
                I[DI] = 0

        return I.reshape((1, -1))