Example #1
0
    def reduce(self):
        if self.residual_range is not False:
            with self.logger.block('Estimating residual range ...'):
                try:
                    self.residual_range, self.residual_range_dims = \
                        estimate_image_hierarchical([self.operator], [self.rhs],
                                                    self.RB,
                                                    (self.residual_range, self.residual_range_dims),
                                                    orthonormalize=True, product=self.product,
                                                    riesz_representatives=self.riesz_representatives)
                except ImageCollectionError as e:
                    self.logger.warning('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
                    self.residual_range = False

        if self.residual_range is False:
            operator = project(self.operator, None, self.RB)
            return NonProjectedResidualOperator(operator, self.rhs, self.riesz_representatives, self.product)

        with self.logger.block('Projecting residual operator ...'):
            if self.riesz_representatives:
                operator = project(self.operator, self.residual_range, self.RB, product=None)  # the product cancels out.
                rhs = project(self.rhs, self.residual_range, None, product=None)
            else:
                operator = project(self.operator, self.residual_range, self.RB, product=self.product)
                rhs = project(self.rhs, self.residual_range, None, product=self.product)

        return ResidualOperator(operator, rhs)
Example #2
0
    def reduce(self):
        if self.residual_range is not False:
            with self.logger.block('Estimating residual range ...'):
                try:
                    self.residual_range, self.residual_range_dims = \
                        estimate_image_hierarchical([self.operator], [self.rhs],
                                                    self.RB,
                                                    (self.residual_range, self.residual_range_dims),
                                                    orthonormalize=True, product=self.product,
                                                    riesz_representatives=self.riesz_representatives)
                except ImageCollectionError as e:
                    self.logger.warning(f'Cannot compute range of {e.op}. Evaluation will be slow.')
                    self.residual_range = False

        if self.residual_range is False:
            operator = project(self.operator, None, self.RB)
            return NonProjectedResidualOperator(operator, self.rhs, self.riesz_representatives, self.product)

        with self.logger.block('Projecting residual operator ...'):
            if self.riesz_representatives:
                operator = project(self.operator, self.residual_range, self.RB, product=None)  # the product cancels out.
                rhs = project(self.rhs, self.residual_range, None, product=None)
            else:
                operator = project(self.operator, self.residual_range, self.RB, product=self.product)
                rhs = project(self.rhs, self.residual_range, None, product=self.product)

        return ResidualOperator(operator, rhs)
Example #3
0
    def reduce(self):
        if self.residual_range is not False:
            with self.logger.block('Estimating residual range ...'):
                try:
                    self.residual_range, self.residual_range_dims = \
                        estimate_image_hierarchical([self.operator, self.mass], [self.rhs],
                                                    self.RB,
                                                    (self.residual_range, self.residual_range_dims),
                                                    orthonormalize=True, product=self.product,
                                                    riesz_representatives=True)
                except ImageCollectionError as e:
                    self.logger.warning(f'Cannot compute range of {e.op}. Evaluation will be slow.')
                    self.residual_range = False

        if self.residual_range is False:
            operator = project(self.operator, None, self.RB)
            mass = project(self.mass, None, self.RB)
            return NonProjectedImplicitEulerResidualOperator(operator, mass, self.rhs, self.dt, self.product)

        with self.logger.block('Projecting residual operator ...'):
            operator = project(self.operator, self.residual_range, self.RB, product=None)  # the product always cancels out.
            mass = project(self.mass, self.residual_range, self.RB, product=None)
            rhs = project(self.rhs, self.residual_range, None, product=None)

        return ImplicitEulerResidualOperator(operator, mass, rhs, self.dt)
Example #4
0
    def reduce(self):
        if self.residual_range is not False:
            with self.logger.block('Estimating residual range ...'):
                try:
                    self.residual_range, self.residual_range_dims = \
                        estimate_image_hierarchical([self.operator, self.mass], [self.functional.T],
                                                    self.RB,
                                                    (self.residual_range, self.residual_range_dims),
                                                    orthonormalize=True, product=self.product,
                                                    riesz_representatives=True)
                except ImageCollectionError as e:
                    self.logger.warning('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
                    self.residual_range = False

        if self.residual_range is False:
            operator = project(self.operator, None, self.RB)
            mass = project(self.mass, None, self.RB)
            return NonProjectedImplicitEulerResidualOperator(operator, mass, self.functional, self.dt, self.product)

        with self.logger.block('Projecting residual operator ...'):
            operator = project(self.operator, self.residual_range, self.RB, product=None)  # the product always cancels out.
            mass = project(self.mass, self.residual_range, self.RB, product=None)
            functional = project(self.functional, None, self.residual_range, product=None)

        return ImplicitEulerResidualOperator(operator, mass, functional, self.dt)
Example #5
0
    def reduce(self):
        # Note that it is possible that rhs.source == rhs.range, nameley if both
        # are one-dimensional NumpyVectorSpaces which agree with the range of
        # operator. Usually, this should not happen, since at least one of these
        # spaces should have an id which is different from the id of operator.range.
        # However, even if it happens but rhs is actually a vector, we are on
        # the safe side, since first computing the Riesz representatives does not
        # change anything in one-dimensional spaces, and it does not matter whether
        # we project from the left or from the right.
        rhs_is_functional = (self.rhs.source == self.operator.range)

        if self.residual_range is not False:
            with self.logger.block('Estimating residual range ...'):
                try:
                    self.residual_range, self.residual_range_dims = \
                        estimate_image_hierarchical([self.operator], [self.rhs.T if rhs_is_functional else self.rhs],
                                                    self.RB,
                                                    (self.residual_range, self.residual_range_dims),
                                                    orthonormalize=True, product=self.product,
                                                    riesz_representatives=rhs_is_functional)
                except ImageCollectionError as e:
                    self.logger.warning('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
                    self.residual_range = False

        if self.residual_range is False:
            operator = project(self.operator, None, self.RB)
            return NonProjectedResidualOperator(operator, self.rhs, rhs_is_functional, self.product)

        with self.logger.block('Projecting residual operator ...'):
            if rhs_is_functional:
                operator = project(self.operator, self.residual_range, self.RB, product=None)  # the product cancels out.
                rhs = project(self.rhs, None, self.residual_range, product=None)
            else:
                operator = project(self.operator, self.residual_range, self.RB, product=self.product)
                rhs = project(self.rhs, self.residual_range, None, product=self.product)

        return ResidualOperator(operator, rhs, rhs_is_functional)
Example #6
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(range_basis=residual_range, source_basis=RB)

    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.residual.reduce_residual')

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

    if extends and isinstance(extends[0], NonProjectedResidualOperator):
        extends = None
    if extends:
        residual_range = extends[1].RB
        residual_range_dims = list(extends[2]['residual_range_dims'])
    else:
        residual_range = operator.range.empty()
        residual_range_dims = []

    with logger.block('Estimating residual range ...'):
        try:
            residual_range, residual_range_dims = \
                estimate_image_hierarchical([operator], [functional], RB, (residual_range, residual_range_dims),
                                            orthonormalize=True, product=product, riesz_representatives=True)
        except ImageCollectionError as e:
            logger.warn('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
            operator = operator.projected(None, RB)
            return (NonProjectedResidualOperator(operator, functional, product),
                    NonProjectedReconstructor(product),
                    {})

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

    return (ResidualOperator(operator, functional),
            GenericRBReconstructor(residual_range),
            {'residual_range_dims': residual_range_dims})
Example #7
0
def reduce_residual(operator, rhs=None, RB=None, product=None, extends=None):
    """Generic reduced basis residual reductor.

    Given an operator and a right-hand side, the residual is given by::

        residual.apply(U, mu) == operator.apply(U, mu) - rhs.as_vector(mu)

    When the rhs is a functional we are interested in the Riesz representative
    of the residual::

        residual.apply(U, mu)
            == product.apply_inverse(operator.apply(U, mu) - rhs.as_vector(mu))

    Given a basis `RB` of a subspace of the source space of `operator`, this method
    uses :func:`~pymor.algorithms.image.estimate_image_hierarchical` to determine
    a low-dimensional subspace containing the image of the subspace under
    `residual` (resp. `riesz_residual`), computes an orthonormal basis
    `residual_range` for this range space and then returns the Petrov-Galerkin projection ::

        projected_residual
            === residual.projected(range_basis=residual_range, source_basis=RB)

    of the residual operator. Given a reduced basis coefficient vector `u`, w.r.t.
    `RB`, the (dual) norm of the residual can then be computed as ::

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

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

        reconstructor.reconstruct(projected_residual.apply(u, mu))
            == residual.apply(RB.lincomb(u), mu)

    Parameters
    ----------
    operator
        See definition of `residual`.
    rhs
        See definition of `residual`. If `None`, zero right-hand side is assumed.
    RB
        |VectorArray| containing a basis of the reduced space onto which to project.
    product
        Inner product |Operator| w.r.t. which to orthonormalize and w.r.t. which to
        compute the Riesz representatives in case `rhs` is a functional.
    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_residual
        See above.
    reconstructor
        See above.
    reduction_data
        Additional data produced by the reduction process (compare the `extends` parameter).
    """
    assert rhs is None \
        or (rhs.range.is_scalar and rhs.source == operator.range and rhs.linear) \
        or (rhs.source.is_scalar and rhs.range == operator.range and rhs.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

    # Note that it is possible that rhs.source == rhs.range, nameley if both
    # are one-dimensional NumpyVectorSpaces which agree with the range of
    # operator. Usually, this should not happen, since at least one of these
    # spaces should have an id which is different from the id of operator.range.
    # However, even if it happens but rhs is actually a vector, we are on
    # the safe side, since first computing the Riesz representatives does not
    # change anything in one-dimensional spaces, and it does not matter whether
    # we project from the left or from the right.
    rhs_is_functional = (rhs.source == operator.range)

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

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

    if extends and isinstance(extends[0], NonProjectedResidualOperator):
        extends = None
    if extends:
        residual_range = extends[1].RB
        residual_range_dims = list(extends[2]['residual_range_dims'])
    else:
        residual_range = operator.range.empty()
        residual_range_dims = []

    with logger.block('Estimating residual range ...'):
        try:
            residual_range, residual_range_dims = \
                estimate_image_hierarchical([operator], [rhs.T if rhs_is_functional else rhs], RB,
                                            (residual_range, residual_range_dims),
                                            orthonormalize=True, product=product,
                                            riesz_representatives=rhs_is_functional)
        except ImageCollectionError as e:
            logger.warn('Cannot compute range of {}. Evaluation will be slow.'.format(e.op))
            operator = operator.projected(None, RB)
            return (NonProjectedResidualOperator(operator, rhs, rhs_is_functional, product),
                    NonProjectedReconstructor(product),
                    {})

    with logger.block('Projecting residual operator ...'):
        if rhs_is_functional:
            operator = operator.projected(residual_range, RB, product=None)  # the product cancels out.
            rhs = rhs.projected(None, residual_range, product=None)
        else:
            operator = operator.projected(residual_range, RB, product=product)
            rhs = rhs.projected(residual_range, None, product=product)

    return (ResidualOperator(operator, rhs, rhs_is_functional),
            GenericRBReconstructor(residual_range),
            {'residual_range_dims': residual_range_dims})