Пример #1
0
def test_to_matrix_Concatenation():
    np.random.seed(0)
    A = np.random.randn(2, 3)
    B = np.random.randn(3, 4)
    C = A.dot(B)

    Aop = NumpyMatrixOperator(A)
    Bop = NumpyMatrixOperator(B)
    Cop = Concatenation([Aop, Bop])
    assert_type_and_allclose(C, Cop, 'dense')

    Aop = NumpyMatrixOperator(sps.csc_matrix(A))
    Bop = NumpyMatrixOperator(B)
    Cop = Concatenation([Aop, Bop])
    assert_type_and_allclose(C, Cop, 'dense')

    Aop = NumpyMatrixOperator(A)
    Bop = NumpyMatrixOperator(sps.csc_matrix(B))
    Cop = Concatenation([Aop, Bop])
    assert_type_and_allclose(C, Cop, 'dense')

    Aop = NumpyMatrixOperator(sps.csc_matrix(A))
    Bop = NumpyMatrixOperator(sps.csc_matrix(B))
    Cop = Concatenation([Aop, Bop])
    assert_type_and_allclose(A, Aop, 'sparse')
Пример #2
0
def test_to_matrix_LincombOperator():
    np.random.seed(0)
    A = np.random.randn(3, 3)
    B = np.random.randn(3, 2)
    a = np.random.randn()
    b = np.random.randn()
    C = a * A + b * B.dot(B.T)

    Aop = NumpyMatrixOperator(A)
    Bop = NumpyMatrixOperator(B)
    Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b])
    assert_type_and_allclose(C, Cop, 'dense')

    Aop = NumpyMatrixOperator(sps.csc_matrix(A))
    Bop = NumpyMatrixOperator(B)
    Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b])
    assert_type_and_allclose(C, Cop, 'dense')

    Aop = NumpyMatrixOperator(A)
    Bop = NumpyMatrixOperator(sps.csc_matrix(B))
    Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b])
    assert_type_and_allclose(C, Cop, 'dense')

    Aop = NumpyMatrixOperator(sps.csc_matrix(A))
    Bop = NumpyMatrixOperator(sps.csc_matrix(B))
    Cop = LincombOperator([Aop, Concatenation([Bop, Bop.T])], [a, b])
    assert_type_and_allclose(C, Cop, 'sparse')
Пример #3
0
def test_to_matrix():
    np.random.seed(0)
    A = np.random.randn(2, 2)
    B = np.random.randn(3, 3)
    C = np.random.randn(3, 3)

    X = np.bmat([[np.eye(2) + A, np.zeros((2, 3))],
                 [np.zeros((3, 2)), B.dot(C.T)]])

    C = sps.csc_matrix(C)

    Aop = NumpyMatrixOperator(A)
    Bop = NumpyMatrixOperator(B)
    Cop = NumpyMatrixOperator(C)

    Xop = BlockDiagonalOperator([
        LincombOperator([IdentityOperator(NumpyVectorSpace(2)), Aop], [1, 1]),
        Concatenation(Bop, AdjointOperator(Cop))
    ])

    assert np.allclose(X, to_matrix(Xop))
    assert np.allclose(X, to_matrix(Xop, format='csr').toarray())

    np.random.seed(0)
    V = np.random.randn(10, 2)
    Vva = NumpyVectorArray(V.T)
    Vop = VectorArrayOperator(Vva)
    assert np.allclose(V, to_matrix(Vop))
    Vop = VectorArrayOperator(Vva, transposed=True)
    assert np.allclose(V, to_matrix(Vop).T)
Пример #4
0
 def action_Concatenation(self,
                          op,
                          range_basis,
                          source_basis,
                          product=None):
     if source_basis is not None and op.first.linear and not op.first.parametric:
         V = op.first.apply(source_basis)
         return self.apply(op.second, range_basis, V, product=product)
     elif range_basis is not None and op.second.linear and not op.second.parametric:
         if product:
             range_basis = product.apply(range_basis)
         V = op.second.apply_transpose(range_basis)
         return self.apply(op.first, V, source_basis)
     else:
         projected_first = self.apply(op.first,
                                      None,
                                      source_basis,
                                      product=None)
         projected_second = self.apply(op.second,
                                       range_basis,
                                       None,
                                       product=product)
         return Concatenation(projected_second,
                              projected_first,
                              name=op.name)
