Esempio n. 1
0
 def with_(self, **kwargs):
     assert 'operators' in kwargs
     operators = kwargs.pop('operators')
     assert set(operators.keys()) == {'operator', 'rhs'}
     assert all(op.type_source == NumpyVectorArray for op in operators.itervalues())
     assert all(op.type_range == NumpyVectorArray for op in operators.itervalues())
     d = StationaryDiscretization(operator=operators['operator'], rhs=operators['rhs'])
     return d.with_(**kwargs)
Esempio n. 2
0
 def with_(self, **kwargs):
     assert 'operators' and 'functionals' in kwargs or kwargs.keys() == ['parameter_space']
     assert 'vector_operators' not in kwargs or not kwargs['vector_operators']
     if 'operators' in kwargs:
         operators = kwargs.pop('operators')
         functionals = kwargs.pop('functionals')
         assert set(operators.keys()) == {'operator'}
         assert set(functionals.keys()) == {'rhs'}
         operator = operators['operator']
         rhs = functionals['rhs']
         assert all(op.source.type == NumpyVectorArray or op.source.type == BlockVectorArray for op in (operator, rhs))
         assert all(op.range.type  == NumpyVectorArray or op.range.type  == BlockVectorArray for op in (operator, rhs))
         d = StationaryDiscretization(operator=operator, rhs=rhs)
         return d.with_(**kwargs)
     else:
         d = type(self)(self._impl)
         d.unlock()
         d.parameter_space = kwargs['parameter_space']
         d.lock()
         return d
def discretize(grid_and_problem_data, polorder=1, solver_options=None):

    logger = getLogger('discretize_elliptic_swipdg.discretize')
    logger.info('discretizing ... ')
    over_integrate = 2

    grid, boundary_info = grid_and_problem_data['grid'], grid_and_problem_data[
        'boundary_info']

    _lambda, kappa, f = (grid_and_problem_data['lambda'],
                         grid_and_problem_data['kappa'],
                         grid_and_problem_data['f'])
    lambda_bar, lambda_bar = grid_and_problem_data[
        'lambda_bar'], grid_and_problem_data['lambda_bar']
    mu_bar, mu_hat, parameter_range = (
        grid_and_problem_data['mu_bar'], grid_and_problem_data['mu_hat'],
        grid_and_problem_data['parameter_range'])
    space = make_dg_space(grid)
    # prepare operators and functionals
    if isinstance(_lambda, dict):
        system_ops = [
            make_elliptic_swipdg_matrix_operator(lambda_func, kappa,
                                                 boundary_info, space,
                                                 over_integrate)
            for lambda_func in _lambda['functions']
        ]
        elliptic_ops = [
            make_elliptic_matrix_operator(lambda_func, kappa, space,
                                          over_integrate)
            for lambda_func in _lambda['functions']
        ]
    else:
        system_ops = [
            make_elliptic_swipdg_matrix_operator(_lambda, kappa, boundary_info,
                                                 space, over_integrate),
        ]
        elliptic_ops = [
            make_elliptic_matrix_operator(_lambda, kappa, space,
                                          over_integrate),
        ]
    if isinstance(f, dict):
        rhs_functionals = [
            make_l2_volume_vector_functional(f_func, space, over_integrate)
            for f_func in f['functions']
        ]
    else:
        rhs_functionals = [
            make_l2_volume_vector_functional(f, space, over_integrate),
        ]
    l2_matrix_with_system_pattern = system_ops[0].matrix().copy()
    l2_operator = make_l2_matrix_operator(l2_matrix_with_system_pattern, space)
    # assemble everything in one grid walk
    system_assembler = make_system_assembler(space)
    for op in system_ops:
        system_assembler.append(op)
    for op in elliptic_ops:
        system_assembler.append(op)
    for func in rhs_functionals:
        system_assembler.append(func)
    system_assembler.append(l2_operator)
    system_assembler.walk()
    # wrap everything
    if isinstance(_lambda, dict):
        op = LincombOperator([
            DuneXTMatrixOperator(o.matrix(),
                                 dof_communicator=space.dof_communicator)
            for o in system_ops
        ], _lambda['coefficients'])
        elliptic_op = LincombOperator(
            [DuneXTMatrixOperator(o.matrix()) for o in elliptic_ops],
            _lambda['coefficients'])
    else:
        op = DuneXTMatrixOperator(system_ops[0].matrix())
        elliptic_op = DuneXTMatrixOperator(elliptic_ops[0].matrix())
    if isinstance(f, dict):
        rhs = LincombOperator([
            VectorFunctional(op.range.make_array([func.vector()]))
            for func in rhs_functionals
        ], f['coefficients'])
    else:
        rhs = VectorFunctional(
            op.range.make_array([rhs_functionals[0].vector()]))
    operators = {
        'l2':
        DuneXTMatrixOperator(l2_matrix_with_system_pattern),
        'elliptic':
        elliptic_op,
        'elliptic_mu_bar':
        DuneXTMatrixOperator(elliptic_op.assemble(mu=mu_bar).matrix)
    }
    d = StationaryDiscretization(op,
                                 rhs,
                                 operators=operators,
                                 visualizer=DuneGDTVisualizer(space))
    d = d.with_(parameter_space=CubicParameterSpace(
        d.parameter_type, parameter_range[0], parameter_range[1]))

    return d, {'space': space}
