def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     diffusive_flux_aa_product = make_diffusive_flux_aa_product(
             self.grid, self.subdomain,
             self.block_space.local_space(self.subdomain),
             self.lambda_bar,
             lambda_u=self.lambda_xi, lambda_v=self.lambda_xi_prime,
             kappa=self.kappa,
             over_integrate=2)
     subdomain_walker = make_subdomain_walker(self.grid, self.subdomain)
     subdomain_walker.append(diffusive_flux_aa_product)
     subdomain_walker.walk()
     self.matrix = DuneXTMatrixOperator(diffusive_flux_aa_product.matrix(),
                                        range_id='domain_{}'.format(self.subdomain),
                                        source_id='domain_{}'.format(self.subdomain))
def assemble_estimator_diffusive_flux_aa(lambda_xi, lambda_xi_prime, grid, ii,
                                         block_space, lambda_hat, kappa,
                                         solution_space):
    local_subdomains, num_local_subdomains, num_global_subdomains = _get_subdomains(
        grid)
    diffusive_flux_aa_product = make_diffusive_flux_aa_product(
        grid,
        ii,
        block_space.local_space(ii),
        lambda_hat,
        lambda_u=lambda_xi,
        lambda_v=lambda_xi_prime,
        kappa=kappa,
        over_integrate=2)
    subdomain_walker = make_subdomain_walker(grid, ii)
    subdomain_walker.append(diffusive_flux_aa_product)
    subdomain_walker.walk()
    # , block_space.local_space(ii).dof_communicator,
    matrix = DuneXTMatrixOperator(diffusive_flux_aa_product.matrix(),
                                  range_id='domain_{}'.format(ii),
                                  source_id='domain_{}'.format(ii))
    df_ops = np.full((num_global_subdomains, ) * 2, None)
    df_ops[ii, ii] = matrix
    return BlockOperator(df_ops,
                         range_spaces=solution_space.subspaces,
                         source_spaces=solution_space.subspaces)
class DiffusiveFluxOperatorAB(EstimatorOperatorBase):

    RT_source = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        subdomain_space = self.block_space.local_space(self.subdomain)
        self.subdomain_rt_space = self.global_rt_space.restrict_to_dd_subdomain_part(self.grid, self.subdomain)
        diffusive_flux_ab_product = make_diffusive_flux_ab_product(
                self.grid, self.subdomain,
                range_space=subdomain_space,
                source_space=self.subdomain_rt_space,
                lambda_range=self.lambda_xi,
                lambda_hat=self.lambda_bar,
                kappa=self.kappa,
                over_integrate=2)
        subdomain_walker = make_subdomain_walker(self.grid, self.subdomain)
        subdomain_walker.append(diffusive_flux_ab_product)
        subdomain_walker.walk()
        self.matrix = DuneXTMatrixOperator(diffusive_flux_ab_product.matrix(),
                                           range_id='domain_{}'.format(self.subdomain),
                                           source_id='RT_{}'.format(self.subdomain))

    def apply(self, U, mu=None):
        raise NotImplementedError

    def apply2(self, V, U, mu=None):
        assert V in self.range and U in self.source

        reconstructed_uh_kk_on_subdomain = self.matrix.source.make_array(
                [self.subdomain_rt_space.restrict(u.impl) for u in U._list])

        return self.matrix.apply2(V, reconstructed_uh_kk_on_subdomain)
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     subdomain_space = self.block_space.local_space(self.subdomain)
     self.subdomain_rt_space = self.global_rt_space.restrict_to_dd_subdomain_part(self.grid, self.subdomain)
     diffusive_flux_ab_product = make_diffusive_flux_ab_product(
             self.grid, self.subdomain,
             range_space=subdomain_space,
             source_space=self.subdomain_rt_space,
             lambda_range=self.lambda_xi,
             lambda_hat=self.lambda_bar,
             kappa=self.kappa,
             over_integrate=2)
     subdomain_walker = make_subdomain_walker(self.grid, self.subdomain)
     subdomain_walker.append(diffusive_flux_ab_product)
     subdomain_walker.walk()
     self.matrix = DuneXTMatrixOperator(diffusive_flux_ab_product.matrix(),
                                        range_id='domain_{}'.format(self.subdomain),
                                        source_id='RT_{}'.format(self.subdomain))
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.range = BlockVectorSpace([self.global_space.subspaces[ii] for ii in self.grid.neighborhood_of(self.jj)],
                                   'OI_{}'.format(self.jj))
     self.source = BlockVectorSpace([self.global_space.subspaces[ii] for ii in self.grid.neighborhood_of(self.kk)],
                                    'OI_{}'.format(self.kk))
     if self.subdomain not in self._matrices:
         matrix = make_local_elliptic_matrix_operator(self.grid, self.subdomain,
                                                      self.block_space.local_space(self.subdomain),
                                                      self.lambda_bar, self.kappa)
         matrix.assemble()
         matrix = matrix.matrix()
         self._matrices[self.subdomain] = DuneXTMatrixOperator(matrix,
                                                               range_id='domain_{}'.format(self.subdomain),
                                                               source_id='domain_{}'.format(self.subdomain))
     self.matrix = self._matrices[self.subdomain]
     self.range_index = self.grid.neighborhood_of(self.jj).index(self.subdomain)
     self.source_index = self.grid.neighborhood_of(self.kk).index(self.subdomain)
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     if self.subdomain not in self._subdomain_rt_spaces:
         self._subdomain_rt_spaces[self.subdomain] = self.global_rt_space.restrict_to_dd_subdomain_part(
                 self.grid, self.subdomain)
     self.subdomain_rt_space = self._subdomain_rt_spaces[self.subdomain]
     if self.subdomain not in self._matrices:
         h_div_semi_product = make_Hdiv_semi_product_matrix_operator_on_subdomain(
                 self.grid, self.subdomain,
                 self.subdomain_rt_space,
                 over_integrate=2)
         subdomain_walker = make_subdomain_walker(self.grid, self.subdomain)
         subdomain_walker.append(h_div_semi_product)
         subdomain_walker.walk()
         self._matrices[self.subdomain] = DuneXTMatrixOperator(h_div_semi_product.matrix(),
                                                               range_id='RT_{}'.format(self.subdomain),
                                                               source_id='RT_{}'.format(self.subdomain))
     self.matrix = self._matrices[self.subdomain]
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))
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     if self.subdomain not in self._subdomain_rt_spaces:
         self._subdomain_rt_spaces[self.subdomain] = self.global_rt_space.restrict_to_dd_subdomain_part(
                 self.grid, self.subdomain)
     self.subdomain_rt_space = self._subdomain_rt_spaces[self.subdomain]
     if self.subdomain not in self._matrices:
         diffusive_flux_bb_product = make_diffusive_flux_bb_product(
                 self.grid, self.subdomain,
                 self.subdomain_rt_space,
                 self.lambda_bar,
                 kappa=self.kappa,
                 over_integrate=2)
         subdomain_walker = make_subdomain_walker(self.grid, self.subdomain)
         subdomain_walker.append(diffusive_flux_bb_product)
         subdomain_walker.walk()
         self._matrices[self.subdomain] = DuneXTMatrixOperator(diffusive_flux_bb_product.matrix(),
                                                               range_id='RT_{}'.format(self.subdomain),
                                                               source_id='RT_{}'.format(self.subdomain))
     self.matrix = self._matrices[self.subdomain]