Пример #5
0
    def action_Concatenation(self, op):
        if len(op.operators) == 1:
            return self.apply(op.operators[0])

        range_basis, source_basis, product = self.range_basis, self.source_basis, self.product
        last, first = op.operators[0], op.operators[-1]

        if source_basis is not None and first.linear and not first.parametric:
            V = first.apply(source_basis)
            return type(self)(range_basis, V, product).apply(
                op.with_(operators=op.operators[:-1]))
        elif range_basis is not None and last.linear and not last.parametric:
            if product:
                range_basis = product.apply(range_basis)
            V = last.apply_adjoint(range_basis)
            return type(self)(V, source_basis,
                              None).apply(op.with_(operators=op.operators[1:]))
        else:
            projected_first = type(self)(None, source_basis,
                                         product=None).apply(first)
            projected_last = type(self)(range_basis, None,
                                        product=product).apply(last)
            return Concatenation(
                (projected_last, ) + op.operators[1:-1] + (projected_first, ),
                name=op.name)
Пример #6
0
    def project_operators(self):
        fom = self.fom
        RB = self.bases['RB']
        product = self.products['RB']

        if self.initial_data_product != product:
            # TODO there should be functionality for this somewhere else
            projection_matrix = RB.gramian(self.initial_data_product)
            projection_op = NumpyMatrixOperator(projection_matrix)
            inverse_projection_op = InverseOperator(projection_op, 'inverse_projection_op')
            pid = project(fom.initial_data, range_basis=RB, source_basis=None, product=self.initial_data_product)
            projected_initial_data = Concatenation([inverse_projection_op, pid])
        else:
            projected_initial_data = project(fom.initial_data, range_basis=RB, source_basis=None,
                                             product=product)

        projected_operators = {
            'mass':              None if fom.mass is None or self.product_is_mass else project(fom.mass, RB, RB),
            'operator':          project(fom.operator, RB, RB),
            'rhs':               project(fom.rhs, RB, None) if fom.rhs is not None else None,
            'initial_data':      projected_initial_data,
            'products':          {k: project(v, RB, RB) for k, v in fom.products.items()},
            'output_functional': project(fom.output_functional, None, RB) if fom.output_functional else None
        }

        return projected_operators
Пример #7
0
    def project_operators_to_subbasis(self, dims):
        rom = self._last_rom
        dim = dims['RB']
        product = self.products['RB']

        if self.initial_data_product != product:
            # TODO there should be functionality for this somewhere else
            pop = project_to_subbasis(rom.initial_data.operators[1], dim_range=dim, dim_source=None)
            inverse_projection_op = InverseOperator(
                project_to_subbasis(rom.initial_data.operators[0].operator, dim_range=dim, dim_source=dim),
                name='inverse_projection_op'
            )
            projected_initial_data = Concatenation([inverse_projection_op, pop])
        else:
            projected_initial_data = project_to_subbasis(rom.initial_data, dim_range=dim, dim_source=None)

        projected_operators = {
            'mass':              (None if rom.mass is None or self.product_is_mass else
                                  project_to_subbasis(rom.mass, dim, dim)),
            'operator':          project_to_subbasis(rom.operator, dim, dim),
            'rhs':               project_to_subbasis(rom.rhs, dim, None) if rom.rhs is not None else None,
            'initial_data':      projected_initial_data,
            'products':          {k: project_to_subbasis(v, dim, dim) for k, v in rom.products.items()},
            'output_functional': project_to_subbasis(rom.output_functional, None, dim) if rom.output_functional else None
        }
        return projected_operators