Esempio n. 4
0
def discretize_elliptic_cg(analytical_problem,
                           diameter=None,
                           domain_discretizer=None,
                           grid=None,
                           boundary_info=None):
    """Discretizes an |EllipticProblem| using finite elements.

    Parameters
    ----------
    analytical_problem
        The |EllipticProblem| to discretize.
    diameter
        If not `None`, `diameter` is passed to the `domain_discretizer`.
    domain_discretizer
        Discretizer to be used for discretizing the analytical domain. This has
        to be a function `domain_discretizer(domain_description, diameter, ...)`.
        If further arguments should be passed to the discretizer, use
        :func:`functools.partial`. If `None`, |discretize_domain_default| is used.
    grid
        Instead of using a domain discretizer, the |Grid| can also be passed directly
        using this parameter.
    boundary_info
        A |BoundaryInfo| specifying the boundary types of the grid boundary entities.
        Must be provided if `grid` is specified.

    Returns
    -------
    discretization
        The |Discretization| that has been generated.
    data
        Dictionary with the following entries:

            :grid:           The generated |Grid|.
            :boundary_info:  The generated |BoundaryInfo|.
    """

    assert isinstance(analytical_problem, EllipticProblem)
    assert grid is None or boundary_info is not None
    assert boundary_info is None or grid is not None
    assert grid is None or domain_discretizer is None

    if grid is None:
        domain_discretizer = domain_discretizer or discretize_domain_default
        if diameter is None:
            grid, boundary_info = domain_discretizer(analytical_problem.domain)
        else:
            grid, boundary_info = domain_discretizer(analytical_problem.domain,
                                                     diameter=diameter)

    assert isinstance(grid, (OnedGrid, TriaGrid, RectGrid))

    if isinstance(grid, RectGrid):
        Operator = cg.DiffusionOperatorQ1
        Functional = cg.L2ProductFunctionalQ1
    else:
        Operator = cg.DiffusionOperatorP1
        Functional = cg.L2ProductFunctionalP1

    p = analytical_problem

    if p.diffusion_functionals is not None:
        L0 = Operator(grid,
                      boundary_info,
                      diffusion_constant=0,
                      name='diffusion_boundary_part')

        Li = [
            Operator(grid,
                     boundary_info,
                     diffusion_function=df,
                     dirichlet_clear_diag=True,
                     name='diffusion_{}'.format(i))
            for i, df in enumerate(p.diffusion_functions)
        ]

        L = LincombOperator(operators=[L0] + Li,
                            coefficients=[1.] + list(p.diffusion_functionals),
                            name='diffusion')
    else:
        assert len(p.diffusion_functions) == 1
        L = Operator(grid,
                     boundary_info,
                     diffusion_function=p.diffusion_functions[0],
                     name='diffusion')

    F = Functional(grid,
                   p.rhs,
                   boundary_info,
                   dirichlet_data=p.dirichlet_data,
                   neumann_data=p.neumann_data)

    if isinstance(grid, (TriaGrid, RectGrid)):
        visualizer = PatchVisualizer(grid=grid,
                                     bounding_box=grid.domain,
                                     codim=2)
    else:
        visualizer = Matplotlib1DVisualizer(grid=grid, codim=1)

    empty_bi = EmptyBoundaryInfo(grid)
    l2_product = cg.L2ProductQ1(grid, empty_bi) if isinstance(
        grid, RectGrid) else cg.L2ProductP1(grid, empty_bi)
    h1_semi_product = Operator(grid, empty_bi)
    products = {
        'h1': l2_product + h1_semi_product,
        'h1_semi': h1_semi_product,
        'l2': l2_product
    }

    parameter_space = p.parameter_space if hasattr(p,
                                                   'parameter_space') else None

    discretization = StationaryDiscretization(L,
                                              F,
                                              products=products,
                                              visualizer=visualizer,
                                              parameter_space=parameter_space,
                                              name='{}_CG'.format(p.name))

    return discretization, {'grid': grid, 'boundary_info': boundary_info}
Esempio n. 5
0
def discretize_elliptic_fv(analytical_problem,
                           diameter=None,
                           domain_discretizer=None,
                           grid=None,
                           boundary_info=None):
    """Discretizes an |EllipticProblem| using the finite volume method.

    Parameters
    ----------
    analytical_problem
        The |EllipticProblem| to discretize.
    diameter
        If not `None`, `diameter` is passed to the `domain_discretizer`.
    domain_discretizer
        Discretizer to be used for discretizing the analytical domain. This has
        to be a function `domain_discretizer(domain_description, diameter, ...)`.
        If further arguments should be passed to the discretizer, use
        :func:`functools.partial`. If `None`, |discretize_domain_default| is used.
    grid
        Instead of using a domain discretizer, the |Grid| can also be passed directly
        using this parameter.
    boundary_info
        A |BoundaryInfo| specifying the boundary types of the grid boundary entities.
        Must be provided if `grid` is specified.

    Returns
    -------
    discretization
        The |Discretization| that has been generated.
    data
        Dictionary with the following entries:

            :grid:           The generated |Grid|.
            :boundary_info:  The generated |BoundaryInfo|.
    """

    assert isinstance(analytical_problem, EllipticProblem)
    assert grid is None or boundary_info is not None
    assert boundary_info is None or grid is not None
    assert grid is None or domain_discretizer is None

    if grid is None:
        domain_discretizer = domain_discretizer or discretize_domain_default
        if diameter is None:
            grid, boundary_info = domain_discretizer(analytical_problem.domain)
        else:
            grid, boundary_info = domain_discretizer(analytical_problem.domain,
                                                     diameter=diameter)

    p = analytical_problem

    if p.diffusion_functionals is not None:
        Li = [
            fv.DiffusionOperator(grid,
                                 boundary_info,
                                 diffusion_function=df,
                                 name='diffusion_{}'.format(i))
            for i, df in enumerate(p.diffusion_functions)
        ]
        L = LincombOperator(operators=Li,
                            coefficients=list(p.diffusion_functionals),
                            name='diffusion')

        F0 = fv.L2ProductFunctional(grid,
                                    p.rhs,
                                    boundary_info=boundary_info,
                                    neumann_data=p.neumann_data)
        if p.dirichlet_data is not None:
            Fi = [
                fv.L2ProductFunctional(grid,
                                       None,
                                       boundary_info=boundary_info,
                                       dirichlet_data=p.dirichlet_data,
                                       diffusion_function=df,
                                       name='dirichlet_{}'.format(i))
                for i, df in enumerate(p.diffusion_functions)
            ]
            F = LincombOperator(operators=[F0] + Fi,
                                coefficients=[1.] +
                                list(p.diffusion_functionals),
                                name='rhs')
        else:
            F = F0

    else:
        assert len(p.diffusion_functions) == 1
        L = fv.DiffusionOperator(grid,
                                 boundary_info,
                                 diffusion_function=p.diffusion_functions[0],
                                 name='diffusion')

        F = fv.L2ProductFunctional(grid,
                                   p.rhs,
                                   boundary_info=boundary_info,
                                   dirichlet_data=p.dirichlet_data,
                                   diffusion_function=p.diffusion_functions[0],
                                   neumann_data=p.neumann_data)

    if isinstance(grid, (TriaGrid, RectGrid)):
        visualizer = PatchVisualizer(grid=grid,
                                     bounding_box=grid.domain,
                                     codim=0)
    elif isinstance(grid, (OnedGrid)):
        visualizer = Matplotlib1DVisualizer(grid=grid, codim=0)
    else:
        visualizer = None

    l2_product = fv.L2Product(grid)
    products = {'l2': l2_product}

    parameter_space = p.parameter_space if hasattr(p,
                                                   'parameter_space') else None

    discretization = StationaryDiscretization(L,
                                              F,
                                              products=products,
                                              visualizer=visualizer,
                                              parameter_space=parameter_space,
                                              name='{}_FV'.format(p.name))

    return discretization, {'grid': grid, 'boundary_info': boundary_info}