class DiffusiveFluxOperatorAA(EstimatorOperatorBase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        diffusive_flux_aa_product = make_diffusive_flux_aa_product(
                self.grid, self.subdomain,
                self.block_space.local_space(self.subdomain),
                self.lambda_bar,
                lambda_u=self.lambda_xi, lambda_v=self.lambda_xi_prime,
                kappa=self.kappa,
                over_integrate=2)
        subdomain_walker = make_subdomain_walker(self.grid, self.subdomain)
        subdomain_walker.append(diffusive_flux_aa_product)
        subdomain_walker.walk()
        self.matrix = DuneXTMatrixOperator(diffusive_flux_aa_product.matrix(),
                                           range_id='domain_{}'.format(self.subdomain),
                                           source_id='domain_{}'.format(self.subdomain))

    def apply(self, U, mu=None):
        raise NotImplementedError

    def _apply2(self, V, U, mu=None):
        assert V in self.range and U in self.source
        return self.matrix.apply2(V, U)
def discretize_lhs(lambda_func, grid, block_space, local_patterns,
                   boundary_patterns, coupling_matrices, kappa,
                   local_all_neumann_boundary_info, boundary_info,
                   coupling_patterns, solver_options):
    logger = getLogger('discretize_lhs')
    logger.debug('...')
    local_subdomains, num_local_subdomains, num_global_subdomains = _get_subdomains(
        grid)
    local_matrices = [None] * num_global_subdomains
    boundary_matrices = {}
    logger.debug('discretize lhs coupling matrices ...')
    for ii in range(num_global_subdomains):
        local_matrices[ii] = Matrix(
            block_space.local_space(ii).size(),
            block_space.local_space(ii).size(), local_patterns[ii])
        if ii in grid.boundary_subdomains():
            boundary_matrices[ii] = Matrix(
                block_space.local_space(ii).size(),
                block_space.local_space(ii).size(), boundary_patterns[ii])

    logger.debug('discretize lhs ipdg ops ...')
    for ii in range(num_global_subdomains):
        ss = block_space.local_space(ii)
        ll = local_matrices[ii]
        ipdg_operator = make_elliptic_swipdg_matrix_operator(
            lambda_func,
            kappa,
            local_all_neumann_boundary_info,
            ll,
            ss,
            over_integrate=2)
        ipdg_operator.assemble(False)

    logger.debug('discretize lhs ops ...')
    local_ipdg_coupling_operator = make_local_elliptic_swipdg_coupling_operator(
        lambda_func, kappa)

    def assemble_coupling_contributions(subdomain, neighboring_subdomain):
        coupling_assembler = block_space.coupling_assembler(
            subdomain, neighboring_subdomain)
        coupling_assembler.append(
            local_ipdg_coupling_operator,
            coupling_matrices['in_in'][(subdomain, neighboring_subdomain)],
            coupling_matrices['out_out'][(subdomain, neighboring_subdomain)],
            coupling_matrices['in_out'][(subdomain, neighboring_subdomain)],
            coupling_matrices['out_in'][(subdomain, neighboring_subdomain)])
        coupling_assembler.assemble()

    for ii in range(num_global_subdomains):
        for jj in grid.neighboring_subdomains(ii):
            if ii < jj:  # Assemble primally (visit each coupling only once).
                assemble_coupling_contributions(ii, jj)

    logger.debug('discretize lhs boundary ...')
    local_ipdg_boundary_operator = make_local_elliptic_swipdg_boundary_operator(
        lambda_func, kappa)
    apply_on_dirichlet_intersections = make_apply_on_dirichlet_intersections(
        boundary_info)

    def assemble_boundary_contributions(subdomain):
        boundary_assembler = block_space.boundary_assembler(subdomain)
        boundary_assembler.append(local_ipdg_boundary_operator,
                                  boundary_matrices[subdomain],
                                  apply_on_dirichlet_intersections)
        boundary_assembler.assemble()

    for ii in grid.boundary_subdomains():
        assemble_boundary_contributions(ii)

    logger.debug('discretize lhs global contributions ...')
    global_pattern = SparsityPatternDefault(block_space.mapper.size)
    for ii in range(num_global_subdomains):
        block_space.mapper.copy_local_to_global(local_patterns[ii], ii,
                                                global_pattern)
        if ii in grid.boundary_subdomains():
            block_space.mapper.copy_local_to_global(boundary_patterns[ii], ii,
                                                    global_pattern)
        for jj in grid.neighboring_subdomains(ii):
            if ii < jj:  # Assemble primally (visit each coupling only once).
                block_space.mapper.copy_local_to_global(
                    coupling_patterns['in_in'][(ii, jj)], ii, ii,
                    global_pattern)
                block_space.mapper.copy_local_to_global(
                    coupling_patterns['out_out'][(ii, jj)], jj, jj,
                    global_pattern)
                block_space.mapper.copy_local_to_global(
                    coupling_patterns['in_out'][(ii, jj)], ii, jj,
                    global_pattern)
                block_space.mapper.copy_local_to_global(
                    coupling_patterns['out_in'][(ii, jj)], jj, ii,
                    global_pattern)

    system_matrix = Matrix(block_space.mapper.size, block_space.mapper.size,
                           global_pattern)
    for ii in range(num_global_subdomains):
        block_space.mapper.copy_local_to_global(local_matrices[ii],
                                                local_patterns[ii], ii,
                                                system_matrix)
        if ii in grid.boundary_subdomains():
            block_space.mapper.copy_local_to_global(boundary_matrices[ii],
                                                    boundary_patterns[ii], ii,
                                                    ii, system_matrix)
        for jj in grid.neighboring_subdomains(ii):
            if ii < jj:  # Assemble primally (visit each coupling only once).
                block_space.mapper.copy_local_to_global(
                    coupling_matrices['in_in'][(ii, jj)],
                    coupling_patterns['in_in'][(ii, jj)], ii, ii,
                    system_matrix)
                block_space.mapper.copy_local_to_global(
                    coupling_matrices['out_out'][(ii, jj)],
                    coupling_patterns['out_out'][(ii, jj)], jj, jj,
                    system_matrix)
                block_space.mapper.copy_local_to_global(
                    coupling_matrices['in_out'][(ii, jj)],
                    coupling_patterns['in_out'][(ii, jj)], ii, jj,
                    system_matrix)
                block_space.mapper.copy_local_to_global(
                    coupling_matrices['out_in'][(ii, jj)],
                    coupling_patterns['out_in'][(ii, jj)], jj, ii,
                    system_matrix)
    logger.debug('discretize lhs global op ...')
    op = DuneXTMatrixOperator(system_matrix,
                              dof_communicator=block_space.dof_communicator,
                              solver_options=solver_options)
    logger.debug('discretize lhs global op done ...')
    mats = np.full((num_global_subdomains, num_global_subdomains), None)
    for ii in range(num_global_subdomains):
        ii_size = block_space.local_space(ii).size()
        for jj in range(ii, num_global_subdomains):
            jj_size = block_space.local_space(jj).size()
            if ii == jj:
                mats[ii, ii] = Matrix(ii_size, ii_size, local_patterns[ii])
            elif (ii, jj) in coupling_matrices['in_out']:
                mats[ii, jj] = Matrix(ii_size, jj_size,
                                      coupling_patterns['in_out'][(ii, jj)])
                mats[jj, ii] = Matrix(jj_size, ii_size,
                                      coupling_patterns['out_in'][(ii, jj)])

    for ii in range(num_global_subdomains):
        for jj in range(ii, num_global_subdomains):
            if ii == jj:
                mats[ii, ii].axpy(1., local_matrices[ii])
                if ii in boundary_matrices:
                    mats[ii, ii].axpy(1., boundary_matrices[ii])
            elif (ii, jj) in coupling_matrices['in_out']:
                mats[ii, ii].axpy(1., coupling_matrices['in_in'][(ii, jj)])
                mats[jj, jj].axpy(1., coupling_matrices['out_out'][(ii, jj)])
                mats[ii, jj].axpy(1., coupling_matrices['in_out'][(ii, jj)])
                mats[jj, ii].axpy(1., coupling_matrices['out_in'][(ii, jj)])

    logger.debug('discretize lhs block op ...')
    ops = np.full((num_global_subdomains, num_global_subdomains), None)
    for (ii, jj), mat in np.ndenumerate(mats):
        ops[ii, jj] = DuneXTMatrixOperator(
            mat,
            name='local_block_{}-{}'.format(ii, jj),
            source_id='domain_{}'.format(jj),
            range_id='domain_{}'.format(ii)) if mat else None

    block_op = BlockOperator(ops,
                             dof_communicator=block_space.dof_communicator,
                             name='BlockOp')
    return op, block_op
    def solve_for_local_correction(self,
                                   subdomain,
                                   Us,
                                   mu=None,
                                   inverse_options=None):
        grid, local_boundary_info, affine_lambda, kappa, f, block_space = self.enrichment_data
        neighborhood = self.neighborhoods[subdomain]
        neighborhood_space = block_space.restricted_to_neighborhood(
            neighborhood)
        # Compute current solution restricted to the neighborhood to be usable as Dirichlet values for the correction
        # problem.
        current_solution = [U._list for U in Us]
        assert np.all(len(v) == 1 for v in current_solution)
        current_solution = [v[0].impl for v in current_solution]
        current_solution = neighborhood_space.project_onto_neighborhood(
            current_solution, neighborhood)
        current_solution = make_discrete_function(neighborhood_space,
                                                  current_solution)
        # Solve the local corrector problem.
        #   LHS
        ops = []
        for lambda_ in affine_lambda['functions']:
            ops.append(
                make_elliptic_swipdg_matrix_operator_on_neighborhood(
                    grid,
                    subdomain,
                    local_boundary_info,
                    neighborhood_space,
                    lambda_,
                    kappa,
                    over_integrate=0))
        ops_coeffs = affine_lambda['coefficients'].copy()
        #   RHS
        funcs = []

        # We don't have any boundary treatment right now. Things will probably
        # break in multiple ways in case of non-trivial boundary conditions,
        # so we can comment this out for now ..

        # for lambda_ in affine_lambda['functions']:
        #     funcs.append(make_elliptic_swipdg_vector_functional_on_neighborhood(
        #         grid, subdomain, local_boundary_info,
        #         neighborhood_space,
        #         current_solution, lambda_, kappa,
        #         over_integrate=0))
        # funcs_coeffs = affine_lambda['coefficients'].copy()
        funcs.append(
            make_l2_vector_functional_on_neighborhood(grid,
                                                      subdomain,
                                                      neighborhood_space,
                                                      f,
                                                      over_integrate=2))
        # funcs_coeffs.append(1.)
        funcs_coeffs = [1]
        #   assemble in one grid walk
        neighborhood_assembler = make_neighborhood_system_assembler(
            grid, subdomain, neighborhood_space)
        for op in ops:
            neighborhood_assembler.append(op)
        for func in funcs:
            neighborhood_assembler.append(func)
        neighborhood_assembler.assemble()
        # solve
        local_space_id = self.solution_space.subspaces[subdomain].id
        lhs = LincombOperator([
            DuneXTMatrixOperator(
                o.matrix(), source_id=local_space_id, range_id=local_space_id)
            for o in ops
        ], ops_coeffs)
        rhs = LincombOperator([
            VectorFunctional(lhs.range.make_array([v.vector()])) for v in funcs
        ], funcs_coeffs)
        correction = lhs.apply_inverse(rhs.as_source_array(mu),
                                       mu=mu,
                                       inverse_options=inverse_options)
        assert len(correction) == 1
        # restrict to subdomain
        local_sizes = [
            block_space.local_space(nn).size() for nn in neighborhood
        ]
        local_starts = [
            int(np.sum(local_sizes[:nn])) for nn in range(len(local_sizes))
        ]
        local_starts.append(neighborhood_space.mapper.size)
        localized_corrections_as_np = np.array(correction._list[0].impl,
                                               copy=False)
        localized_corrections_as_np = [
            localized_corrections_as_np[local_starts[nn]:local_starts[nn + 1]]
            for nn in range(len(local_sizes))
        ]
        subdomain_index_in_neighborhood = np.where(
            np.array(list(neighborhood)) == subdomain)[0]
        assert len(subdomain_index_in_neighborhood) == 1
        subdomain_index_in_neighborhood = subdomain_index_in_neighborhood[0]
        subdomain_correction = Vector(
            local_sizes[subdomain_index_in_neighborhood], 0.)
        subdomain_correction_as_np = np.array(subdomain_correction, copy=False)
        subdomain_correction_as_np[:] = localized_corrections_as_np[
            subdomain_index_in_neighborhood][:]
        return self.solution_space.subspaces[subdomain].make_array(
            [subdomain_correction])
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}
    def discretize_lhs_for_lambda(lambda_):
        local_matrices = [None]*grid.num_subdomains
        local_vectors = [None]*grid.num_subdomains
        boundary_matrices = {}
        coupling_matrices_in_in = {}
        coupling_matrices_out_out = {}
        coupling_matrices_in_out = {}
        coupling_matrices_out_in = {}
        for ii in range(grid.num_subdomains):
            local_matrices[ii] = Matrix(block_space.local_space(ii).size(),
                                        block_space.local_space(ii).size(),
                                        local_patterns[ii])
            local_vectors[ii] = Vector(block_space.local_space(ii).size())
            if ii in grid.boundary_subdomains():
                boundary_matrices[ii] = Matrix(block_space.local_space(ii).size(),
                                               block_space.local_space(ii).size(),
                                               boundary_patterns[ii])
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    coupling_matrices_in_in[(ii, jj)] = Matrix(block_space.local_space(ii).size(),
                                                               block_space.local_space(ii).size(),
                                                               coupling_patterns_in_in[(ii, jj)])
                    coupling_matrices_out_out[(ii, jj)] = Matrix(block_space.local_space(jj).size(),
                                                                 block_space.local_space(jj).size(),
                                                                 coupling_patterns_out_out[(ii, jj)])
                    coupling_matrices_in_out[(ii, jj)] = Matrix(block_space.local_space(ii).size(),
                                                                block_space.local_space(jj).size(),
                                                                coupling_patterns_in_out[(ii, jj)])
                    coupling_matrices_out_in[(ii, jj)] = Matrix(block_space.local_space(jj).size(),
                                                                block_space.local_space(ii).size(),
                                                                coupling_patterns_out_in[(ii, jj)])

        def assemble_local_contributions(subdomain):
            ipdg_operator = make_elliptic_swipdg_matrix_operator(lambda_, kappa, local_all_neumann_boundary_info,
                                                                 local_matrices[subdomain],
                                                                 block_space.local_space(subdomain))
            l2_functional = make_l2_volume_vector_functional(f, local_vectors[subdomain],
                                                             block_space.local_space(subdomain))
            local_assembler = make_system_assembler(block_space.local_space(subdomain))
            local_assembler.append(ipdg_operator)
            local_assembler.append(l2_functional)
            local_assembler.assemble()

        for ii in range(grid.num_subdomains):
            assemble_local_contributions(ii)

        local_ipdg_coupling_operator = make_local_elliptic_swipdg_coupling_operator(lambda_, kappa)

        def assemble_coupling_contributions(subdomain, neighboring_subdomain):
            coupling_assembler = block_space.coupling_assembler(subdomain, neighboring_subdomain)
            coupling_assembler.append(local_ipdg_coupling_operator,
                                      coupling_matrices_in_in[(subdomain, neighboring_subdomain)],
                                      coupling_matrices_out_out[(subdomain, neighboring_subdomain)],
                                      coupling_matrices_in_out[(subdomain, neighboring_subdomain)],
                                      coupling_matrices_out_in[(subdomain, neighboring_subdomain)])
            coupling_assembler.assemble()

        for ii in range(grid.num_subdomains):
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    assemble_coupling_contributions(ii, jj)

        local_ipdg_boundary_operator = make_local_elliptic_swipdg_boundary_operator(lambda_, kappa)
        apply_on_dirichlet_intersections = make_apply_on_dirichlet_intersections(boundary_info)

        def assemble_boundary_contributions(subdomain):
            boundary_assembler = block_space.boundary_assembler(subdomain)
            boundary_assembler.append(local_ipdg_boundary_operator,
                                      boundary_matrices[subdomain],
                                      apply_on_dirichlet_intersections)
            boundary_assembler.assemble()

        for ii in grid.boundary_subdomains():
            assemble_boundary_contributions(ii)

        global_pattern = SparsityPatternDefault(block_space.mapper.size)
        for ii in range(grid.num_subdomains):
            block_space.mapper.copy_local_to_global(local_patterns[ii], ii, global_pattern)
            if ii in grid.boundary_subdomains():
                block_space.mapper.copy_local_to_global(boundary_patterns[ii], ii, global_pattern)
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    block_space.mapper.copy_local_to_global(coupling_patterns_in_in[(ii, jj)], ii, ii, global_pattern)
                    block_space.mapper.copy_local_to_global(coupling_patterns_out_out[(ii, jj)], jj, jj, global_pattern)
                    block_space.mapper.copy_local_to_global(coupling_patterns_in_out[(ii, jj)], ii, jj, global_pattern)
                    block_space.mapper.copy_local_to_global(coupling_patterns_out_in[(ii, jj)], jj, ii, global_pattern)

        system_matrix = Matrix(block_space.mapper.size, block_space.mapper.size, global_pattern)
        rhs_vector = Vector(block_space.mapper.size, 0.)
        for ii in range(grid.num_subdomains):
            block_space.mapper.copy_local_to_global(local_matrices[ii], local_patterns[ii], ii, system_matrix)
            block_space.mapper.copy_local_to_global(local_vectors[ii], ii, rhs_vector)
            if ii in grid.boundary_subdomains():
                block_space.mapper.copy_local_to_global(boundary_matrices[ii], boundary_patterns[ii], ii, ii, system_matrix)
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    block_space.mapper.copy_local_to_global(coupling_matrices_in_in[(ii, jj)],
                                                            coupling_patterns_in_in[(ii, jj)],
                                                            ii, ii, system_matrix)
                    block_space.mapper.copy_local_to_global(coupling_matrices_out_out[(ii, jj)],
                                                            coupling_patterns_out_out[(ii, jj)],
                                                            jj, jj, system_matrix)
                    block_space.mapper.copy_local_to_global(coupling_matrices_in_out[(ii, jj)],
                                                            coupling_patterns_in_out[(ii, jj)],
                                                            ii, jj, system_matrix)
                    block_space.mapper.copy_local_to_global(coupling_matrices_out_in[(ii, jj)],
                                                            coupling_patterns_out_in[(ii, jj)],
                                                            jj, ii, system_matrix)

        op = DuneXTMatrixOperator(system_matrix)
        mats = np.full((grid.num_subdomains, grid.num_subdomains), None)
        for ii in range(grid.num_subdomains):
            for jj in range(ii, grid.num_subdomains):
                if ii == jj:
                    mats[ii, ii] = Matrix(block_space.local_space(ii).size(),
                                          block_space.local_space(ii).size(),
                                          local_patterns[ii])
                elif (ii, jj) in coupling_matrices_in_out:
                    mats[ii, jj] = Matrix(block_space.local_space(ii).size(),
                                          block_space.local_space(jj).size(),
                                          coupling_patterns_in_out[(ii, jj)])
                    mats[jj, ii] = Matrix(block_space.local_space(jj).size(),
                                          block_space.local_space(ii).size(),
                                          coupling_patterns_out_in[(ii, jj)])

        for ii in range(grid.num_subdomains):
            for jj in range(ii, grid.num_subdomains):
                if ii == jj:
                    mats[ii, ii].axpy(1.,  local_matrices[ii])
                    if ii in boundary_matrices:
                        mats[ii, ii].axpy(1.,  boundary_matrices[ii])
                elif (ii, jj) in coupling_matrices_in_out:
                    mats[ii, ii].axpy(1., coupling_matrices_in_in[(ii, jj)])
                    mats[jj, jj].axpy(1., coupling_matrices_out_out[(ii, jj)])
                    mats[ii, jj].axpy(1., coupling_matrices_in_out[(ii, jj)])
                    mats[jj, ii].axpy(1., coupling_matrices_out_in[(ii, jj)])

        ops = np.full((grid.num_subdomains, grid.num_subdomains), None)
        for (ii, jj), mat in np.ndenumerate(mats):
            ops[ii, jj] = DuneXTMatrixOperator(mat,
                                               source_id='domain_{}'.format(jj),
                                               range_id='domain_{}'.format(ii)) if mat else None
        block_op = BlockOperator(ops)

        rhs = VectorFunctional(op.range.make_array([rhs_vector]))
        rhss = []
        for ii in range(grid.num_subdomains):
            rhss.append(ops[ii, ii].range.make_array([local_vectors[ii]]))
        block_rhs = VectorFunctional(block_op.range.make_array(rhss))

        return op, block_op, rhs, block_rhs
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):
    logger = getLogger('discretize_elliptic.discretize_block_SWIPDG')
    logger.info('discretizing ... ')

    grid, boundary_info, inner_boundary_id = (grid_and_problem_data['grid'],
                                              grid_and_problem_data['boundary_info'],
                                              grid_and_problem_data['inner_boundary_id'])
    local_all_dirichlet_boundary_info = make_subdomain_boundary_info(grid, {'type': 'xt.grid.boundaryinfo.alldirichlet'})
    local_all_neumann_boundary_info = make_subdomain_boundary_info(grid, {'type': 'xt.grid.boundaryinfo.allneumann'})
    neighborhood_boundary_info = make_subdomain_boundary_info(
        grid,
        {'type': 'xt.grid.boundaryinfo.boundarysegmentindexbased',
         'default': 'dirichlet',
         'neumann': '[{} {}]'.format(inner_boundary_id, inner_boundary_id+1)})

    affine_lambda, kappa, f = (grid_and_problem_data['lambda'],
                               grid_and_problem_data['kappa'],
                               grid_and_problem_data['f'])
    lambda_bar, lambda_hat = grid_and_problem_data['lambda_bar'], grid_and_problem_data['lambda_hat']
    mu_bar, mu_hat, parameter_range  = (grid_and_problem_data['mu_bar'],
                                        grid_and_problem_data['mu_hat'],
                                        grid_and_problem_data['parameter_range'])

    block_space = make_block_space(grid)

    local_patterns = [block_space.local_space(ii).compute_pattern('face_and_volume')
                      for ii in range(block_space.num_blocks)]
    coupling_patterns_in_in = {}
    coupling_patterns_out_out = {}
    coupling_patterns_in_out = {}
    coupling_patterns_out_in = {}
    for ii in range(grid.num_subdomains):
        for jj in grid.neighboring_subdomains(ii):
            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')
    boundary_patterns = {}
    for ii in grid.boundary_subdomains():
        boundary_patterns[ii] = block_space.local_space(ii).compute_pattern('face_and_volume')

    def discretize_lhs_for_lambda(lambda_):
        local_matrices = [None]*grid.num_subdomains
        local_vectors = [None]*grid.num_subdomains
        boundary_matrices = {}
        coupling_matrices_in_in = {}
        coupling_matrices_out_out = {}
        coupling_matrices_in_out = {}
        coupling_matrices_out_in = {}
        for ii in range(grid.num_subdomains):
            local_matrices[ii] = Matrix(block_space.local_space(ii).size(),
                                        block_space.local_space(ii).size(),
                                        local_patterns[ii])
            local_vectors[ii] = Vector(block_space.local_space(ii).size())
            if ii in grid.boundary_subdomains():
                boundary_matrices[ii] = Matrix(block_space.local_space(ii).size(),
                                               block_space.local_space(ii).size(),
                                               boundary_patterns[ii])
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    coupling_matrices_in_in[(ii, jj)] = Matrix(block_space.local_space(ii).size(),
                                                               block_space.local_space(ii).size(),
                                                               coupling_patterns_in_in[(ii, jj)])
                    coupling_matrices_out_out[(ii, jj)] = Matrix(block_space.local_space(jj).size(),
                                                                 block_space.local_space(jj).size(),
                                                                 coupling_patterns_out_out[(ii, jj)])
                    coupling_matrices_in_out[(ii, jj)] = Matrix(block_space.local_space(ii).size(),
                                                                block_space.local_space(jj).size(),
                                                                coupling_patterns_in_out[(ii, jj)])
                    coupling_matrices_out_in[(ii, jj)] = Matrix(block_space.local_space(jj).size(),
                                                                block_space.local_space(ii).size(),
                                                                coupling_patterns_out_in[(ii, jj)])

        def assemble_local_contributions(subdomain):
            ipdg_operator = make_elliptic_swipdg_matrix_operator(lambda_, kappa, local_all_neumann_boundary_info,
                                                                 local_matrices[subdomain],
                                                                 block_space.local_space(subdomain))
            l2_functional = make_l2_volume_vector_functional(f, local_vectors[subdomain],
                                                             block_space.local_space(subdomain))
            local_assembler = make_system_assembler(block_space.local_space(subdomain))
            local_assembler.append(ipdg_operator)
            local_assembler.append(l2_functional)
            local_assembler.assemble()

        for ii in range(grid.num_subdomains):
            assemble_local_contributions(ii)

        local_ipdg_coupling_operator = make_local_elliptic_swipdg_coupling_operator(lambda_, kappa)

        def assemble_coupling_contributions(subdomain, neighboring_subdomain):
            coupling_assembler = block_space.coupling_assembler(subdomain, neighboring_subdomain)
            coupling_assembler.append(local_ipdg_coupling_operator,
                                      coupling_matrices_in_in[(subdomain, neighboring_subdomain)],
                                      coupling_matrices_out_out[(subdomain, neighboring_subdomain)],
                                      coupling_matrices_in_out[(subdomain, neighboring_subdomain)],
                                      coupling_matrices_out_in[(subdomain, neighboring_subdomain)])
            coupling_assembler.assemble()

        for ii in range(grid.num_subdomains):
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    assemble_coupling_contributions(ii, jj)

        local_ipdg_boundary_operator = make_local_elliptic_swipdg_boundary_operator(lambda_, kappa)
        apply_on_dirichlet_intersections = make_apply_on_dirichlet_intersections(boundary_info)

        def assemble_boundary_contributions(subdomain):
            boundary_assembler = block_space.boundary_assembler(subdomain)
            boundary_assembler.append(local_ipdg_boundary_operator,
                                      boundary_matrices[subdomain],
                                      apply_on_dirichlet_intersections)
            boundary_assembler.assemble()

        for ii in grid.boundary_subdomains():
            assemble_boundary_contributions(ii)

        global_pattern = SparsityPatternDefault(block_space.mapper.size)
        for ii in range(grid.num_subdomains):
            block_space.mapper.copy_local_to_global(local_patterns[ii], ii, global_pattern)
            if ii in grid.boundary_subdomains():
                block_space.mapper.copy_local_to_global(boundary_patterns[ii], ii, global_pattern)
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    block_space.mapper.copy_local_to_global(coupling_patterns_in_in[(ii, jj)], ii, ii, global_pattern)
                    block_space.mapper.copy_local_to_global(coupling_patterns_out_out[(ii, jj)], jj, jj, global_pattern)
                    block_space.mapper.copy_local_to_global(coupling_patterns_in_out[(ii, jj)], ii, jj, global_pattern)
                    block_space.mapper.copy_local_to_global(coupling_patterns_out_in[(ii, jj)], jj, ii, global_pattern)

        system_matrix = Matrix(block_space.mapper.size, block_space.mapper.size, global_pattern)
        rhs_vector = Vector(block_space.mapper.size, 0.)
        for ii in range(grid.num_subdomains):
            block_space.mapper.copy_local_to_global(local_matrices[ii], local_patterns[ii], ii, system_matrix)
            block_space.mapper.copy_local_to_global(local_vectors[ii], ii, rhs_vector)
            if ii in grid.boundary_subdomains():
                block_space.mapper.copy_local_to_global(boundary_matrices[ii], boundary_patterns[ii], ii, ii, system_matrix)
            for jj in grid.neighboring_subdomains(ii):
                if ii < jj:  # Assemble primally (visit each coupling only once).
                    block_space.mapper.copy_local_to_global(coupling_matrices_in_in[(ii, jj)],
                                                            coupling_patterns_in_in[(ii, jj)],
                                                            ii, ii, system_matrix)
                    block_space.mapper.copy_local_to_global(coupling_matrices_out_out[(ii, jj)],
                                                            coupling_patterns_out_out[(ii, jj)],
                                                            jj, jj, system_matrix)
                    block_space.mapper.copy_local_to_global(coupling_matrices_in_out[(ii, jj)],
                                                            coupling_patterns_in_out[(ii, jj)],
                                                            ii, jj, system_matrix)
                    block_space.mapper.copy_local_to_global(coupling_matrices_out_in[(ii, jj)],
                                                            coupling_patterns_out_in[(ii, jj)],
                                                            jj, ii, system_matrix)

        op = DuneXTMatrixOperator(system_matrix)
        mats = np.full((grid.num_subdomains, grid.num_subdomains), None)
        for ii in range(grid.num_subdomains):
            for jj in range(ii, grid.num_subdomains):
                if ii == jj:
                    mats[ii, ii] = Matrix(block_space.local_space(ii).size(),
                                          block_space.local_space(ii).size(),
                                          local_patterns[ii])
                elif (ii, jj) in coupling_matrices_in_out:
                    mats[ii, jj] = Matrix(block_space.local_space(ii).size(),
                                          block_space.local_space(jj).size(),
                                          coupling_patterns_in_out[(ii, jj)])
                    mats[jj, ii] = Matrix(block_space.local_space(jj).size(),
                                          block_space.local_space(ii).size(),
                                          coupling_patterns_out_in[(ii, jj)])

        for ii in range(grid.num_subdomains):
            for jj in range(ii, grid.num_subdomains):
                if ii == jj:
                    mats[ii, ii].axpy(1.,  local_matrices[ii])
                    if ii in boundary_matrices:
                        mats[ii, ii].axpy(1.,  boundary_matrices[ii])
                elif (ii, jj) in coupling_matrices_in_out:
                    mats[ii, ii].axpy(1., coupling_matrices_in_in[(ii, jj)])
                    mats[jj, jj].axpy(1., coupling_matrices_out_out[(ii, jj)])
                    mats[ii, jj].axpy(1., coupling_matrices_in_out[(ii, jj)])
                    mats[jj, ii].axpy(1., coupling_matrices_out_in[(ii, jj)])

        ops = np.full((grid.num_subdomains, grid.num_subdomains), None)
        for (ii, jj), mat in np.ndenumerate(mats):
            ops[ii, jj] = DuneXTMatrixOperator(mat,
                                               source_id='domain_{}'.format(jj),
                                               range_id='domain_{}'.format(ii)) if mat else None
        block_op = BlockOperator(ops)

        rhs = VectorFunctional(op.range.make_array([rhs_vector]))
        rhss = []
        for ii in range(grid.num_subdomains):
            rhss.append(ops[ii, ii].range.make_array([local_vectors[ii]]))
        block_rhs = VectorFunctional(block_op.range.make_array(rhss))

        return op, block_op, rhs, block_rhs

    ops, block_ops, rhss, block_rhss = zip(*(discretize_lhs_for_lambda(l) for l in affine_lambda['functions']))
    rhs = rhss[0]
    block_rhs = block_rhss[0]

    lambda_coeffs = affine_lambda['coefficients']
    op = LincombOperator(ops, lambda_coeffs)
    block_op = LincombOperator(block_ops, lambda_coeffs, name='lhs')

    operators = {'global_op': op, 'global_rhs': rhs}
    global_rt_space = make_rt_space(grid)

    def assemble_oswald_interpolation_error():
        oi_ops = [OswaldInterpolationErrorOperator(ii, ii, ii, block_op.source, grid, block_space,
                                                   global_rt_space, neighborhood_boundary_info,
                                                   None, None, None, None)
                  for ii in range(grid.num_subdomains)]
        return BlockDiagonalOperator(oi_ops, name='oswald_interpolation_error')

    oi_op = assemble_oswald_interpolation_error()

    def assemble_flux_reconstruction(lambda_xi):
        fr_ops = [FluxReconstructionOperator(ii, ii, ii, block_op.source, grid, block_space,
                                             global_rt_space, neighborhood_boundary_info,
                                             lambda_hat, lambda_xi, lambda_xi, kappa)
                  for ii in range(grid.num_subdomains)]
        return BlockDiagonalOperator(fr_ops)

    fr_op = LincombOperator([assemble_flux_reconstruction(lambda_xi) for lambda_xi in affine_lambda['functions']],
                            lambda_coeffs, name='flux_reconstruction')

    spaces = block_op.source.subspaces
    rt_spaces = fr_op.range.subspaces

    # assemble local products
    for ii in range(grid.num_subdomains):
        local_space = block_space.local_space(ii)
        # we want a larger pattern for the elliptic part, to allow for axpy with the penalty part
        tmp_local_matrix = Matrix(local_space.size(),
                                  local_space.size(),
                                  local_space.compute_pattern('face_and_volume'))
        local_product_ops = []
        local_product_coeffs = []
        for func, coeff in zip(affine_lambda['functions'], affine_lambda['coefficients']):
            local_product_ops.append(make_elliptic_matrix_operator(
                func, kappa, tmp_local_matrix.copy(), local_space, over_integrate=0))
            local_product_coeffs.append(coeff)
            local_product_ops.append(make_penalty_product_matrix_operator(
                grid, ii, local_all_dirichlet_boundary_info,
                local_space,
                func, kappa, over_integrate=0))
            local_product_coeffs.append(coeff)
        del tmp_local_matrix
        local_assembler = make_system_assembler(local_space)
        for local_product_op in local_product_ops:
            local_assembler.append(local_product_op)
        local_assembler.assemble()
        local_product_name = 'local_energy_dg_product_{}'.format(ii)
        local_product = LincombOperator([DuneXTMatrixOperator(op.matrix(),
                                                              source_id='domain_{}'.format(ii),
                                                              range_id='domain_{}'.format(ii))
                                         for op in local_product_ops],
                                        local_product_coeffs,
                                        name=local_product_name)
        operators[local_product_name] = local_product.assemble(mu_bar).with_(name=local_product_name)

    # assemble error estimator
    for ii in range(grid.num_subdomains):

        neighborhood = grid.neighborhood_of(ii)

        def assemble_estimator_noconformity():
            nc_ops = np.full((grid.num_subdomains,) * 2, None)
            for jj in neighborhood:
                for kk in neighborhood:
                    nc_ops[jj, kk] = NonconformityOperator(ii, jj, kk, block_op.source, grid, block_space,
                                                           global_rt_space, neighborhood_boundary_info,
                                                           lambda_bar, None, None, kappa)
            return BlockOperator(nc_ops, range_spaces=oi_op.range.subspaces, source_spaces=oi_op.range.subspaces,
                                 name='nonconformity_{}'.format(ii))

        def assemble_estimator_diffusive_flux_aa(lambda_xi, lambda_xi_prime):
            df_ops = np.full((grid.num_subdomains,) * 2, None)
            df_ops[ii, ii] = DiffusiveFluxOperatorAA(ii, ii, ii, block_op.source, grid, block_space,
                                                     global_rt_space, neighborhood_boundary_info,
                                                     lambda_hat, lambda_xi, lambda_xi_prime, kappa)

            return BlockOperator(df_ops, range_spaces=spaces, source_spaces=spaces)

        def assemble_estimator_diffusive_flux_bb():
            df_ops = np.full((grid.num_subdomains,) * 2, None)
            for jj in neighborhood:
                for kk in neighborhood:
                    df_ops[jj, kk] = DiffusiveFluxOperatorBB(ii, jj, kk, block_op.source, grid, block_space,
                                                             global_rt_space, neighborhood_boundary_info,
                                                             lambda_hat, None, None, kappa)

            return BlockOperator(df_ops, range_spaces=rt_spaces, source_spaces=rt_spaces,
                                 name='diffusive_flux_bb_{}'.format(ii))

        def assemble_estimator_diffusive_flux_ab(lambda_xi):
            df_ops = np.full((grid.num_subdomains,) * 2, None)
            for kk in neighborhood:
                df_ops[ii, kk] = DiffusiveFluxOperatorAB(ii, ii, kk, block_op.source, grid, block_space,
                                                         global_rt_space, neighborhood_boundary_info,
                                                         lambda_hat, lambda_xi, None, kappa)

            return BlockOperator(df_ops, range_spaces=spaces, source_spaces=rt_spaces)

        def assemble_estimator_residual():
            r2_ops = np.full((grid.num_subdomains,) * 2, None)
            for jj in neighborhood:
                for kk in neighborhood:
                    r2_ops[jj, kk] = ResidualPartOperator(ii, jj, kk, block_op.source, grid, block_space,
                                                          global_rt_space, neighborhood_boundary_info,
                                                          lambda_hat, None, None, kappa)

            return BlockOperator(r2_ops, range_spaces=rt_spaces, source_spaces=rt_spaces, name='residual_{}'.format(ii))

        def assemble_estimator_residual_functional():
            r1_ops = np.full((1, grid.num_subdomains,), None)
            for jj in neighborhood:
                r1_ops[0, jj] = ResidualPartFunctional(f, ii, jj, jj, block_op.source, grid, block_space,
                                                       global_rt_space, neighborhood_boundary_info,
                                                       lambda_hat, None, None, kappa)
            return BlockOperator(r1_ops, source_spaces=rt_spaces, name='residual_functional_{}'.format(ii))

        operators['nc_{}'.format(ii)] = assemble_estimator_noconformity()

        operators['r1_{}'.format(ii)] = assemble_estimator_residual_functional()
        operators['r2_{}'.format(ii)] = assemble_estimator_residual()

        operators['df_aa_{}'.format(ii)] = LincombOperator(
            [assemble_estimator_diffusive_flux_aa(lambda_xi, lambda_xi_prime)
             for lambda_xi in affine_lambda['functions']
             for lambda_xi_prime in affine_lambda['functions']],
            [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()
        operators['df_ab_{}'.format(ii)] = LincombOperator(
            [assemble_estimator_diffusive_flux_ab(lambda_xi) for lambda_xi in affine_lambda['functions']],
            lambda_coeffs,
            name='diffusive_flux_ab_{}'.format(ii)
        )

    min_diffusion_evs = np.array([min_diffusion_eigenvalue(grid, ii, lambda_hat, kappa) for ii in
                                  range(grid.num_subdomains)])
    subdomain_diameters = np.array([subdomain_diameter(grid, ii) for ii in range(grid.num_subdomains)])
    local_eta_rf_squared = np.array([apply_l2_product(grid, ii, f, f, over_integrate=2) for ii in
                                     range(grid.num_subdomains)])
    estimator = Estimator(min_diffusion_evs, subdomain_diameters, local_eta_rf_squared, lambda_coeffs, mu_bar, mu_hat,
                          fr_op, oi_op)

    neighborhoods = [grid.neighborhood_of(ii) for ii in range(grid.num_subdomains)]
    local_boundary_info = make_subdomain_boundary_info(grid_and_problem_data['grid'],
                                                       {'type': 'xt.grid.boundaryinfo.alldirichlet'})
    d = DuneDiscretization(block_op, block_rhs,
                           neighborhoods,
                           (grid, local_boundary_info, affine_lambda, kappa, f, block_space),
                           visualizer=DuneGDTVisualizer(block_space),
                           operators=operators, estimator=estimator)
    d = d.with_(parameter_space=CubicParameterSpace(d.parameter_type, parameter_range[0], parameter_range[1]))

    return d, block_space
Example #16
0
def make_marix_operator(mat, ID):
    return DuneXTMatrixOperator(mat, source_id=ID, range_id=ID)
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