Example #1
0
def interpolate_operators(fom,
                          operator_names,
                          parameter_sample,
                          error_norm=None,
                          product=None,
                          atol=None,
                          rtol=None,
                          max_interpolation_dofs=None,
                          pod_options={},
                          alg='ei_greedy',
                          pool=dummy_pool):
    """Empirical operator interpolation using the EI-Greedy/DEIM algorithm.

    This is a convenience method to facilitate the use of :func:`ei_greedy` or :func:`deim`.
    Given a |Model|, names of |Operators|, and a sample of |Parameters|, first
    the operators are evaluated on the solution snapshots of the model for the
    provided parameters. These evaluations are then used as input for
    :func:`ei_greedy`/:func:`deim`.  Finally the resulting interpolation data is used to
    create |EmpiricalInterpolatedOperators| and a new model with the interpolated
    operators is returned.

    Note that this implementation creates *one* common collateral basis for all specified
    operators, which might not be what you want.

    Parameters
    ----------
    fom
        The |Model| whose |Operators| will be interpolated.
    operator_names
        List of keys in the `operators` dict of the model. The corresponding
        |Operators| will be interpolated.
    parameter_sample
        A list of |Parameters| for which solution snapshots are calculated.
    error_norm
        See :func:`ei_greedy`.
        Has no effect if `alg == 'deim'`.
    product
        Inner product for POD computation in :func:`deim`.
        Has no effect if `alg == 'ei_greedy'`.
    atol
        See :func:`ei_greedy`.
    rtol
        See :func:`ei_greedy`.
    max_interpolation_dofs
        See :func:`ei_greedy`.
    pod_options
        Further options for :func:`~pymor.algorithms.pod.pod` algorithm.
        Has no effect if `alg == 'ei_greedy'`.
    alg
        Either `ei_greedy` or `deim`.
    pool
        If not `None`, the |WorkerPool| to use for parallelization.

    Returns
    -------
    eim
        |Model| with |Operators| given by `operator_names` replaced by
        |EmpiricalInterpolatedOperators|.
    data
        Dict containing the following fields:

            :dofs:                  |NumPy array| of the DOFs at which the |Operators| have to be evaluated.
            :basis:                 |VectorArray| containing the generated collateral basis.

        In addition, `data` contains the fields of the `data` `dict` returned by
        :func:`ei_greedy`/:func:`deim`.
    """

    assert alg in ('ei_greedy', 'deim')
    logger = getLogger('pymor.algorithms.ei.interpolate_operators')
    with RemoteObjectManager() as rom:
        operators = [
            getattr(fom, operator_name) for operator_name in operator_names
        ]
        with logger.block(
                'Computing operator evaluations on solution snapshots ...'):
            if pool:
                logger.info(
                    f'Using pool of {len(pool)} workers for parallel evaluation'
                )
                evaluations = rom.manage(pool.push(fom.solution_space.empty()))
                pool.map(_interpolate_operators_build_evaluations,
                         parameter_sample,
                         fom=fom,
                         operators=operators,
                         evaluations=evaluations)
            else:
                evaluations = operators[0].range.empty()
                for mu in parameter_sample:
                    U = fom.solve(mu)
                    for op in operators:
                        evaluations.append(op.apply(U, mu=mu))

        if alg == 'ei_greedy':
            with logger.block('Performing EI-Greedy:'):
                dofs, basis, data = ei_greedy(
                    evaluations,
                    error_norm,
                    atol=atol,
                    rtol=rtol,
                    max_interpolation_dofs=max_interpolation_dofs,
                    copy=False,
                    pool=pool)
        elif alg == 'deim':
            if alg == 'deim' and pool is not dummy_pool:
                logger.warn(
                    'DEIM algorithm not parallel. Collecting operator evaluations.'
                )
                evaluations = pool.apply(_identity, x=evaluations)
                evs = evaluations[0]
                for e in evaluations[1:]:
                    evs.append(e, remove_from_other=True)
                evaluations = evs
            with logger.block('Executing DEIM algorithm:'):
                dofs, basis, data = deim(evaluations,
                                         modes=max_interpolation_dofs,
                                         atol=atol,
                                         rtol=rtol,
                                         pod_options=pod_options,
                                         product=product)
        else:
            assert False

    ei_operators = {
        name: EmpiricalInterpolatedOperator(operator,
                                            dofs,
                                            basis,
                                            triangular=(alg == 'ei_greedy'))
        for name, operator in zip(operator_names, operators)
    }
    eim = fom.with_(name=f'{fom.name}_ei', **ei_operators)

    data.update({'dofs': dofs, 'basis': basis})
    return eim, data