Пример #8
0
    def jacobian(self, U, mu=None):
        mu = self.parse_parameter(mu)

        if len(self.interpolation_dofs) == 0:
            if self.source.type == self.range.type == NumpyVectorArray:
                return NumpyMatrixOperator(np.zeros((0, self.source.dim)), name=self.name + '_jacobian')
            else:
                return ZeroOperator(self.source, self.range, name=self.name + '_jacobian')
        elif hasattr(self, 'operator'):
            return EmpiricalInterpolatedOperator(self.operator.jacobian(U, mu=mu), self.interpolation_dofs,
                                                 self.collateral_basis, self.triangular, self.name + '_jacobian')
        else:
            U_components = NumpyVectorArray(U.components(self.source_dofs), copy=False)
            JU = self.restricted_operator.jacobian(U_components, mu=mu) \
                                         .apply(NumpyVectorArray(np.eye(len(self.source_dofs)), copy=False))
            try:
                if self.triangular:
                    interpolation_coefficients = solve_triangular(self.interpolation_matrix, JU.data.T,
                                                                  lower=True, unit_diagonal=True).T
                else:
                    interpolation_coefficients = np.linalg.solve(self.interpolation_matrix, JU._array.T).T
            except ValueError:  # this exception occurs when AU contains NaNs ...
                interpolation_coefficients = np.empty((len(JU), len(self.collateral_basis))) + np.nan
            J = self.collateral_basis.lincomb(interpolation_coefficients)
            if isinstance(J, NumpyVectorArray):
                J = NumpyMatrixOperator(J.data.T)
            else:
                J = VectorArrayOperator(J, copy=False)
            return Concatenation(J, ComponentProjection(self.source_dofs, self.source), name=self.name + '_jacobian')
Пример #9
0
    def jacobian(self, U, mu=None):
        mu = self.parse_parameter(mu)
        options = self.solver_options.get('jacobian') if self.solver_options else None

        if len(self.interpolation_dofs) == 0:
            if isinstance(self.source, NumpyVectorSpace) and isinstance(self.range, NumpyVectorSpace):
                return NumpyMatrixOperator(np.zeros((self.range.dim, self.source.dim)), solver_options=options,
                                           source_id=self.source.id, range_id=self.range.id,
                                           name=self.name + '_jacobian')
            else:
                return ZeroOperator(self.range, self.source, name=self.name + '_jacobian')
        elif hasattr(self, 'operator'):
            return EmpiricalInterpolatedOperator(self.operator.jacobian(U, mu=mu), self.interpolation_dofs,
                                                 self.collateral_basis, self.triangular,
                                                 solver_options=options, name=self.name + '_jacobian')
        else:
            restricted_source = self.restricted_operator.source
            U_dofs = restricted_source.make_array(U.dofs(self.source_dofs))
            JU = self.restricted_operator.jacobian(U_dofs, mu=mu) \
                                         .apply(restricted_source.make_array(np.eye(len(self.source_dofs))))
            try:
                if self.triangular:
                    interpolation_coefficients = solve_triangular(self.interpolation_matrix, JU.to_numpy().T,
                                                                  lower=True, unit_diagonal=True).T
                else:
                    interpolation_coefficients = solve(self.interpolation_matrix, JU.to_numpy().T).T
            except ValueError:  # this exception occurs when AU contains NaNs ...
                interpolation_coefficients = np.empty((len(JU), len(self.collateral_basis))) + np.nan
            J = self.collateral_basis.lincomb(interpolation_coefficients)
            if isinstance(J.space, NumpyVectorSpace):
                J = NumpyMatrixOperator(J.to_numpy().T, range_id=self.range.id)
            else:
                J = VectorArrayOperator(J)
            return Concatenation([J, ComponentProjection(self.source_dofs, self.source)],
                                 solver_options=options, name=self.name + '_jacobian')
Пример #10
0
 def __matmul__(self, other):
     if not isinstance(other, OperatorInterface):
         return NotImplemented
     from pymor.operators.constructions import Concatenation
     if isinstance(other, Concatenation):
         return NotImplemented
     else:
         return Concatenation((self, other))
Пример #11
0
 def restricted(self, dofs):
     source_dofs = np.setdiff1d(np.union1d(self.grid.neighbours(0, 0)[dofs].ravel(), dofs),
                                np.array([-1], dtype=np.int32),
                                assume_unique=True)
     sub_grid = SubGrid(self.grid, entities=source_dofs)
     sub_boundary_info = SubGridBoundaryInfo(sub_grid, self.grid, self.boundary_info)
     op = self.with_(grid=sub_grid, boundary_info=sub_boundary_info, name='{}_restricted'.format(self.name))
     sub_grid_indices = sub_grid.indices_from_parent_indices(dofs, codim=0)
     proj = ComponentProjection(sub_grid_indices, op.range)
     return Concatenation(proj, op), sub_grid.parent_indices(0)
