class OswaldInterpolationErrorOperator(OperatorBase):

    linear = True

    def __init__(self, subdomain, solution_space, grid, block_space):
        self.subdomain, self.grid, self.block_space = subdomain, grid, block_space
        self.neighborhood = grid.neighborhood_of(subdomain)
        self.source = solution_space.subspaces[subdomain]
        self.range = BlockVectorSpace(
            [solution_space.subspaces[ii] for ii in self.neighborhood],
            'OI_{}'.format(subdomain))

    def apply(self, U, mu=None):
        assert U in self.source
        results = self.range.empty(reserve=len(U))
        for u_i in range(len(U)):
            result = self.range.zeros()
            result._blocks[self.neighborhood.index(self.subdomain)].axpy(
                1, U[u_i])

            for i_ii, ii in enumerate(self.neighborhood):
                ii_neighborhood = self.grid.neighborhood_of(ii)
                ii_neighborhood_space = self.block_space.restricted_to_neighborhood(
                    ii_neighborhood)

                subdomain_uh_with_neighborhood_support = make_discrete_function(
                    ii_neighborhood_space,
                    ii_neighborhood_space.project_onto_neighborhood([
                        U._list[u_i].impl if nn == self.subdomain else Vector(
                            self.block_space.local_space(nn).size(), 0.)
                        for nn in ii_neighborhood
                    ], ii_neighborhood))

                interpolated_u_vector = ii_neighborhood_space.project_onto_neighborhood(
                    [
                        Vector(self.block_space.local_space(nn).size(), 0.)
                        for nn in ii_neighborhood
                    ], ii_neighborhood)
                interpolated_u = make_discrete_function(
                    ii_neighborhood_space, interpolated_u_vector)

                apply_oswald_interpolation_operator(
                    self.grid, ii,
                    make_subdomain_boundary_info(
                        self.grid,
                        {'type': 'xt.grid.boundaryinfo.alldirichlet'}),
                    subdomain_uh_with_neighborhood_support, interpolated_u)

                local_sizes = np.array([
                    ii_neighborhood_space.local_space(nn).size()
                    for nn in ii_neighborhood
                ])
                offsets = np.hstack(([0], np.cumsum(local_sizes)))
                ind = ii_neighborhood.index(ii)
                result._blocks[i_ii]._list[0].data[:] -= \
                    np.frombuffer(interpolated_u_vector)[offsets[ind]:offsets[ind+1]]
            results.append(result)

        return results
class FluxReconstructionOperator(OperatorBase):

    linear = True

    def __init__(self, subdomain, solution_space, grid, block_space,
                 global_rt_space, subdomain_rt_spaces, lambda_xi, kappa):
        self.grid = grid
        self.block_space = block_space
        self.global_rt_space = global_rt_space
        self.subdomain_rt_spaces = subdomain_rt_spaces
        self.subdomain = subdomain
        self.neighborhood = grid.neighborhood_of(subdomain)
        self.lambda_xi = lambda_xi
        self.kappa = kappa

        self.source = solution_space.subspaces[subdomain]
        vector_type = solution_space.subspaces[0].vector_type
        self.range = BlockVectorSpace([
            DuneXTVectorSpace(vector_type, subdomain_rt_spaces[ii].size(),
                              'LOCALRT_' + str(ii))
            for ii in self.grid.neighborhood_of(subdomain)
        ], 'RT_{}'.format(subdomain))

    def apply(self, U, mu=None):
        assert U in self.source
        result = self.range.empty(reserve=len(U))
        local_subdomains, num_local_subdomains, num_global_subdomains = _get_subdomains(
            self.grid)
        for u_i in range(len(U)):
            subdomain_uhs_with_global_support = \
                make_discrete_function(
                    self.block_space,
                    self.block_space.project_onto_neighborhood(
                        [U._list[u_i].impl if nn == self.subdomain else
                         Vector(self.block_space.local_space(nn).size(), 0.)
                         for nn in range(num_global_subdomains)],
                        [nn for nn in range(num_global_subdomains)]
                    )
                )

            reconstructed_uh_kk_with_global_support = make_discrete_function(
                self.global_rt_space)
            apply_diffusive_flux_reconstruction_in_neighborhood(
                self.grid, self.subdomain, self.lambda_xi, self.kappa,
                subdomain_uhs_with_global_support,
                reconstructed_uh_kk_with_global_support)

            blocks = [
                s.make_array([
                    self.subdomain_rt_spaces[ii].restrict(
                        reconstructed_uh_kk_with_global_support.vector_copy())
                ])  # NOQA
                for s, ii in zip(self.range.subspaces,
                                 self.grid.neighborhood_of(self.subdomain))
            ]
            result.append(self.range.make_array(blocks))

        return result