Esempio n. 6
0
def discretize_stationary_cg(analytical_problem,
                             diameter=None,
                             domain_discretizer=None,
                             grid_type=None,
                             grid=None,
                             boundary_info=None,
                             preassemble=True):
    """Discretizes an |StationaryProblem| using finite elements.

    Parameters
    ----------
    analytical_problem
        The |StationaryProblem| to discretize.
    diameter
        If not `None`, `diameter` is passed as an argument to the `domain_discretizer`.
    domain_discretizer
        Discretizer to be used for discretizing the analytical domain. This has
        to be a function `domain_discretizer(domain_description, diameter, ...)`.
        If `None`, |discretize_domain_default| is used.
    grid_type
        If not `None`, this parameter is forwarded to `domain_discretizer` to specify
        the type of the generated |Grid|.
    grid
        Instead of using a domain discretizer, the |Grid| can also be passed directly
        using this parameter.
    boundary_info
        A |BoundaryInfo| specifying the boundary types of the grid boundary entities.
        Must be provided if `grid` is specified.
    preassemble
        If `True`, preassemble all operators in the resulting |Discretization|.

    Returns
    -------
    d
        The |Discretization| that has been generated.
    data
        Dictionary with the following entries:

            :grid:           The generated |Grid|.
            :boundary_info:  The generated |BoundaryInfo|.
    """

    assert isinstance(analytical_problem, StationaryProblem)
    assert grid is None or boundary_info is not None
    assert boundary_info is None or grid is not None
    assert grid is None or domain_discretizer is None
    assert grid_type is None or grid is None

    p = analytical_problem

    if not (p.nonlinear_advection == p.nonlinear_advection_derivative ==
            p.nonlinear_reaction == p.nonlinear_reaction_derivative == None):
        raise NotImplementedError

    if grid is None:
        domain_discretizer = domain_discretizer or discretize_domain_default
        if grid_type:
            domain_discretizer = partial(domain_discretizer,
                                         grid_type=grid_type)
        if diameter is None:
            grid, boundary_info = domain_discretizer(p.domain)
        else:
            grid, boundary_info = domain_discretizer(p.domain,
                                                     diameter=diameter)

    assert grid.reference_element in (line, triangle, square)

    if grid.reference_element is square:
        DiffusionOperator = DiffusionOperatorQ1
        AdvectionOperator = AdvectionOperatorQ1
        ReactionOperator = L2ProductQ1
        Functional = L2ProductFunctionalQ1
    else:
        DiffusionOperator = DiffusionOperatorP1
        AdvectionOperator = AdvectionOperatorP1
        ReactionOperator = L2ProductP1
        Functional = L2ProductFunctionalP1

    Li = [
        DiffusionOperator(grid,
                          boundary_info,
                          diffusion_constant=0,
                          name='boundary_part')
    ]
    coefficients = [1.]

    # diffusion part
    if isinstance(p.diffusion, LincombFunction):
        Li += [
            DiffusionOperator(grid,
                              boundary_info,
                              diffusion_function=df,
                              dirichlet_clear_diag=True,
                              name='diffusion_{}'.format(i))
            for i, df in enumerate(p.diffusion.functions)
        ]
        coefficients += list(p.diffusion.coefficients)
    elif p.diffusion is not None:
        Li += [
            DiffusionOperator(grid,
                              boundary_info,
                              diffusion_function=p.diffusion,
                              dirichlet_clear_diag=True,
                              name='diffusion')
        ]
        coefficients.append(1.)

    # advection part
    if isinstance(p.advection, LincombFunction):
        Li += [
            AdvectionOperator(grid,
                              boundary_info,
                              advection_function=af,
                              dirichlet_clear_diag=True,
                              name='advection_{}'.format(i))
            for i, af in enumerate(p.advection.functions)
        ]
        coefficients += list(p.advection.coefficients)
    elif p.advection is not None:
        Li += [
            AdvectionOperator(grid,
                              boundary_info,
                              advection_function=p.advection,
                              dirichlet_clear_diag=True,
                              name='advection')
        ]
        coefficients.append(1.)

    # reaction part
    if isinstance(p.reaction, LincombFunction):
        Li += [
            ReactionOperator(grid,
                             boundary_info,
                             coefficient_function=rf,
                             dirichlet_clear_diag=True,
                             name='reaction_{}'.format(i))
            for i, rf in enumerate(p.reaction.functions)
        ]
        coefficients += list(p.reaction.coefficients)
    elif p.reaction is not None:
        Li += [
            ReactionOperator(grid,
                             boundary_info,
                             coefficient_function=p.reaction,
                             dirichlet_clear_diag=True,
                             name='reaction')
        ]
        coefficients.append(1.)

    # robin boundaries
    if p.robin_data is not None:
        if grid.reference_element is square:
            raise NotImplementedError
        Li += [
            RobinBoundaryOperator(grid,
                                  boundary_info,
                                  robin_data=p.robin_data,
                                  order=2,
                                  name='robin')
        ]
        coefficients.append(1.)

    L = LincombOperator(operators=Li,
                        coefficients=coefficients,
                        name='ellipticOperator')

    rhs = p.rhs or ConstantFunction(0., dim_domain=p.domain.dim)
    F = Functional(grid,
                   rhs,
                   boundary_info,
                   dirichlet_data=p.dirichlet_data,
                   neumann_data=p.neumann_data)

    if grid.reference_element in (triangle, square):
        visualizer = PatchVisualizer(grid=grid,
                                     bounding_box=grid.bounding_box(),
                                     codim=2)
    elif grid.reference_element is line:
        visualizer = OnedVisualizer(grid=grid, codim=1)
    else:
        visualizer = None

    Prod = L2ProductQ1 if grid.reference_element is square else L2ProductP1
    empty_bi = EmptyBoundaryInfo(grid)
    l2_product = Prod(grid, empty_bi, name='l2')
    l2_0_product = Prod(grid,
                        boundary_info,
                        dirichlet_clear_columns=True,
                        name='l2_0')
    h1_semi_product = DiffusionOperator(grid, empty_bi, name='h1_semi')
    h1_0_semi_product = DiffusionOperator(grid,
                                          boundary_info,
                                          dirichlet_clear_columns=True,
                                          name='h1_0_semi')
    products = {
        'h1': l2_product + h1_semi_product,
        'h1_semi': h1_semi_product,
        'l2': l2_product,
        'h1_0': l2_0_product + h1_0_semi_product,
        'h1_0_semi': h1_0_semi_product,
        'l2_0': l2_0_product
    }

    parameter_space = p.parameter_space if hasattr(p,
                                                   'parameter_space') else None

    d = StationaryDiscretization(L,
                                 F,
                                 products=products,
                                 visualizer=visualizer,
                                 parameter_space=parameter_space,
                                 name='{}_CG'.format(p.name))

    data = {'grid': grid, 'boundary_info': boundary_info}

    if preassemble:
        data['unassembled_d'] = d
        d = preassemble_(d)

    return d, data