Пример #12
0
 def collect_functional_ranges(op, image):
     if isinstance(op, (LincombOperator, SelectionOperator)):
         for o in op.operators:
             collect_functional_ranges(o, image)
     elif isinstance(op, AdjointOperator):
         operator = Concatenation(
             op.range_product,
             op.operator) if op.range_product else op.operator
         collect_operator_ranges(operator, NumpyVectorArray(np.ones(1)),
                                 image)
     elif op.linear and not op.parametric:
         image.append(op.as_vector())
     else:
         raise ImageCollectionError(op)
Пример #13
0
    def action_Concatenation(self, op):
        if len(op.operators) == 1:
            return self.apply(op.operators[0])

        range_basis, source_basis = self.range_basis, self.source_basis
        last, first = op.operators[0], op.operators[-1]

        if source_basis is not None and first.linear and not first.parametric:
            V = first.apply(source_basis)
            return project(op.with_(operators=op.operators[:-1]), range_basis, V)
        elif range_basis is not None and last.linear and not last.parametric:
            V = last.apply_adjoint(range_basis)
            return project(op.with_(operators=op.operators[1:]), V, source_basis)
        else:
            projected_first = project(first, None, source_basis)
            projected_last = project(last, range_basis, None)
            return Concatenation((projected_last,) + op.operators[1:-1] + (projected_first,), name=op.name)
def assemble_estimator_diffusive_flux_bb(grid, ii, subdomain_rt_spaces,
                                         lambda_hat, kappa,
                                         local_rt_projection):
    diffusive_flux_bb_product = make_diffusive_flux_bb_product(
        grid,
        ii,
        subdomain_rt_spaces[ii],
        lambda_hat,
        kappa=kappa,
        over_integrate=2)
    subdomain_walker = make_subdomain_walker(grid, ii)
    subdomain_walker.append(diffusive_flux_bb_product)
    subdomain_walker.walk()
    # subdomain_rt_spaces[ii].dof_communicator,
    matrix = DuneXTMatrixOperator(diffusive_flux_bb_product.matrix(),
                                  range_id='LOCALRT_{}'.format(ii),
                                  source_id='LOCALRT_{}'.format(ii))
    return Concatenation([local_rt_projection.T, matrix, local_rt_projection],
                         name='diffusive_flux_bb_{}'.format(ii))
Пример #15
0
def thermalblock_concatenation_factory(xblocks, yblocks, diameter, seed):
    from pymor.operators.constructions import Concatenation
    op, mu, U, V, sp, rp = thermalblock_factory(xblocks, yblocks, diameter,
                                                seed)
    op = Concatenation(sp, op)
    return op, mu, U, V, sp, rp