Example #2
0
def main(
        dim: int = Argument(..., help='Spatial dimension of the problem.'),
        n: int = Argument(
            ..., help='Number of mesh intervals per spatial dimension.'),
        order: int = Argument(..., help='Finite element order.'),
):
    """Reduces a FEniCS-based nonlinear diffusion problem using POD/DEIM."""

    from pymor.tools import mpi

    if mpi.parallel:
        from pymor.models.mpi import mpi_wrap_model
        local_models = mpi.call(mpi.function_call_manage, discretize, dim, n,
                                order)
        fom = mpi_wrap_model(local_models,
                             use_with=True,
                             pickle_local_spaces=False)
    else:
        fom = discretize(dim, n, order)

    parameter_space = fom.parameters.space((0, 1000.))

    # ### ROM generation (POD/DEIM)
    from pymor.algorithms.ei import ei_greedy
    from pymor.algorithms.newton import newton
    from pymor.algorithms.pod import pod
    from pymor.operators.ei import EmpiricalInterpolatedOperator
    from pymor.reductors.basic import StationaryRBReductor

    U = fom.solution_space.empty()
    residuals = fom.solution_space.empty()
    for mu in parameter_space.sample_uniformly(10):
        UU, data = newton(fom.operator,
                          fom.rhs.as_vector(),
                          mu=mu,
                          rtol=1e-6,
                          return_residuals=True)
        U.append(UU)
        residuals.append(data['residuals'])

    dofs, cb, _ = ei_greedy(residuals, rtol=1e-7)
    ei_op = EmpiricalInterpolatedOperator(fom.operator,
                                          collateral_basis=cb,
                                          interpolation_dofs=dofs,
                                          triangular=True)

    rb, svals = pod(U, rtol=1e-7)
    fom_ei = fom.with_(operator=ei_op)
    reductor = StationaryRBReductor(fom_ei, rb)
    rom = reductor.reduce()
    # the reductor currently removes all solver_options so we need to add them again
    rom = rom.with_(operator=rom.operator.with_(
        solver_options=fom.operator.solver_options))

    # ### ROM validation
    import time
    import numpy as np

    # ensure that FFC is not called during runtime measurements
    rom.solve(1)

    errs = []
    speedups = []
    for mu in parameter_space.sample_randomly(10):
        tic = time.perf_counter()
        U = fom.solve(mu)
        t_fom = time.perf_counter() - tic

        tic = time.perf_counter()
        u_red = rom.solve(mu)
        t_rom = time.perf_counter() - tic

        U_red = reductor.reconstruct(u_red)
        errs.append(((U - U_red).norm() / U.norm())[0])
        speedups.append(t_fom / t_rom)
    print(f'Maximum relative ROM error: {max(errs)}')
    print(f'Median of ROM speedup: {np.median(speedups)}')