Esempio n. 7
0
def _discretize_fenics(xblocks, yblocks, grid_num_intervals, element_order):

    # assemble system matrices - FEniCS code
    ########################################

    import dolfin as df
    mesh = df.UnitSquareMesh(grid_num_intervals, grid_num_intervals, 'crossed')
    V = df.FunctionSpace(mesh, 'Lagrange', element_order)
    u = df.TrialFunction(V)
    v = df.TestFunction(V)

    diffusion = df.Expression(
        '(lower0 <= x[0]) * (open0 ? (x[0] < upper0) : (x[0] <= upper0)) *' +
        '(lower1 <= x[1]) * (open1 ? (x[1] < upper1) : (x[1] <= upper1))',
        lower0=0.,
        upper0=0.,
        open0=0,
        lower1=0.,
        upper1=0.,
        open1=0,
        element=df.FunctionSpace(mesh, 'DG', 0).ufl_element())

    def assemble_matrix(x, y, nx, ny):
        diffusion.user_parameters['lower0'] = x / nx
        diffusion.user_parameters['lower1'] = y / ny
        diffusion.user_parameters['upper0'] = (x + 1) / nx
        diffusion.user_parameters['upper1'] = (y + 1) / ny
        diffusion.user_parameters['open0'] = (x + 1 == nx)
        diffusion.user_parameters['open1'] = (y + 1 == ny)
        return df.assemble(
            df.inner(diffusion * df.nabla_grad(u), df.nabla_grad(v)) * df.dx)

    mats = [
        assemble_matrix(x, y, xblocks, yblocks) for x in range(xblocks)
        for y in range(yblocks)
    ]
    mat0 = mats[0].copy()
    mat0.zero()
    h1_mat = df.assemble(df.inner(df.nabla_grad(u), df.nabla_grad(v)) * df.dx)
    l2_mat = df.assemble(u * v * df.dx)

    f = df.Constant(1.) * v * df.dx
    F = df.assemble(f)

    bc = df.DirichletBC(V, 0., df.DomainBoundary())
    for m in mats:
        bc.zero(m)
    bc.apply(mat0)
    bc.apply(h1_mat)
    bc.apply(F)

    # wrap everything as a pyMOR discretization
    ###########################################

    # FEniCS wrappers
    from pymor.gui.fenics import FenicsVisualizer
    from pymor.operators.fenics import FenicsMatrixOperator
    from pymor.vectorarrays.fenics import FenicsVector

    # generic pyMOR classes
    from pymor.discretizations.basic import StationaryDiscretization
    from pymor.operators.constructions import LincombOperator, VectorFunctional
    from pymor.parameters.functionals import ProjectionParameterFunctional
    from pymor.parameters.spaces import CubicParameterSpace
    from pymor.vectorarrays.list import ListVectorArray

    # define parameter functionals (same as in pymor.analyticalproblems.thermalblock)
    def parameter_functional_factory(x, y):
        return ProjectionParameterFunctional(
            component_name='diffusion',
            component_shape=(yblocks, xblocks),
            coordinates=(yblocks - y - 1, x),
            name='diffusion_{}_{}'.format(x, y))

    parameter_functionals = tuple(
        parameter_functional_factory(x, y) for x in range(xblocks)
        for y in range(yblocks))

    # wrap operators
    ops = [FenicsMatrixOperator(mat0, V, V)
           ] + [FenicsMatrixOperator(m, V, V) for m in mats]
    op = LincombOperator(ops, (1., ) + parameter_functionals)
    rhs = VectorFunctional(ListVectorArray([FenicsVector(F, V)]))
    h1_product = FenicsMatrixOperator(h1_mat, V, V, name='h1_0_semi')
    l2_product = FenicsMatrixOperator(l2_mat, V, V, name='l2')

    # build discretization
    visualizer = FenicsVisualizer(V)
    parameter_space = CubicParameterSpace(op.parameter_type, 0.1, 1.)
    d = StationaryDiscretization(op,
                                 rhs,
                                 products={
                                     'h1_0_semi': h1_product,
                                     'l2': l2_product
                                 },
                                 parameter_space=parameter_space,
                                 visualizer=visualizer)

    return d
Esempio n. 8
0
def discretize_stationary_from_disk(parameter_file):
    """Generates stationary discretization only based on data loaded from files.

    The path and further specifications to these objects are given in an '.ini' parameter file (see example below).
    Suitable for discrete problems given by::

        L(u, w) = F(w)

    with an operator L and a linear functional F with a parameter w  given as system matrices and rhs vectors in
    an affine decomposition on the hard disk.

    Parameters
    ----------
    parameterFile
        String containing the path to the .ini parameter file.

    Returns
    -------
    discretization
        The |Discretization| that has been generated.


    Example
    -------
    Following parameter file is suitable for a discrete elliptic problem with

    L(u, w) = (f_1(w)*K1 + f_2(w)*K2+...)*u and F(w) = g_1(w)*L1+g_2(w)*L2+... with
    parameter w_i in [a_i,b_i], where f_i(w) and g_i(w) are strings of valid python
    expressions.

    Optional products can be provided to introduce a dict of inner products on
    the discrete space. The content of the file is then given as::

        [system-matrices]
        # path_to_object: parameter_functional_associated_with_object
        K1.mat: f_1(w_1,...,w_n)
        K2.mat: f_2(w_1,...,w_n)
        ...

        [rhs-vectors]
        L1.mat: g_1(w_1,...,w_n)
        L2.mat: g_2(w_1,...,w_n)
        ...

        [parameter]
        # Name: lower_bound,upper_bound
        w_1: a_1,b_1
        ...
        w_n: a_n,b_n

        [products]
        # Name: path_to_object
        Prod1: S.mat
        Prod2: T.mat
        ...
    """
    assert ".ini" == parameter_file[-4:], "Given file is not an .ini file"
    base_path = os.path.dirname(parameter_file)

    # Get input from parameter file
    config = configparser.ConfigParser()
    config.optionxform = str
    config.read(parameter_file)

    # Assert that all needed entries given
    assert 'system-matrices' in config.sections()
    assert 'rhs-vectors' in config.sections()
    assert 'parameter' in config.sections()

    system_mat = config.items('system-matrices')
    rhs_vec = config.items('rhs-vectors')
    parameter = config.items('parameter')

    # Dict of parameters types and ranges
    parameter_type = {}
    parameter_range = {}

    # get parameters
    for i in range(len(parameter)):
        parameter_name = parameter[i][0]
        parameter_list = tuple(float(j) for j in parameter[i][1].replace(" ", "").split(','))
        parameter_range[parameter_name] = parameter_list
        # Assume scalar parameter dependence
        parameter_type[parameter_name] = 0

    # Create parameter space
    parameter_space = CubicParameterSpace(parameter_type=parameter_type, ranges=parameter_range)

    # Assemble operators
    system_operators, system_functionals = [], []

    # get parameter functionals and system matrices
    for i in range(len(system_mat)):
        path = os.path.join(base_path, system_mat[i][0])
        expr = system_mat[i][1]
        parameter_functional = ExpressionParameterFunctional(expr, parameter_type=parameter_type)
        system_operators.append(NumpyMatrixOperator.from_file(path))
        system_functionals.append(parameter_functional)

    system_lincombOperator = LincombOperator(system_operators, coefficients=system_functionals)

    # get rhs vectors
    rhs_operators, rhs_functionals = [], []

    for i in range(len(rhs_vec)):
        path = os.path.join(base_path, rhs_vec[i][0])
        expr = rhs_vec[i][1]
        parameter_functional = ExpressionParameterFunctional(expr, parameter_type=parameter_type)
        op = NumpyMatrixOperator.from_file(path)
        assert isinstance(op._matrix, np.ndarray)
        op = op.with_(matrix=op._matrix.reshape((1, -1)))
        rhs_operators.append(op)
        rhs_functionals.append(parameter_functional)

    rhs_lincombOperator = LincombOperator(rhs_operators, coefficients=rhs_functionals)

    # get products if given
    if 'products' in config.sections():
        product = config.items('products')
        products = {}
        for i in range(len(product)):
            product_name = product[i][0]
            product_path = os.path.join(base_path, product[i][1])
            products[product_name] = NumpyMatrixOperator.from_file(product_path)
    else:
        products = None

    # Create and return stationary discretization
    return StationaryDiscretization(operator=system_lincombOperator, rhs=rhs_lincombOperator,
                                    parameter_space=parameter_space, products=products)