def discretize(grid_and_problem_data, solver_options, mpi_comm):
    ################ Setup

    logger = getLogger('discretize_elliptic_block_swipdg.discretize')
    logger.info('discretizing ... ')

    grid, boundary_info = grid_and_problem_data['grid'], grid_and_problem_data[
        'boundary_info']
    local_all_dirichlet_boundary_info = make_subdomain_boundary_info(
        grid, {'type': 'xt.grid.boundaryinfo.alldirichlet'})
    local_subdomains, num_local_subdomains, num_global_subdomains = _get_subdomains(
        grid)
    local_all_neumann_boundary_info = make_subdomain_boundary_info(
        grid, {'type': 'xt.grid.boundaryinfo.allneumann'})

    block_space = make_block_dg_space(grid)
    global_rt_space = make_rt_space(grid)
    subdomain_rt_spaces = [
        global_rt_space.restrict_to_dd_subdomain_view(grid, ii)
        for ii in range(num_global_subdomains)
    ]

    local_patterns = [
        block_space.local_space(ii).compute_pattern('face_and_volume')
        for ii in range(block_space.num_blocks)
    ]
    coupling_patterns = {
        'in_in': {},
        'out_out': {},
        'in_out': {},
        'out_in': {}
    }
    coupling_matrices = {
        'in_in': {},
        'out_out': {},
        'in_out': {},
        'out_in': {}
    }

    for ii in range(num_global_subdomains):
        ii_size = block_space.local_space(ii).size()
        for jj in grid.neighboring_subdomains(ii):
            jj_size = block_space.local_space(jj).size()
            if ii < jj:  # Assemble primally (visit each coupling only once).
                coupling_patterns['in_in'][(ii, jj)] = block_space.local_space(
                    ii).compute_pattern('face_and_volume')
                coupling_patterns['out_out'][(
                    ii, jj)] = block_space.local_space(jj).compute_pattern(
                        'face_and_volume')
                coupling_patterns['in_out'][(
                    ii, jj)] = block_space.compute_coupling_pattern(
                        ii, jj, 'face')
                coupling_patterns['out_in'][(
                    ii, jj)] = block_space.compute_coupling_pattern(
                        jj, ii, 'face')
                coupling_matrices['in_in'][(ii, jj)] = Matrix(
                    ii_size, ii_size, coupling_patterns['in_in'][(ii, jj)])
                coupling_matrices['out_out'][(ii, jj)] = Matrix(
                    jj_size, jj_size, coupling_patterns['out_out'][(ii, jj)])
                coupling_matrices['in_out'][(ii, jj)] = Matrix(
                    ii_size, jj_size, coupling_patterns['in_out'][(ii, jj)])
                coupling_matrices['out_in'][(ii, jj)] = Matrix(
                    jj_size, ii_size, coupling_patterns['out_in'][(ii, jj)])
    boundary_patterns = {}
    for ii in grid.boundary_subdomains():
        boundary_patterns[ii] = block_space.local_space(ii).compute_pattern(
            'face_and_volume')

    ################ Assemble LHS and RHS

    lambda_, kappa = grid_and_problem_data['lambda'], grid_and_problem_data[
        'kappa']
    if isinstance(lambda_, dict):
        lambda_funcs = lambda_['functions']
        lambda_coeffs = lambda_['coefficients']
    else:
        lambda_funcs = [
            lambda_,
        ]
        lambda_coeffs = [
            1,
        ]

    logger.debug('block op ... ')
    ops, block_ops = zip(*(discretize_lhs(
        lf, grid, block_space, local_patterns, boundary_patterns,
        coupling_matrices, kappa, local_all_neumann_boundary_info,
        boundary_info, coupling_patterns, solver_options)
                           for lf in lambda_funcs))
    global_operator = LincombOperator(ops,
                                      lambda_coeffs,
                                      solver_options=solver_options,
                                      name='GlobalOperator')
    logger.debug('block op global done ')
    block_op = LincombOperator(block_ops,
                               lambda_coeffs,
                               name='lhs',
                               solver_options=solver_options)
    logger.debug('block op done ')

    f = grid_and_problem_data['f']
    if isinstance(f, dict):
        f_funcs = f['functions']
        f_coeffs = f['coefficients']
    else:
        f_funcs = [
            f,
        ]
        f_coeffs = [
            1,
        ]
    rhss, block_rhss = zip(*(discretize_rhs(
        ff, grid, block_space, global_operator, block_ops, block_op)
                             for ff in f_funcs))
    global_rhs = LincombOperator(rhss, f_coeffs)
    block_rhs = LincombOperator(block_rhss, f_coeffs)

    solution_space = block_op.source

    ################ Assemble interpolation and reconstruction operators
    logger.info('discretizing interpolation ')

    # Oswald interpolation error operator
    oi_op = BlockDiagonalOperator([
        OswaldInterpolationErrorOperator(ii, block_op.source, grid,
                                         block_space)
        for ii in range(num_global_subdomains)
    ],
                                  name='oswald_interpolation_error')

    # Flux reconstruction operator
    fr_op = LincombOperator([
        BlockDiagonalOperator([
            FluxReconstructionOperator(ii, block_op.source, grid, block_space,
                                       global_rt_space, subdomain_rt_spaces,
                                       lambda_xi, kappa)
            for ii in range(num_global_subdomains)
        ]) for lambda_xi in lambda_funcs
    ],
                            lambda_coeffs,
                            name='flux_reconstruction')

    ################ Assemble inner products and error estimator operators
    logger.info('discretizing inner products ')

    lambda_bar, lambda_hat = grid_and_problem_data[
        'lambda_bar'], grid_and_problem_data['lambda_hat']
    mu_bar, mu_hat = grid_and_problem_data['mu_bar'], grid_and_problem_data[
        'mu_hat']
    operators = {}
    local_projections = []
    local_rt_projections = []
    local_oi_projections = []
    local_div_ops = []
    local_l2_products = []
    data = dict(grid=grid,
                block_space=block_space,
                local_projections=local_projections,
                local_rt_projections=local_rt_projections,
                local_oi_projections=local_oi_projections,
                local_div_ops=local_div_ops,
                local_l2_products=local_l2_products)

    for ii in range(num_global_subdomains):

        neighborhood = grid.neighborhood_of(ii)

        ################ Assemble local inner products

        local_dg_space = block_space.local_space(ii)
        # we want a larger pattern to allow for axpy with other matrices
        tmp_local_matrix = Matrix(
            local_dg_space.size(), local_dg_space.size(),
            local_dg_space.compute_pattern('face_and_volume'))
        local_energy_product_ops = []
        local_energy_product_coeffs = []
        for func, coeff in zip(lambda_funcs, lambda_coeffs):
            local_energy_product_ops.append(
                make_elliptic_matrix_operator(func,
                                              kappa,
                                              tmp_local_matrix.copy(),
                                              local_dg_space,
                                              over_integrate=0))
            local_energy_product_coeffs.append(coeff)
            local_energy_product_ops.append(
                make_penalty_product_matrix_operator(
                    grid,
                    ii,
                    local_all_dirichlet_boundary_info,
                    local_dg_space,
                    func,
                    kappa,
                    over_integrate=0))
            local_energy_product_coeffs.append(coeff)
        local_l2_product = make_l2_matrix_operator(tmp_local_matrix.copy(),
                                                   local_dg_space)
        del tmp_local_matrix
        local_assembler = make_system_assembler(local_dg_space)
        for local_product_op in local_energy_product_ops:
            local_assembler.append(local_product_op)
        local_assembler.append(local_l2_product)
        local_assembler.assemble()
        local_energy_product_name = 'local_energy_dg_product_{}'.format(ii)
        local_energy_product = LincombOperator([
            DuneXTMatrixOperator(op.matrix(),
                                 source_id='domain_{}'.format(ii),
                                 range_id='domain_{}'.format(ii))
            for op in local_energy_product_ops
        ],
                                               local_energy_product_coeffs,
                                               name=local_energy_product_name)
        operators[local_energy_product_name] = \
            local_energy_product.assemble(mu_bar).with_(name=local_energy_product_name)

        local_l2_product = DuneXTMatrixOperator(
            local_l2_product.matrix(),
            source_id='domain_{}'.format(ii),
            range_id='domain_{}'.format(ii))
        local_l2_products.append(local_l2_product)

        # assemble local elliptic product
        matrix = make_local_elliptic_matrix_operator(grid, ii, local_dg_space,
                                                     lambda_bar, kappa)
        matrix.assemble()
        local_elliptic_product = DuneXTMatrixOperator(
            matrix.matrix(),
            range_id='domain_{}'.format(ii),
            source_id='domain_{}'.format(ii))

        ################ Assemble local to global projections

        # assemble projection (solution space) ->  (ii space)
        local_projection = BlockProjectionOperator(block_op.source, ii)
        local_projections.append(local_projection)

        # assemble projection (RT spaces on neighborhoods of subdomains) ->  (local RT space on ii)
        ops = np.full(num_global_subdomains, None)
        for kk in neighborhood:
            component = grid.neighborhood_of(kk).index(ii)
            assert fr_op.range.subspaces[kk].subspaces[
                component].id == 'LOCALRT_{}'.format(ii)
            ops[kk] = BlockProjectionOperator(fr_op.range.subspaces[kk],
                                              component)
        local_rt_projection = BlockRowOperator(
            ops,
            source_spaces=fr_op.range.subspaces,
            name='local_rt_projection_{}'.format(ii))
        local_rt_projections.append(local_rt_projection)

        # assemble projection (OI spaces on neighborhoods of subdomains) ->  (ii space)
        ops = np.full(num_global_subdomains, None)
        for kk in neighborhood:
            component = grid.neighborhood_of(kk).index(ii)
            assert oi_op.range.subspaces[kk].subspaces[
                component].id == 'domain_{}'.format(ii)
            ops[kk] = BlockProjectionOperator(oi_op.range.subspaces[kk],
                                              component)
        local_oi_projection = BlockRowOperator(
            ops,
            source_spaces=oi_op.range.subspaces,
            name='local_oi_projection_{}'.format(ii))
        local_oi_projections.append(local_oi_projection)

        ################ Assemble additional operators for error estimation

        # assemble local divergence operator
        local_rt_space = global_rt_space.restrict_to_dd_subdomain_view(
            grid, ii)
        local_div_op = make_divergence_matrix_operator_on_subdomain(
            grid, ii, local_dg_space, local_rt_space)
        local_div_op.assemble()
        local_div_op = DuneXTMatrixOperator(
            local_div_op.matrix(),
            source_id='LOCALRT_{}'.format(ii),
            range_id='domain_{}'.format(ii),
            name='local_divergence_{}'.format(ii))
        local_div_ops.append(local_div_op)

        ################ Assemble error estimator operators -- Nonconformity

        operators['nc_{}'.format(ii)] = \
            Concatenation([local_oi_projection.T, local_elliptic_product, local_oi_projection],
                          name='nonconformity_{}'.format(ii))

        ################ Assemble error estimator operators -- Residual

        if len(f_funcs) == 1:
            assert f_coeffs[0] == 1
            local_div = Concatenation([local_div_op, local_rt_projection])
            local_rhs = VectorFunctional(
                block_rhs.operators[0]._array._blocks[ii])

            operators['r_fd_{}'.format(ii)] = \
                Concatenation([local_rhs, local_div], name='r1_{}'.format(ii))

            operators['r_dd_{}'.format(ii)] = \
                Concatenation([local_div.T, local_l2_product, local_div], name='r2_{}'.format(ii))

        ################ Assemble error estimator operators -- Diffusive flux

        operators['df_aa_{}'.format(ii)] = LincombOperator(
            [
                assemble_estimator_diffusive_flux_aa(
                    lambda_xi, lambda_xi_prime, grid, ii, block_space,
                    lambda_hat, kappa, solution_space)
                for lambda_xi in lambda_funcs
                for lambda_xi_prime in lambda_funcs
            ], [
                ProductParameterFunctional([c1, c2]) for c1 in lambda_coeffs
                for c2 in lambda_coeffs
            ],
            name='diffusive_flux_aa_{}'.format(ii))

        operators['df_bb_{}'.format(
            ii)] = assemble_estimator_diffusive_flux_bb(
                grid, ii, subdomain_rt_spaces, lambda_hat, kappa,
                local_rt_projection)

        operators['df_ab_{}'.format(ii)] = LincombOperator(
            [
                assemble_estimator_diffusive_flux_ab(
                    lambda_xi, grid, ii, block_space, subdomain_rt_spaces,
                    lambda_hat, kappa, local_rt_projection, local_projection)
                for lambda_xi in lambda_funcs
            ],
            lambda_coeffs,
            name='diffusive_flux_ab_{}'.format(ii))

    ################ Final assembly
    logger.info('final assembly ')

    # instantiate error estimator
    min_diffusion_evs = np.array([
        min_diffusion_eigenvalue(grid, ii, lambda_hat, kappa)
        for ii in range(num_global_subdomains)
    ])
    subdomain_diameters = np.array(
        [subdomain_diameter(grid, ii) for ii in range(num_global_subdomains)])
    if len(f_funcs) == 1:
        assert f_coeffs[0] == 1
        local_eta_rf_squared = np.array([
            apply_l2_product(grid,
                             ii,
                             f_funcs[0],
                             f_funcs[0],
                             over_integrate=2)
            for ii in range(num_global_subdomains)
        ])
    else:
        local_eta_rf_squared = None
    estimator = EllipticEstimator(grid,
                                  min_diffusion_evs,
                                  subdomain_diameters,
                                  local_eta_rf_squared,
                                  lambda_coeffs,
                                  mu_bar,
                                  mu_hat,
                                  fr_op,
                                  oswald_interpolation_error=oi_op,
                                  mpi_comm=mpi_comm)
    l2_product = BlockDiagonalOperator(local_l2_products)

    # instantiate discretization
    neighborhoods = [
        grid.neighborhood_of(ii) for ii in range(num_global_subdomains)
    ]
    local_boundary_info = make_subdomain_boundary_info(
        grid_and_problem_data['grid'],
        {'type': 'xt.grid.boundaryinfo.alldirichlet'})
    d = DuneDiscretization(global_operator=global_operator,
                           global_rhs=global_rhs,
                           neighborhoods=neighborhoods,
                           enrichment_data=(grid, local_boundary_info, lambda_,
                                            kappa, f, block_space),
                           operator=block_op,
                           rhs=block_rhs,
                           visualizer=DuneGDTVisualizer(block_space),
                           operators=operators,
                           products={'l2': l2_product},
                           estimator=estimator,
                           data=data)
    parameter_range = grid_and_problem_data['parameter_range']
    logger.info('final assembly B')
    d = d.with_(parameter_space=CubicParameterSpace(
        d.parameter_type, parameter_range[0], parameter_range[1]))
    logger.info('final assembly C')
    return d, data