Example #3
0
def interpolate_operators(d, operator_names, parameter_sample, error_norm=None,
                          atol=None, rtol=None, max_interpolation_dofs=None, pool=dummy_pool):
    """Empirical operator interpolation using the EI-Greedy algorithm.

    This is a convenience method to facilitate the use of :func:`ei_greedy`. Given
    a |Discretization|, names of |Operators|, and a sample of |Parameters|, first the operators
    are evaluated on the solution snapshots of the discretization for the provided parameters.
    These evaluations are then used as input for :func:`ei_greedy`. Finally the resulting
    interpolation data is used to create |EmpiricalInterpolatedOperators| and a new
    discretization with the interpolated operators is returned.

    Note that this implementation creates *one* common collateral basis for all specified
    operators, which might not be what you want.

    Parameters
    ----------
    d
        The |Discretization| whose |Operators| will be interpolated.
    operator_names
        List of keys in the `operators` dict of the discretization. The corresponding
        |Operators| will be interpolated.
    parameter_sample
        A list of |Parameters| for which solution snapshots are calculated.
    error_norm
        See :func:`ei_greedy`.
    atol
        See :func:`ei_greedy`.
    rtol
        See :func:`ei_greedy`.
    max_interpolation_dofs
        See :func:`ei_greedy`.
    pool
        If not `None`, the |WorkerPool| to use for parallelization.

    Returns
    -------
    ei_d
        |Discretization| with |Operators| given by `operator_names` replaced by
        |EmpiricalInterpolatedOperators|.
    data
        Dict containing the following fields:

            :dofs:                  |NumPy array| of the DOFs at which the |Operators| have to be evaluated.
            :basis:                 |VectorArray| containing the generated collateral basis.
            :errors:                Sequence of maximum approximation errors during greedy search.
            :triangularity_errors:  Sequence of maximum absolute values of interoplation
                                    matrix coefficients in the upper triangle (should
                                    be near zero).
    """

    logger = getLogger('pymor.algorithms.ei.interpolate_operators')
    with RemoteObjectManager() as rom:
        operators = [d.operators[operator_name] for operator_name in operator_names]
        with logger.block('Computing operator evaluations on solution snapshots ...'):
            if pool:
                logger.info('Using pool of {} workers for parallel evaluation'.format(len(pool)))
                evaluations = rom.manage(pool.push(d.solution_space.empty()))
                pool.map(_interpolate_operators_build_evaluations, parameter_sample,
                         d=d, operators=operators, evaluations=evaluations)
            else:
                evaluations = operators[0].range.empty()
                for mu in parameter_sample:
                    U = d.solve(mu)
                    for op in operators:
                        evaluations.append(op.apply(U, mu=mu))

        with logger.block('Performing EI-Greedy:'):
            dofs, basis, data = ei_greedy(evaluations, error_norm, atol=atol, rtol=rtol,
                                          max_interpolation_dofs=max_interpolation_dofs,
                                          copy=False, pool=pool)

    ei_operators = {name: EmpiricalInterpolatedOperator(operator, dofs, basis, triangular=True)
                    for name, operator in zip(operator_names, operators)}
    operators_dict = d.operators.copy()
    operators_dict.update(ei_operators)
    ei_d = d.with_(operators=operators_dict, name='{}_ei'.format(d.name))

    data.update({'dofs': dofs, 'basis': basis})
    return ei_d, data
def fenics_nonlinear_demo(args):
    DIM = int(args['DIM'])
    N = int(args['N'])
    ORDER = int(args['ORDER'])

    from pymor.tools import mpi

    if mpi.parallel:
        from pymor.models.mpi import mpi_wrap_model
        local_models = mpi.call(mpi.function_call_manage, discretize, DIM, N,
                                ORDER)
        fom = mpi_wrap_model(local_models,
                             use_with=True,
                             pickle_local_spaces=False)
    else:
        fom = discretize(DIM, N, ORDER)

    # ### ROM generation (POD/DEIM)
    from pymor.algorithms.ei import ei_greedy
    from pymor.algorithms.newton import newton
    from pymor.algorithms.pod import pod
    from pymor.operators.ei import EmpiricalInterpolatedOperator
    from pymor.reductors.basic import StationaryRBReductor

    U = fom.solution_space.empty()
    residuals = fom.solution_space.empty()
    for mu in fom.parameter_space.sample_uniformly(10):
        UU, data = newton(fom.operator,
                          fom.rhs.as_vector(),
                          mu=mu,
                          rtol=1e-6,
                          return_residuals=True)
        U.append(UU)
        residuals.append(data['residuals'])

    dofs, cb, _ = ei_greedy(residuals, rtol=1e-7)
    ei_op = EmpiricalInterpolatedOperator(fom.operator,
                                          collateral_basis=cb,
                                          interpolation_dofs=dofs,
                                          triangular=True)

    rb, svals = pod(U, rtol=1e-7)
    fom_ei = fom.with_(operator=ei_op)
    reductor = StationaryRBReductor(fom_ei, rb)
    rom = reductor.reduce()
    # the reductor currently removes all solver_options so we need to add them again
    rom = rom.with_(operator=rom.operator.with_(
        solver_options=fom.operator.solver_options))

    # ### ROM validation
    import time
    import numpy as np

    # ensure that FFC is not called during runtime measurements
    rom.solve(1)

    errs = []
    speedups = []
    for mu in fom.parameter_space.sample_randomly(10):
        tic = time.time()
        U = fom.solve(mu)
        t_fom = time.time() - tic

        tic = time.time()
        u_red = rom.solve(mu)
        t_rom = time.time() - tic

        U_red = reductor.reconstruct(u_red)
        errs.append(((U - U_red).l2_norm() / U.l2_norm())[0])
        speedups.append(t_fom / t_rom)
    print(f'Maximum relative ROM error: {max(errs)}')
    print(f'Median of ROM speedup: {np.median(speedups)}')