Esempio n. 9
0
 def with_(self, **kwargs):
     assert 'vector_operators' not in kwargs or not kwargs['vector_operators']
     if 'operators' in kwargs and 'functionals' in kwargs:
         operators = kwargs.pop('operators')
         functionals = kwargs.pop('functionals')
         assert set(operators.keys()) == {'operator'}
         assert set(functionals.keys()) == {'rhs'}
         operator = operators['operator']
         rhs = functionals['rhs']
         d = StationaryDiscretization(operator=operator, rhs=rhs, parameter_space=self.parameter_space)
         return d.with_(**kwargs)
     elif 'cache_region' in kwargs:
         d = type(self)(self._impl)
         d.unlock()
         d.enable_caching(kwargs.pop('cache_region'))
         d.lock()
         return d.with_(**kwargs)
     else:
         d = type(self)(self._impl)
         d.unlock()
         for attr in ('solver_options', 'parameter_space'):
             if attr in dir(self):
                 setattr(d, attr, getattr(self, attr))
         if 'parameter_space' in kwargs:
             d.parameter_space = kwargs.pop('parameter_space')
         assert len(kwargs) == 0
         d.lock()
         return d
Esempio n. 10
0
def discretize_stationary_fv(analytical_problem,
                             diameter=None,
                             domain_discretizer=None,
                             grid_type=None,
                             num_flux='lax_friedrichs',
                             lxf_lambda=1.,
                             eo_gausspoints=5,
                             eo_intervals=1,
                             grid=None,
                             boundary_info=None,
                             preassemble=True):
    """Discretizes an |StationaryProblem| using the finite volume method.

    Parameters
    ----------
    analytical_problem
        The |StationaryProblem| to discretize.
    diameter
        If not `None`, `diameter` is passed as an argument to the `domain_discretizer`.
    domain_discretizer
        Discretizer to be used for discretizing the analytical domain. This has
        to be a function `domain_discretizer(domain_description, diameter, ...)`.
        If `None`, |discretize_domain_default| is used.
    grid_type
        If not `None`, this parameter is forwarded to `domain_discretizer` to specify
        the type of the generated |Grid|.
    num_flux
        The numerical flux to use in the finite volume formulation. Allowed
        values are `'lax_friedrichs'`, `'engquist_osher'`, `'simplified_engquist_osher'`
        (see :mod:`pymor.operators.fv`).
    lxf_lambda
        The stabilization parameter for the Lax-Friedrichs numerical flux
        (ignored, if different flux is chosen).
    eo_gausspoints
        Number of Gauss points for the Engquist-Osher numerical flux
        (ignored, if different flux is chosen).
    eo_intervals
        Number of sub-intervals to use for integration when using Engquist-Osher
        numerical flux (ignored, if different flux is chosen).
    grid
        Instead of using a domain discretizer, the |Grid| can also be passed directly
        using this parameter.
    boundary_info
        A |BoundaryInfo| specifying the boundary types of the grid boundary entities.
        Must be provided if `grid` is specified.
    preassemble
        If `True`, preassemble all operators in the resulting |Discretization|.

    Returns
    -------
    discretization
        The |Discretization| that has been generated.
    data
        Dictionary with the following entries:

            :grid:           The generated |Grid|.
            :boundary_info:  The generated |BoundaryInfo|.
    """

    assert isinstance(analytical_problem, StationaryProblem)
    assert grid is None or boundary_info is not None
    assert boundary_info is None or grid is not None
    assert grid is None or domain_discretizer is None
    assert grid_type is None or grid is None

    p = analytical_problem

    if analytical_problem.robin_data is not None:
        raise NotImplementedError

    if grid is None:
        domain_discretizer = domain_discretizer or discretize_domain_default
        if grid_type:
            domain_discretizer = partial(domain_discretizer,
                                         grid_type=grid_type)
        if diameter is None:
            grid, boundary_info = domain_discretizer(analytical_problem.domain)
        else:
            grid, boundary_info = domain_discretizer(analytical_problem.domain,
                                                     diameter=diameter)

    L, L_coefficients = [], []
    F, F_coefficients = [], []

    if p.rhs is not None or p.neumann_data is not None:
        F += [
            L2ProductFunctional(grid,
                                p.rhs,
                                boundary_info=boundary_info,
                                neumann_data=p.neumann_data)
        ]
        F_coefficients += [1.]

    # diffusion part
    if isinstance(p.diffusion, LincombFunction):
        L += [
            DiffusionOperator(grid,
                              boundary_info,
                              diffusion_function=df,
                              name='diffusion_{}'.format(i))
            for i, df in enumerate(p.diffusion.functions)
        ]
        L_coefficients += p.diffusion.coefficients
        if p.dirichlet_data is not None:
            F += [
                L2ProductFunctional(grid,
                                    None,
                                    boundary_info=boundary_info,
                                    dirichlet_data=p.dirichlet_data,
                                    diffusion_function=df,
                                    name='dirichlet_{}'.format(i))
                for i, df in enumerate(p.diffusion.functions)
            ]
            F_coefficients += p.diffusion.coefficients

    elif p.diffusion is not None:
        L += [
            DiffusionOperator(grid,
                              boundary_info,
                              diffusion_function=p.diffusion,
                              name='diffusion')
        ]
        L_coefficients += [1.]
        if p.dirichlet_data is not None:
            F += [
                L2ProductFunctional(grid,
                                    None,
                                    boundary_info=boundary_info,
                                    dirichlet_data=p.dirichlet_data,
                                    diffusion_function=p.diffusion,
                                    name='dirichlet')
            ]
            F_coefficients += [1.]

    # advection part
    if isinstance(p.advection, LincombFunction):
        L += [
            LinearAdvectionLaxFriedrichs(grid,
                                         boundary_info,
                                         af,
                                         name='advection_{}'.format(i))
            for i, af in enumerate(p.advection.functions)
        ]
        L_coefficients += list(p.advection.coefficients)
    elif p.advection is not None:
        L += [
            LinearAdvectionLaxFriedrichs(grid,
                                         boundary_info,
                                         p.advection,
                                         name='advection')
        ]
        L_coefficients.append(1.)

    # nonlinear advection part
    if p.nonlinear_advection is not None:
        if num_flux == 'lax_friedrichs':
            L += [
                nonlinear_advection_lax_friedrichs_operator(
                    grid,
                    boundary_info,
                    p.nonlinear_advection,
                    dirichlet_data=p.dirichlet_data,
                    lxf_lambda=lxf_lambda)
            ]
        elif num_flux == 'upwind':
            L += [
                nonlinear_advection_upwind_operator(
                    grid,
                    boundary_info,
                    p.nonlinear_advection,
                    p.nonlinear_advection_derivative,
                    dirichlet_data=p.dirichlet_data)
            ]
        elif num_flux == 'engquist_osher':
            L += [
                nonlinear_advection_engquist_osher_operator(
                    grid,
                    boundary_info,
                    p.nonlinear_advection,
                    p.nonlinear_advection_derivative,
                    gausspoints=eo_gausspoints,
                    intervals=eo_intervals,
                    dirichlet_data=p.dirichlet_data)
            ]
        elif num_flux == 'simplified_engquist_osher':
            L += [
                nonlinear_advection_simplified_engquist_osher_operator(
                    grid,
                    boundary_info,
                    p.nonlinear_advection,
                    p.nonlinear_advection_derivative,
                    dirichlet_data=p.dirichlet_data)
            ]
        else:
            raise NotImplementedError
        L_coefficients.append(1.)

    # reaction part
    if isinstance(p.reaction, LincombFunction):
        raise NotImplementedError
    elif p.reaction is not None:
        L += [ReactionOperator(grid, p.reaction, name='reaction')]
        L_coefficients += [1.]

    # nonlinear reaction part
    if p.nonlinear_reaction is not None:
        L += [
            NonlinearReactionOperator(grid, p.nonlinear_reaction,
                                      p.nonlinear_reaction_derivative)
        ]
        L_coefficients += [1.]

    # system operator
    if len(L_coefficients) == 1 and L_coefficients[0] == 1.:
        L = L[0]
    else:
        L = LincombOperator(operators=L,
                            coefficients=L_coefficients,
                            name='elliptic_operator')

    # rhs
    if len(F_coefficients) == 0:
        F = ZeroOperator(L.range, NumpyVectorSpace(1))
    elif len(F_coefficients) == 1 and F_coefficients[0] == 1.:
        F = F[0]
    else:
        F = LincombOperator(operators=F,
                            coefficients=F_coefficients,
                            name='rhs')

    if grid.reference_element in (triangle, square):
        visualizer = PatchVisualizer(grid=grid,
                                     bounding_box=grid.bounding_box(),
                                     codim=0)
    elif grid.reference_element is line:
        visualizer = OnedVisualizer(grid=grid, codim=0)
    else:
        visualizer = None

    l2_product = L2Product(grid, name='l2')
    products = {'l2': l2_product}

    parameter_space = p.parameter_space if hasattr(p,
                                                   'parameter_space') else None

    discretization = StationaryDiscretization(L,
                                              F,
                                              products=products,
                                              visualizer=visualizer,
                                              parameter_space=parameter_space,
                                              name='{}_FV'.format(p.name))

    data = {'grid': grid, 'boundary_info': boundary_info}

    if preassemble:
        data['unassembled_discretization'] = discretization
        discretization = preassemble_(discretization)

    return discretization, data