def discretize(grid_and_problem_data, T, nt):
    d, d_data = discretize_ell(grid_and_problem_data)
    assert isinstance(d.parameter_space, CubicParameterSpace)
    parameter_range = grid_and_problem_data['parameter_range']
    block_space = d_data['block_space']
    # assemble global L2 product
    l2_mat = d.global_operator.operators[0].matrix.copy(
    )  # to ensure matching pattern
    l2_mat.scal(0.)
    for ii in range(block_space.num_blocks):
        local_l2_product = d.l2_product._blocks[ii, ii]
        block_space.mapper.copy_local_to_global(
            local_l2_product.matrix, local_l2_product.matrix.pattern(), ii,
            l2_mat)
    mass = d.l2_product
    operators = {
        k: v
        for k, v in d.operators.items() if k not in d.special_operators
    }
    global_mass = DuneXTMatrixOperator(l2_mat)

    local_div_ops, local_l2_products, local_projections, local_rt_projections = \
        d_data['local_div_ops'], d_data['local_l2_products'], d_data['local_projections'], d_data['local_rt_projections']

    for ii in range(d_data['grid'].num_subdomains):

        local_div = Concatenation(
            [local_div_ops[ii], local_rt_projections[ii]])

        operators['r_ud_{}'.format(ii)] = \
            Concatenation([local_projections[ii].T, local_l2_products[ii], local_div], name='r_ud_{}'.format(ii))

        operators['r_l2_{}'.format(ii)] = \
            Concatenation([local_projections[ii].T, local_l2_products[ii], local_projections[ii]],
                          name='r_l2_{}'.format(ii))

    e = d.estimator
    estimator = ParabolicEstimator(e.min_diffusion_evs, e.subdomain_diameters,
                                   e.local_eta_rf_squared, e.lambda_coeffs,
                                   e.mu_bar, e.mu_hat, e.flux_reconstruction,
                                   e.oswald_interpolation_error)

    d = InstationaryDuneDiscretization(
        d.global_operator,
        d.global_rhs,
        global_mass,
        T,
        d.operator.source.zeros(1),
        d.operator,
        d.rhs,
        mass=mass,
        time_stepper=ImplicitEulerTimeStepper(nt=nt,
                                              solver_options='operator'),
        products=d.products,
        operators=operators,
        estimator=estimator,
        visualizer=DuneGDTVisualizer(block_space))
    d = d.with_(parameter_space=CubicParameterSpace(
        d.parameter_type, parameter_range[0], parameter_range[1]))

    return d, d_data