Esempio n. 11
0
def discretize_stationary_from_disk(parameter_file):
    """Load a linear affinely decomposed |StationaryDiscretization| from file.

    The discretization is defined via an `.ini`-style file as follows ::

        [system-matrices]
        L_1.mat: l_1(μ_1,...,μ_n)
        L_2.mat: l_2(μ_1,...,μ_n)
        ...

        [rhs-vectors]
        F_1.mat: f_1(μ_1,...,μ_n)
        F_2.mat: f_2(μ_1,...,μ_n)
        ...

        [parameter]
        μ_1: a_1,b_1
        ...
        μ_n: a_n,b_n

        [products]
        Prod1: P_1.mat
        Prod2: P_2.mat
        ...

    Here, `L_1.mat`, `L_2.mat`, ..., `F_1.mat`, `F_2.mat`, ... are files
    containing matrices `L_1`, `L_2`, ... and vectors `F_1.mat`, `F_2.mat`, ...
    which correspond to the affine components of the operator and right-hand
    side functional.  The respective coefficient functionals, are given via the
    string expressions `l_1(...)`, `l_2(...)`, ..., `f_1(...)` in the
    (scalar-valued) |Parameter| components `w_1`, ..., `w_n`. The allowed lower
    and upper bounds `a_i, b_i` for the component `μ_i` are specified in the
    `[parameters]` section. The resulting operator and right-hand side are
    then of the form ::

        L(μ) = l_1(μ)*L_1 + l_2(μ)*L_2+ ...
        F(μ) = f_1(μ)*F_1 + f_2(μ)*L_2+ ...

    In the `[products]` section, an optional list of inner products `Prod1`, `Prod2`, ..
    with corresponding matrices `P_1.mat`, `P_2.mat` can be specified.

    Example::

        [system-matrices]
        matrix1.mat: 1.
        matrix2.mat: 1. - theta**2

        [rhs-vectors]
        rhs.mat: 1.

        [parameter]
        theta: 0, 0.5

        [products]
        h1: h1.mat
        l2: mass.mat


    Parameters
    ----------
    parameter_file
        Path to the parameter file.

    Returns
    -------
    discretization
        The |StationaryDiscretization| that has been generated.
    """
    assert ".ini" == parameter_file[
        -4:], 'Given file is not an .ini file: {}'.format(parameter_file)
    assert os.path.isfile(parameter_file)
    base_path = os.path.dirname(parameter_file)

    # Get input from parameter file
    config = configparser.ConfigParser()
    config.optionxform = str
    config.read(parameter_file)

    # Assert that all needed entries given
    assert 'system-matrices' in config.sections()
    assert 'rhs-vectors' in config.sections()
    assert 'parameter' in config.sections()

    system_mat = config.items('system-matrices')
    rhs_vec = config.items('rhs-vectors')
    parameter = config.items('parameter')

    # Dict of parameters types and ranges
    parameter_type = {}
    parameter_range = {}

    # get parameters
    for i in range(len(parameter)):
        parameter_name = parameter[i][0]
        parameter_list = tuple(
            float(j) for j in parameter[i][1].replace(" ", "").split(','))
        parameter_range[parameter_name] = parameter_list
        # Assume scalar parameter dependence
        parameter_type[parameter_name] = 0

    # Create parameter space
    parameter_space = CubicParameterSpace(parameter_type=parameter_type,
                                          ranges=parameter_range)

    # Assemble operators
    system_operators, system_functionals = [], []

    # get parameter functionals and system matrices
    for i in range(len(system_mat)):
        path = os.path.join(base_path, system_mat[i][0])
        expr = system_mat[i][1]
        parameter_functional = ExpressionParameterFunctional(
            expr, parameter_type=parameter_type)
        system_operators.append(
            NumpyMatrixOperator.from_file(path,
                                          source_id='STATE',
                                          range_id='STATE'))
        system_functionals.append(parameter_functional)

    system_lincombOperator = LincombOperator(system_operators,
                                             coefficients=system_functionals)

    # get rhs vectors
    rhs_operators, rhs_functionals = [], []

    for i in range(len(rhs_vec)):
        path = os.path.join(base_path, rhs_vec[i][0])
        expr = rhs_vec[i][1]
        parameter_functional = ExpressionParameterFunctional(
            expr, parameter_type=parameter_type)
        op = NumpyMatrixOperator.from_file(path, source_id='STATE')
        assert isinstance(op._matrix, np.ndarray)
        op = op.with_(matrix=op._matrix.reshape((1, -1)))
        rhs_operators.append(op)
        rhs_functionals.append(parameter_functional)

    rhs_lincombOperator = LincombOperator(rhs_operators,
                                          coefficients=rhs_functionals)

    # get products if given
    if 'products' in config.sections():
        product = config.items('products')
        products = {}
        for i in range(len(product)):
            product_name = product[i][0]
            product_path = os.path.join(base_path, product[i][1])
            products[product_name] = NumpyMatrixOperator.from_file(
                product_path, source_id='STATE', range_id='STATE')
    else:
        products = None

    # Create and return stationary discretization
    return StationaryDiscretization(operator=system_lincombOperator,
                                    rhs=rhs_lincombOperator,
                                    parameter_space=parameter_space,
                                    products=products)
Esempio n. 12
0
param = {"lambda": [1.], "mu": [1.]}
# u = cpp_disc.solve(param)
parameter_type = Parameter(param).parameter_type
# cpp_disc.visualize(u, "highdim_solution_cpp.vtk")

lambda_fn, mu_fn = [GenericParameterFunctional(lambda mu: mu[n], Parameter({n: [1.]}).parameter_type) for n in ['lambda', 'mu']]
LOW, HIGH = 1, 10
ops = [DealIIMatrixOperator(getattr(cpp_disc, name)()) for name in ['lambda_mat', 'mu_mat']]
op = LincombOperator(ops, (lambda_fn, mu_fn))
rhs = VectorFunctional(ListVectorArray([DealIIVector(cpp_disc.rhs())]))
viz = PyVis(cpp_disc)
h1_op = DealIIMatrixOperator(cpp_disc.h1_mat(), "h1_0_semi")
energy_op = DealIIMatrixOperator(cpp_disc.mu_mat(), "energy")
py_disc = StationaryDiscretization(op, rhs, products={"energy": energy_op},
                                   visualizer=viz,
                                   parameter_space=CubicParameterSpace(parameter_type, LOW, HIGH))

coercivity_estimator = ExpressionParameterFunctional("max(mu)", parameter_type)
reductor = partial(reduce_stationary_coercive,
                   error_product=energy_op, coercivity_estimator=coercivity_estimator)

greedy_data = greedy(py_disc, reductor, py_disc.parameter_space.sample_uniformly(3),
                     use_estimator=True,
                     extension_algorithm=gram_schmidt_basis_extension, max_extensions=3)
rb_disc, reconstructor = greedy_data['reduced_discretization'], greedy_data['reconstructor']

half = (HIGH - LOW) / 2.
values = itertools.product((LOW, HIGH, half), (LOW, HIGH, half))
for new_param in ({"lambda": [a], "mu": [b]} for a, b in values):
    for disc, s in [(cpp_disc, 'cpp'), (py_disc, 'py'), (rb_disc, 'rb')]:
Esempio n. 13
0
def discretize_elliptic_cg(analytical_problem,
                           diameter=None,
                           domain_discretizer=None,
                           grid=None,
                           boundary_info=None):
    """Discretizes an |EllipticProblem| using finite elements.

    Parameters
    ----------
    analytical_problem
        The |EllipticProblem| to discretize.
    diameter
        If not `None`, `diameter` is passed to the `domain_discretizer`.
    domain_discretizer
        Discretizer to be used for discretizing the analytical domain. This has
        to be a function `domain_discretizer(domain_description, diameter, ...)`.
        If further arguments should be passed to the discretizer, use
        :func:`functools.partial`. If `None`, |discretize_domain_default| is used.
    grid
        Instead of using a domain discretizer, the |Grid| can also be passed directly
        using this parameter.
    boundary_info
        A |BoundaryInfo| specifying the boundary types of the grid boundary entities.
        Must be provided if `grid` is specified.

    Returns
    -------
    discretization
        The |Discretization| that has been generated.
    data
        Dictionary with the following entries:

            :grid:           The generated |Grid|.
            :boundary_info:  The generated |BoundaryInfo|.
    """

    assert isinstance(analytical_problem, EllipticProblem)
    assert grid is None or boundary_info is not None
    assert boundary_info is None or grid is not None
    assert grid is None or domain_discretizer is None

    if grid is None:
        domain_discretizer = domain_discretizer or discretize_domain_default
        if diameter is None:
            grid, boundary_info = domain_discretizer(analytical_problem.domain)
        else:
            grid, boundary_info = domain_discretizer(analytical_problem.domain,
                                                     diameter=diameter)

    assert grid.reference_element in (line, triangle, square)

    if grid.reference_element is square:
        DiffusionOperator = cg.DiffusionOperatorQ1
        AdvectionOperator = cg.AdvectionOperatorQ1
        ReactionOperator = cg.L2ProductQ1
        Functional = cg.L2ProductFunctionalQ1
    else:
        DiffusionOperator = cg.DiffusionOperatorP1
        AdvectionOperator = cg.AdvectionOperatorP1
        ReactionOperator = cg.L2ProductP1
        Functional = cg.L2ProductFunctionalP1

    p = analytical_problem

    if p.diffusion_functionals is not None or p.advection_functionals is not None or p.reaction_functionals is not None:
        # parametric case
        Li = [
            DiffusionOperator(grid,
                              boundary_info,
                              diffusion_constant=0,
                              name='boundary_part')
        ]
        coefficients = [1.]

        # diffusion part
        if p.diffusion_functionals is not None:
            Li += [
                DiffusionOperator(grid,
                                  boundary_info,
                                  diffusion_function=df,
                                  dirichlet_clear_diag=True,
                                  name='diffusion_{}'.format(i))
                for i, df in enumerate(p.diffusion_functions)
            ]
            coefficients += list(p.diffusion_functionals)
        elif p.diffusion_functions is not None:
            assert len(p.diffusion_functions) == 1
            Li += [
                DiffusionOperator(grid,
                                  boundary_info,
                                  diffusion_function=p.diffusion_functions[0],
                                  dirichlet_clear_diag=True,
                                  name='diffusion')
            ]
            coefficients.append(1.)

        # advection part
        if p.advection_functionals is not None:
            Li += [
                AdvectionOperator(grid,
                                  boundary_info,
                                  advection_function=af,
                                  dirichlet_clear_diag=True,
                                  name='advection_{}'.format(i))
                for i, af in enumerate(p.advection_functions)
            ]
            coefficients += list(p.advection_functionals)
        elif p.advection_functions is not None:
            assert len(p.advection_functions) == 1
            Li += [
                AdvectionOperator(grid,
                                  boundary_info,
                                  advection_function=p.advection_functions[0],
                                  dirichlet_clear_diag=True,
                                  name='advection')
            ]
            coefficients.append(1.)

        # reaction part
        if p.reaction_functionals is not None:
            Li += [
                ReactionOperator(grid,
                                 boundary_info,
                                 coefficient_function=rf,
                                 dirichlet_clear_diag=True,
                                 name='reaction_{}'.format(i))
                for i, rf in enumerate(p.reaction_functions)
            ]
            coefficients += list(p.reaction_functionals)
        elif p.reaction_functions is not None:
            assert len(p.reaction_functions) == 1
            Li += [
                ReactionOperator(grid,
                                 boundary_info,
                                 coefficient_function=p.reaction_functions[0],
                                 dirichlet_clear_diag=True,
                                 name='reaction')
            ]
            coefficients.append(1.)

        # robin boundaries
        if p.robin_data is not None:
            Li += [
                cg.RobinBoundaryOperator(grid,
                                         boundary_info,
                                         robin_data=p.robin_data,
                                         order=2,
                                         name='robin')
            ]
            coefficients.append(1.)

        L = LincombOperator(operators=Li,
                            coefficients=coefficients,
                            name='ellipticOperator')
    else:
        # unparametric case, not operator for boundary treatment
        Li = []

        # only one operator has diagonal values, all subsequent operators have clear_diag
        dirichlet_clear_diag = False
        # diffusion part
        if p.diffusion_functions is not None:
            assert len(p.diffusion_functions) == 1
            Li += [
                DiffusionOperator(grid,
                                  boundary_info,
                                  diffusion_function=p.diffusion_functions[0],
                                  dirichlet_clear_diag=dirichlet_clear_diag,
                                  name='diffusion')
            ]
            dirichlet_clear_diag = True

        # advection part
        if p.advection_functions is not None:
            assert len(p.advection_functions) == 1
            Li += [
                AdvectionOperator(grid,
                                  boundary_info,
                                  advection_function=p.advection_functions[0],
                                  dirichlet_clear_diag=dirichlet_clear_diag,
                                  name='advection')
            ]
            dirichlet_clear_diag = True

        # reaction part
        if p.reaction_functions is not None:
            assert len(p.reaction_functions) == 1
            Li += [
                ReactionOperator(grid,
                                 boundary_info,
                                 coefficient_function=p.reaction_functions[0],
                                 dirichlet_clear_diag=dirichlet_clear_diag,
                                 name='reaction')
            ]
            dirichlet_clear_diag = True

        # robin boundaries
        if p.robin_data is not None:
            Li += [
                cg.RobinBoundaryOperator(grid,
                                         boundary_info,
                                         robin_data=p.robin_data,
                                         order=2,
                                         name='robin')
            ]

        if len(Li) == 1:
            L = Li[0]
        else:
            L = LincombOperator(operators=Li,
                                coefficients=[1.] * len(Li),
                                name='ellipticOperator')

    F = Functional(grid,
                   p.rhs,
                   boundary_info,
                   dirichlet_data=p.dirichlet_data,
                   neumann_data=p.neumann_data)

    if grid.reference_element in (triangle, square):
        visualizer = PatchVisualizer(grid=grid,
                                     bounding_box=grid.bounding_box(),
                                     codim=2)
    elif grid.reference_element is line:
        visualizer = Matplotlib1DVisualizer(grid=grid, codim=1)
    else:
        visualizer = None

    Prod = cg.L2ProductQ1 if grid.reference_element is square else cg.L2ProductP1
    empty_bi = EmptyBoundaryInfo(grid)
    l2_product = Prod(grid, empty_bi, name='l2')
    l2_0_product = Prod(grid,
                        boundary_info,
                        dirichlet_clear_columns=True,
                        name='l2_0')
    h1_semi_product = DiffusionOperator(grid, empty_bi, name='h1_semi')
    h1_0_semi_product = DiffusionOperator(grid,
                                          boundary_info,
                                          dirichlet_clear_columns=True,
                                          name='h1_0_semi')
    products = {
        'h1': l2_product + h1_semi_product,
        'h1_semi': h1_semi_product,
        'l2': l2_product,
        'h1_0': l2_0_product + h1_0_semi_product,
        'h1_0_semi': h1_0_semi_product,
        'l2_0': l2_0_product
    }

    parameter_space = p.parameter_space if hasattr(p,
                                                   'parameter_space') else None

    discretization = StationaryDiscretization(L,
                                              F,
                                              products=products,
                                              visualizer=visualizer,
                                              parameter_space=parameter_space,
                                              name='{}_CG'.format(p.name))

    return discretization, {'grid': grid, 'boundary_info': boundary_info}