예제 #1
0
    def test_tCoordinates(self):
        NWorld = np.array([5])
        xt = util.tCoordinates(NWorld)
        self.assertTrue(np.isclose(np.max(np.abs(xt.T - np.array([1., 3., 5., 7., 9.])/10)), 0))

        NWorld = np.array([9, 9, 9, 9])
        xt = util.tCoordinates(NWorld)
        ind = util.convertpCoordinateToIndex(NWorld-1, [7, 3, 6, 0])
        self.assertTrue(np.allclose(xt[ind] - np.array([15., 7., 13., 1.])/18., 0))
def build_randomcheckerboard(Nepsilon, NFine, alpha, beta, p):
    # builds a random checkerboard coefficient with spectral bounds alpha and beta,
    # piece-wise constant on mesh with Nepsilon blocks
    # returns a fine coefficient on mesh with NFine blocks
    Ntepsilon = np.prod(Nepsilon)
    c = np.random.binomial(1, p, Ntepsilon)
    values = alpha + (beta - alpha) * c

    def randomcheckerboard(x):
        index = (x * Nepsilon).astype(int)
        d = np.shape(index)[1]

        if d == 1:
            flatindex = index[:]
        if d == 2:
            flatindex = index[:, 1] * Nepsilon[0] + index[:, 0]
        if d == 3:
            flatindex = index[:, 2] * (Nepsilon[0] * Nepsilon[1]
                                       ) + index[:, 1] * Nepsilon[0] + index[:,
                                                                             0]
        else:
            NotImplementedError('other dimensions not available')

        return values[flatindex]

    xFine = util.tCoordinates(NFine)

    return randomcheckerboard(xFine).flatten()
예제 #3
0
    def computeTransformation(self, aFine, f_ref):
        assert (len(aFine) == np.prod(self.world.NWorldFine))
        assert (len(f_ref) == np.prod(self.world.NWorldFine + 1))
        assert (self.psi)
        psi = self.psi

        NFine = self.world.NWorldFine
        xtFine = util.tCoordinates(NFine)

        a_trans = np.einsum('tij, t, tkj, t -> tik', psi.Jinv(xtFine), aFine,
                            psi.Jinv(xtFine), psi.detJ(xtFine))

        xpFine = util.pCoordinates(NFine)
        xpFine_pert = self.psi.evaluate(xpFine)

        f_trans = func.evaluateCQ1(NFine, f_ref, xpFine_pert)

        f_trans = np.einsum('t, t -> t', f_trans, psi.detJ(xpFine_pert))

        return a_trans, f_trans
예제 #4
0
    def computePerturbation(self, aFine, f_ref):
        assert (self.psi)
        assert (len(aFine) == np.prod(self.world.NWorldFine))

        NFine = self.world.NWorldFine

        # Elements
        xtFine = util.tCoordinates(NFine)
        xtFine_ref = self.psi.inverse_evaluate(xtFine)
        xtFine_pert = self.psi.evaluate(xtFine_ref)

        # Nodes
        xpFine = util.pCoordinates(NFine)
        xpFine_ref = self.psi.inverse_evaluate(xpFine)
        xpFine_pert = self.psi.evaluate(xpFine_ref)

        self.checkInvertible(xpFine_pert, xpFine)

        aFine_pert = func.evaluateDQ0(NFine, aFine, xtFine_ref)
        # f_pert = func.evaluateCQ1(NFine, f_ref, xpFine_ref)
        return aFine_pert, f_ref
예제 #5
0
import scipy.sparse as sparse
import matplotlib.pyplot as plt
from gridlod import util, fem, coef, interp
from gridlod.world import World
import lod_wave

# fine mesh parameters
fine = 1024
NFine = np.array([fine])
NpFine = np.prod(NFine + 1)
boundaryConditions = np.array([[0, 0]])
world = World(NFine, NFine // NFine, boundaryConditions)
NWorldFine = world.NWorldCoarse * world.NCoarseElement

# fine grid elements and nodes
xt = util.tCoordinates(NFine).flatten()
xp = util.pCoordinates(NFine).flatten()

# time step parameters
tau = 0.1
numTimeSteps = 10

# ms coefficients
epsA = 2 ** (-4)
epsB = 2 ** (-6)
aFine = (2 - np.sin(2 * np.pi * xt / epsA)) ** (-1)
bFine = (2 - np.cos(2 * np.pi * xt / epsB)) ** (-1)

k_0 = np.inf
k_1 = k_0
N = 16
예제 #6
0
                                          steps=4,
                                          Right=1)
aFine_ref = aFine_ref_shaped.flatten()
number_of_channels = len(CoefClass.ShapeRemember)

# Discrete mapping
Nmapping = np.array([int(fine), int(fine)])

size_of_an_element = 1. / fine
walk_with_perturbation = size_of_an_element

channels_position_from_zero = space
channels_end_from_zero = channels_position_from_zero + thick

xpFine = util.pCoordinates(NFine)
xtFine = util.tCoordinates(NFine)

NWorldCoarse = np.array([N, N])
boundaryConditions = np.array([[0, 0], [0, 0]])

NCoarseElement = NFine // NWorldCoarse
world = World(NWorldCoarse, NCoarseElement, boundaryConditions)
every_psi_was_valid = []
k = 3

#I want to know the exact places of the channels
ref_array = aFine_ref_shaped[0]


def create_psi_function():
    cq1 = np.zeros((int(fine) + 1, int(fine) + 1))
alpha = 1.
psi = psi_functions.smooth_1d(NFine, alpha)

aFine = np.ones(fine)
aFine /= 10
aPert = np.copy(aFine)

for i in range(int(fine* 2/8.) - 1, int(fine * 3/8.) - 1):
    aFine[i] = 1

for i in range(int(fine* 5/8.) - 1, int(fine * 6/8.) - 1):
    aFine[i] = 1


xpCoarse = util.pCoordinates(NFine).flatten()
xtCoarse = util.tCoordinates(NFine).flatten()

aPert = psi.inverse_transformation(aFine, xtCoarse)

jAj = psi.apply_transformation_to_bilinear_form(aFine, xtCoarse)

f = np.ones(fine + 1)
f_pert = psi.apply_transformation_to_linear_functional(f, xpCoarse)

plt.figure('right hand side')
plt.plot(np.arange(fine + 1), f, label='NT')
plt.plot(np.arange(fine + 1), f_pert, label='TR')
plt.legend()

# interior nodes for plotting
xt = util.tCoordinates(NFine).flatten()
예제 #8
0
    def test_1d(self):
        # Example from Peterseim, Variational Multiscale Stabilization and the Exponential Decay of correctors, p. 2
        # Two modifications: A with minus and u(here) = 1/4*u(paper).
        NFine = np.array([3200])
        NpFine = np.prod(NFine + 1)
        NList = [10, 20, 40, 80, 160]
        epsilon = 1024. / NFine
        epsilon = 1. / 320
        k = 2

        pi = np.pi

        xt = util.tCoordinates(NFine).flatten()
        xp = util.pCoordinates(NFine).flatten()
        #aFine = (2 + np.cos(2*pi*xt/epsilon))**(-1)
        aFine = (2 - np.cos(2 * pi * xt / epsilon))**(-1)

        uSol = 4 * (xp - xp**2) - 4 * epsilon * (
            1 / (4 * pi) * np.sin(2 * pi * xp / epsilon) - 1 /
            (2 * pi) * xp * np.sin(2 * pi * xp / epsilon) - epsilon /
            (4 * pi**2) * np.cos(2 * pi * xp / epsilon) + epsilon /
            (4 * pi**2))

        uSol = uSol / 4

        previousErrorCoarse = np.inf
        previousErrorFine = np.inf

        for N in NList:
            NWorldCoarse = np.array([N])
            NCoarseElement = NFine / NWorldCoarse
            boundaryConditions = np.array([[0, 0]])
            world = World(NWorldCoarse, NCoarseElement, boundaryConditions)

            xpCoarse = util.pCoordinates(NWorldCoarse).flatten()

            NpCoarse = np.prod(NWorldCoarse + 1)

            IPatchGenerator = lambda i, N: interp.L2ProjectionPatchMatrix(
                i, N, NWorldCoarse, NCoarseElement, boundaryConditions)
            aCoef = coef.coefficientFine(NWorldCoarse, NCoarseElement, aFine)

            pglod = pg.PetrovGalerkinLOD(world, k, IPatchGenerator, 0)
            pglod.updateCorrectors(aCoef, clearFineQuantities=False)

            KFull = pglod.assembleMsStiffnessMatrix()
            MFull = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)

            free = util.interiorpIndexMap(NWorldCoarse)

            f = np.ones(NpCoarse)
            bFull = MFull * f

            KFree = KFull[free][:, free]
            bFree = bFull[free]

            xFree = sparse.linalg.spsolve(KFree, bFree)

            basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                   NCoarseElement)
            basisCorrectors = pglod.assembleBasisCorrectors()
            modifiedBasis = basis - basisCorrectors
            xFull = np.zeros(NpCoarse)
            xFull[free] = xFree
            uLodCoarse = basis * xFull
            uLodFine = modifiedBasis * xFull

            AFine = fem.assemblePatchMatrix(NFine, world.ALocFine, aFine)
            MFine = fem.assemblePatchMatrix(NFine, world.MLocFine)

            newErrorCoarse = np.sqrt(
                np.dot(uSol - uLodCoarse, MFine * (uSol - uLodCoarse)))
            newErrorFine = np.sqrt(
                np.dot(uSol - uLodFine, AFine * (uSol - uLodFine)))

            self.assertTrue(newErrorCoarse < previousErrorCoarse)
            self.assertTrue(newErrorFine < previousErrorFine)
예제 #9
0
        def test_1d_core(mapper):
            # Example from Peterseim, Variational Multiscale Stabilization and the Exponential Decay of correctors, p. 2
            # Two modifications: A with minus and u(here) = 1/4*u(paper).
            NFine = np.array([3200])
            NpFine = np.prod(NFine + 1)
            NList = [10, 20, 40, 80, 160]
            epsilon = 1. / 320
            k = 2

            pi = np.pi

            xt = util.tCoordinates(NFine).flatten()
            xp = util.pCoordinates(NFine).flatten()
            aFine = (2 - np.cos(2 * pi * xt / epsilon))**(-1)

            uSol = 4 * (xp - xp**2) - 4 * epsilon * (
                1 / (4 * pi) * np.sin(2 * pi * xp / epsilon) - 1 /
                (2 * pi) * xp * np.sin(2 * pi * xp / epsilon) - epsilon /
                (4 * pi**2) * np.cos(2 * pi * xp / epsilon) + epsilon /
                (4 * pi**2))

            uSol = uSol / 4

            previousErrorCoarse = np.inf
            previousErrorFine = np.inf

            for N in NList:
                NWorldCoarse = np.array([N])
                NCoarseElement = NFine // NWorldCoarse
                boundaryConditions = np.array([[0, 0]])
                world = World(NWorldCoarse, NCoarseElement, boundaryConditions)

                xpCoarse = util.pCoordinates(NWorldCoarse).flatten()

                def computeKmsij(TInd):
                    patch = Patch(world, k, TInd)
                    IPatch = lambda: interp.L2ProjectionPatchMatrix(
                        patch, boundaryConditions)
                    aPatch = lambda: coef.localizeCoefficient(patch, aFine)

                    correctorsList = lod.computeBasisCorrectors(
                        patch, IPatch, aPatch)
                    csi = lod.computeBasisCoarseQuantities(
                        patch, correctorsList, aPatch)
                    return patch, correctorsList, csi.Kmsij

                # Use mapper to distribute computations (mapper could be the 'map' built-in or e.g. an ipyparallel map)
                patchT, correctorsListT, KmsijT = zip(
                    *mapper(computeKmsij, range(world.NtCoarse)))

                KFull = pglod.assembleMsStiffnessMatrix(world, patchT, KmsijT)
                MFull = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)

                free = util.interiorpIndexMap(NWorldCoarse)

                f = np.ones(world.NpCoarse)
                bFull = MFull * f

                KFree = KFull[free][:, free]
                bFree = bFull[free]

                xFree = sparse.linalg.spsolve(KFree, bFree)

                basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                       NCoarseElement)
                basisCorrectors = pglod.assembleBasisCorrectors(
                    world, patchT, correctorsListT)
                modifiedBasis = basis - basisCorrectors
                xFull = np.zeros(world.NpCoarse)
                xFull[free] = xFree
                uLodCoarse = basis * xFull
                uLodFine = modifiedBasis * xFull

                AFine = fem.assemblePatchMatrix(NFine, world.ALocFine, aFine)
                MFine = fem.assemblePatchMatrix(NFine, world.MLocFine)

                newErrorCoarse = np.sqrt(
                    np.dot(uSol - uLodCoarse, MFine * (uSol - uLodCoarse)))
                newErrorFine = np.sqrt(
                    np.dot(uSol - uLodFine, AFine * (uSol - uLodFine)))

                self.assertTrue(newErrorCoarse < previousErrorCoarse)
                self.assertTrue(newErrorFine < previousErrorFine)
def helmholtz_nonlinear_adaptive(mapper, fineLvl, coarseLvl, maxit):
    fineExp = fineLvl
    NFine = np.array([2**fineLvl, 2**fineLvl])
    NpFine = np.prod(NFine + 1)
    N = 2**coarseLvl
    tolList = [2.0, 1.0, 0.5, 0.25, 0.125, 0.0625, 0.]
    ell = 2  # localization parameter

    k = 15.  # wavenumber
    maxit_Fine = 200

    xt = util.tCoordinates(NFine)
    xp = util.pCoordinates(NFine)

    # multiscale coefficients on the scale NFine-2
    np.random.seed(444)
    sizeK = np.size(xt[:, 0])
    nFine = NFine[0]

    # determine domain D_eps = supp(1-n) = supp(1-A) (all equal for the moment)
    indicesIn = (xt[:, 0] > 0.15) & (xt[:, 0] < 0.85) & (xt[:, 1] > 0.15) & (
        xt[:, 1] < 0.85)
    indicesInEps = (xt[:, 0] > 0.15) & (xt[:, 0] < 0.85) & (
        xt[:, 1] > 0.15) & (xt[:, 1] < 0.85)

    # coefficients
    aFine = np.ones(xt.shape[0])

    cn = .05  # lower bound on n
    Cn = 1.  # upper bound on n
    nEpsPro = coeffi(xt[:, 0], xt[:, 1], fineLvl)

    k2Fine = k**2 * np.ones(xt.shape[0])
    k2Fine[indicesIn] = k**2 * ((Cn - cn) * nEpsPro[indicesIn] + cn)
    kFine = k * np.ones(xt.shape[0])

    Ceps = 0.3  # upper bound on eps (lower bound is 0)
    epsEpsPro = np.ones(sizeK)
    epsFine = np.zeros(xt.shape[0])
    epsFine[indicesInEps] = Ceps * epsEpsPro[indicesInEps]  # 0 OR Ceps

    plotC = np.ones(sizeK)
    plotC[indicesIn] = nEpsPro[indicesIn]
    drawCoefficient(NFine, plotC)

    xC = xp[:, 0]
    yC = xp[:, 1]

    # define right-hand side and boundary condition
    def funcF(x, y):
        res = 100 * np.ones(x.shape, dtype='complex128')
        return res

    f = funcF(xC, yC)

    # reference solution
    uSol = np.zeros(NpFine, dtype='complex128')

    # boundary conditions
    boundaryConditions = np.array([[1, 1], [1, 1]])
    worldFine = World(NFine, np.array([1, 1]), boundaryConditions)

    # fine matrices
    BdFineFEM = fem.assemblePatchBoundaryMatrix(
        NFine, fem.localBoundaryMassMatrixGetter(NFine))
    MFineFEM = fem.assemblePatchMatrix(NFine, fem.localMassMatrix(NFine))
    KFineFEM = fem.assemblePatchMatrix(
        NFine, fem.localStiffnessMatrix(NFine))  # , aFine)

    kBdFine = fem.assemblePatchBoundaryMatrix(
        NFine, fem.localBoundaryMassMatrixGetter(NFine), kFine)
    KFine = fem.assemblePatchMatrix(NFine, fem.localStiffnessMatrix(NFine),
                                    aFine)

    print('***computing reference solution***')

    uOldFine = np.zeros(NpFine, dtype='complex128')

    for it in np.arange(maxit_Fine):
        print('-- itFine = %d' % it)
        knonlinUpreFine = np.abs(uOldFine)
        knonlinUFine = func.evaluateCQ1(NFine, knonlinUpreFine, xt)

        k2FineUfine = np.copy(k2Fine)
        k2FineUfine[indicesInEps] *= (
            1. + epsFine[indicesInEps] * knonlinUFine[indicesInEps]**2
        )  # full coefficient, including nonlinearity

        k2MFine = fem.assemblePatchMatrix(
            NFine, fem.localMassMatrix(NFine),
            k2FineUfine)  # weighted mass matrix, updated in every iteration

        nodesFine = np.arange(worldFine.NpFine)
        fixFine = util.boundarypIndexMap(NFine, boundaryConditions == 0)
        freeFine = np.setdiff1d(nodesFine, fixFine)

        # right-hand side
        fhQuad = MFineFEM * f

        # fine system
        lhsh = KFine[freeFine][:, freeFine] - k2MFine[
            freeFine][:, freeFine] + 1j * kBdFine[freeFine][:, freeFine]
        rhsh = fhQuad[freeFine]
        xFreeFine = sparse.linalg.spsolve(lhsh, rhsh)

        xFullFine = np.zeros(worldFine.NpFine, dtype='complex128')
        xFullFine[freeFine] = xFreeFine
        uOldFine = np.copy(xFullFine)

        # residual - used as stopping criterion
        knonlinU = np.abs(uOldFine)
        knonlinUFineIt = func.evaluateCQ1(NFine, knonlinU, xt)

        k2FineUfineIt = np.copy(k2Fine)
        k2FineUfineIt[indicesInEps] *= (
            1. + epsFine[indicesInEps] * knonlinUFineIt[indicesInEps]**2
        )  # update full coefficient, including nonlinearity

        k2MFineIt = fem.assemblePatchMatrix(NFine, fem.localMassMatrix(NFine),
                                            k2FineUfineIt)
        Ares = KFine - k2MFineIt + 1j * kBdFine
        residual = np.linalg.norm(Ares * xFullFine - fhQuad) / np.linalg.norm(
            Ares * xFullFine)
        print('---- residual = %.4e' % residual)

        if residual < 1e-12:
            break  # stopping criterion

    uSol = xFullFine  # final fine reference solution

    print('***reference solution computed***\n')

    counter = 0  # for figures

    print('***computing multiscale approximations***')

    relErrEnergy = np.zeros([len(tolList), maxit])

    for tol in tolList:
        counter += 1
        print('H = %.4e, tol = %.4e' % (1. / N, tol))
        NWorldCoarse = np.array([N, N])
        NCoarseElement = NFine // NWorldCoarse
        world = World(NWorldCoarse, NCoarseElement, boundaryConditions)
        NpCoarse = np.prod(NWorldCoarse + 1)

        uOldUps = np.zeros(NpFine, dtype='complex128')

        for it in np.arange(maxit):
            print('-- it = %d:' % it)
            knonlinUpre = np.abs(uOldUps)
            knonlinU = func.evaluateCQ1(NFine, knonlinUpre, xt)

            k2FineU = np.copy(k2Fine)
            k2FineU[indicesInEps] *= (
                1. + epsFine[indicesInEps] * knonlinU[indicesInEps]**2)

            print('---- starting computation of correctors')

            def computeLocalContribution(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def computeIndicators(TInd):
                k2FineUPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineU)
                k2FineUOldPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineUOld)

                E_vh = lod.computeErrorIndicatorCoarse_helmholtz(
                    patchT[TInd], muTPrime[TInd], k2FineUOldPatch,
                    k2FineUPatch)
                return E_vh

            def UpdateCorrectors(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch, k2Patch)
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def UpdateElements(tol, E, Kmsij_old, Mmsij_old, Bdmsij_old,
                               correctors_old, mu_old):
                print('---- apply tolerance')
                Elements_to_be_updated = []
                for (i, eps) in E.items():
                    if eps > tol * k**2:
                        Elements_to_be_updated.append(i)
                if len(E) > 0:
                    print(
                        '---- percentage of non-zero element correctors to be updated: %.4f'
                        % (100 * np.size(Elements_to_be_updated) / len(E)),
                        flush=True)
                    print(
                        '---- total percentage of element correctors to be updated: %.4f'
                        %
                        (100 * np.size(Elements_to_be_updated) / len(mu_old)),
                        flush=True)

                print('---- update local contributions')
                KmsijT_list = list(np.copy(Kmsij_old))
                MmsijT_list = list(np.copy(Mmsij_old))
                BdmsijT_list = list(np.copy(Bdmsij_old))
                muT_list = np.copy(mu_old)
                for T in np.setdiff1d(range(world.NtCoarse),
                                      Elements_to_be_updated):
                    patch = Patch(world, ell, T)
                    aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                    kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                    k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)
                    csi = lod.computeBasisCoarseQuantities_helmholtz(
                        patch, correctors_old[T], aPatch, kPatch, k2Patch)

                    KmsijT_list[T] = csi.Kmsij
                    MmsijT_list[T] = csi.Mmsij
                    BdmsijT_list[T] = csi.Bdmsij
                    muT_list[T] = csi.muTPrime

                if np.size(Elements_to_be_updated) != 0:
                    #print('---- update correctors')
                    patchT_irrelevant, correctorsListTNew, KmsijTNew, MmsijTNew, BdmsijTNew, muTPrimeNew = zip(
                        *mapper(UpdateCorrectors, Elements_to_be_updated))

                    #print('---- update correctorsList')
                    correctorsListT_list = list(np.copy(correctors_old))
                    i = 0
                    for T in Elements_to_be_updated:
                        KmsijT_list[T] = KmsijTNew[i]
                        correctorsListT_list[T] = correctorsListTNew[i]
                        MmsijT_list[T] = MmsijTNew[i]
                        BdmsijT_list[T] = BdmsijTNew[i]
                        muT_list[T] = muTPrimeNew[i]
                        i += 1

                    KmsijT = tuple(KmsijT_list)
                    correctorsListT = tuple(correctorsListT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime
                else:
                    KmsijT = tuple(KmsijT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctors_old, KmsijT, MmsijT, BdmsijT, muTPrime

            if it == 0:
                patchT, correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = zip(
                    *mapper(computeLocalContribution, range(world.NtCoarse)))
            else:
                E_vh = list(mapper(computeIndicators, range(world.NtCoarse)))
                print(
                    '---- maximal value error estimator for basis correctors {}'
                    .format(np.max(E_vh)))
                E = {i: E_vh[i] for i in range(np.size(E_vh)) if E_vh[i] > 0}

                # loop over elements with possible recomputation of correctors
                correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = UpdateElements(
                    tol, E, KmsijT, MmsijT, BdmsijT, correctorsListT,
                    muTPrime)  # tol scaled by maximal error indicator

            print('---- finished computation of correctors')

            KLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, KmsijT)  # ms stiffness matrix
            k2MLOD = pglod.assembleMsStiffnessMatrix(world, patchT,
                                                     MmsijT)  # ms mass matrix
            kBdLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, BdmsijT)  # ms boundary matrix
            MFEM = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)
            BdFEM = fem.assemblePatchBoundaryMatrix(
                NWorldCoarse, fem.localBoundaryMassMatrixGetter(NWorldCoarse))
            print('---- coarse matrices assembled')

            nodes = np.arange(world.NpCoarse)
            fix = util.boundarypIndexMap(NWorldCoarse, boundaryConditions == 0)
            free = np.setdiff1d(nodes, fix)
            assert (nodes.all() == free.all())

            # compute global interpolation matrix
            patchGlobal = Patch(world, NFine[0] + 2, 0)
            IH = interp.L2ProjectionPatchMatrix(patchGlobal,
                                                boundaryConditions)
            assert (IH.shape[0] == NpCoarse)

            basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                   NCoarseElement)

            fHQuad = basis.T * MFineFEM * f

            print('---- solving coarse system')

            # coarse system
            lhsH = KLOD[free][:, free] - k2MLOD[
                free][:, free] + 1j * kBdLOD[free][:, free]
            rhsH = fHQuad[free]
            xFree = sparse.linalg.spsolve(lhsH, rhsH)

            basisCorrectors = pglod.assembleBasisCorrectors(
                world, patchT, correctorsListT)
            modifiedBasis = basis - basisCorrectors

            xFull = np.zeros(world.NpCoarse, dtype='complex128')
            xFull[free] = xFree
            uLodCoarse = basis * xFull
            uLodFine = modifiedBasis * xFull
            uOldUps = np.copy(uLodFine)
            k2FineUOld = np.copy(k2FineU)

            Err = np.sqrt(
                np.dot((uSol - uLodFine).conj(), KFineFEM *
                       (uSol - uLodFine)) + k**2 *
                np.dot((uSol - uLodFine).conj(), MFineFEM * (uSol - uLodFine)))
            ErrEnergy = Err / np.sqrt(
                np.dot((uSol).conj(), KFineFEM *
                       (uSol)) + k**2 * np.dot((uSol).conj(), MFineFEM *
                                               (uSol)))
            print('---- ', np.abs(ErrEnergy),
                  '\n***********************************************')

            # save errors in arrays
            relErrEnergy[counter - 1, it] = ErrEnergy

        print('\n')

    its = np.arange(1, maxit + 1)
    plt.figure(1)
    plt.title(
        'Relative energy errors w.r.t iterations for different tolerances - Ex 3'
    )
    plt.plot(its, relErrEnergy[0, :], 'x--', color='black', label='tol = 2')
    plt.plot(its, relErrEnergy[1, :], 'x-', color='blue', label='tol = 1')
    plt.plot(its, relErrEnergy[2, :], 'x-', color='green', label='tol = 0.5')
    plt.plot(its, relErrEnergy[3, :], 'x-', color='orange', label='tol = 0.25')
    plt.plot(its, relErrEnergy[4, :], 'x-', color='red', label='tol = 0.125')
    plt.plot(its,
             relErrEnergy[5, :],
             'x-',
             color='magenta',
             label='tol = 0.0625')
    plt.plot(its, relErrEnergy[6, :], 'x--', color='black', label='tol = 0')
    plt.yscale('log')
    plt.legend()

    plt.show()
    uLodCoarse = xFull
    uLodFine = modifiedBasis*xFull
    
    return uLodCoarse, uLodFine
    
# Example from Peterseim, Variational Multiscale Stabilization and the Exponential Decay of correctors, p. 2
# Two modifications: A with minus and u(here) = 1/4*u(paper).
fine = 1024
NFine = np.array([fine])
NpFine = np.prod(NFine+1)
NList = [2,4,8,16,32]

epsilon = 2**(-5)

pi = np.pi
xt = util.tCoordinates(NFine).flatten() #whats that mean values of the intervalls xp
xp = util.pCoordinates(NFine).flatten()

aFine = (2 - np.cos(2*pi*xt/epsilon))**(-1)

uSol  = 4*(xp - xp**2) - 4*epsilon*(1/(4*pi)*np.sin(2*pi*xp/epsilon) -
                                    1/(2*pi)*xp*np.sin(2*pi*xp/epsilon) -
                                    epsilon/(4*pi**2)*np.cos(2*pi*xp/epsilon) +
                                    epsilon/(4*pi**2))

uSol = uSol/4

plt.figure('Coefficient')
plt.plot(xt,aFine, label='$A_{\epsilon}(x)$')
plt.yticks((0,np.max(aFine)+np.min(aFine)),fontsize="small")
plt.ylabel('$y$', fontsize="small")
def helmholtz_nonlinear_adaptive(mapper, fineLvl, maxCoarseLvl, maxit):
    NFine = np.array([2**fineLvl, 2**fineLvl])
    NpFine = np.prod(NFine + 1)
    NList = 2**np.arange(1, maxCoarseLvl + 1)
    ell = 2  # localization parameter

    k = 30.  # wavenumber
    maxit_Fine = 250
    tol = 0.5  # coupled to maximal error indicator

    xt = util.tCoordinates(NFine)
    xp = util.pCoordinates(NFine)

    # multiscale coefficients on the scale NFine-2
    np.random.seed(123)
    sizeK = np.size(xt[:, 0])
    nFine = NFine[0]

    # determine domain D_eps = supp(1-n) = supp(1-A) (all equal for this experiment)
    indicesIn = (xt[:, 0] > 0.25) & (xt[:, 0] < 0.75) & (xt[:, 1] > 0.25) & (
        xt[:, 1] < 0.75)
    indicesInEps = (xt[:, 0] > 0.25) & (xt[:, 0] < 0.75) & (
        xt[:, 1] > 0.25) & (xt[:, 1] < 0.75)

    # coefficients
    cA = .2  # lower bound on A
    CA = 1.  # upper bound on A
    aEps = np.random.uniform(0, 1, sizeK // 16)
    aEpsPro = np.zeros(sizeK)
    for i in range((nFine) // 4):
        aEpsPro[4 * i * (nFine):4 * (i + 1) * (nFine)] = np.tile(
            np.repeat(aEps[i * (nFine) // 4:(i + 1) * (nFine) // 4], 4), 4)
    aFine = np.ones(xt.shape[0])
    aFine[indicesIn] = (CA - cA) * aEpsPro[indicesIn] + cA

    cn = 1.  # lower bound on n
    Cn = 1.  # upper bound on n
    nEps = np.random.uniform(0, 1, sizeK // 16)
    nEpsPro = np.zeros(sizeK)
    for i in range((nFine) // 4):
        nEpsPro[4 * i * (nFine):4 * (i + 1) * (nFine)] = np.tile(
            np.repeat(nEps[i * (nFine) // 4:(i + 1) * (nFine) // 4], 4), 4)

    k2Fine = k**2 * np.ones(xt.shape[0])
    k2Fine[indicesIn] = k**2 * ((Cn - cn) * nEpsPro[indicesIn] + cn)
    kFine = k * np.ones(xt.shape[0])

    Ceps = .85  # upper bound on eps (lower bound is 0)
    lvl = 4
    epsEps = np.random.randint(2, size=(sizeK // lvl**2))
    epsEpsPro = np.zeros(sizeK)
    for i in range((nFine) // lvl):
        epsEpsPro[lvl * i * (nFine):lvl * (i + 1) * (nFine)] = np.tile(
            np.repeat(epsEps[i * (nFine) // lvl:(i + 1) * (nFine) // lvl],
                      lvl), lvl)
    epsFine = np.zeros(xt.shape[0])
    epsFine[indicesInEps] = Ceps * epsEpsPro[indicesInEps]  #  0 OR Ceps

    drawCoefficient(NFine, epsFine)

    xC = xp[:, 0]
    yC = xp[:, 1]

    fact = 100.
    mult = .8
    a = .5
    b = .25
    k2 = 30.

    # define right-hand side and boundary condition
    def funcF(x, y):
        res = mult * (-np.exp(-1.j * k2 * (a * x - b)) *
                      (2 * a**2 * fact**2 * np.sinh(fact * (a * x - b))**2 /
                       (np.cosh(fact * (a * x - b)) + 1)**3 -
                       a**2 * fact**2 * np.cosh(fact * (a * x - b)) /
                       (np.cosh(fact * (a * x - b)) + 1)**2) +
                      a**2 * k2**2 * np.exp(-1.j * k2 * (a * x - b)) /
                      (np.cosh(fact * (a * x - b)) + 1) - 2.j * a**2 * fact *
                      k2 * np.exp(-1.j * k2 *
                                  (a * x - b)) * np.sinh(fact * (a * x - b)) /
                      (np.cosh(fact * (a * x - b)) + 1)**2 -
                      k**2 * np.exp(-1.j * k2 * (a * x - b)) /
                      (np.cosh(fact * (a * x - b)) + 1))
        return res

    f = funcF(xC, yC)

    g = np.zeros(NpFine, dtype='complex128')
    # bottom boundary
    g[0:(NFine[0] +
         1)] = mult * 1.j * k * 1. / (np.cosh(fact *
                                              (a * xC[0:(NFine[0] + 1)] - b)) +
                                      1) * np.exp(
                                          -1.j * k2 *
                                          (a * xC[0:(NFine[0] + 1)] - b))
    # top boundary
    g[(NpFine - NFine[0] -
       1):] = mult * 1.j * k * 1. / (np.cosh(fact * (a * xC[
           (NpFine - NFine[0] - 1):NpFine] - b)) + 1) * np.exp(
               -1.j * k2 * (a * xC[(NpFine - NFine[0] - 1):NpFine] - b))
    # left boundary
    g[0:(NpFine - NFine[0]):(
        NFine[0] +
        1)] = mult * 1.j * k * np.ones_like(yC[0:(NpFine - NFine[0]):(
            NFine[0] + 1)]) / (np.cosh(fact * (a * 0 - b)) + 1) * np.exp(
                -1.j * k2 * (a * 0 - b)) + mult * np.ones_like(
                    yC[0:(NpFine - NFine[0]):(NFine[0] + 1)]) * (
                        a * 1.j * k2 * np.exp(-1.j * k2 * (a * 0 - b)) /
                        (np.cosh((a * 0 - b) * fact) + 1) + a * fact * np.sinh(
                            (a * 0 - b) * fact) * np.exp(-1.j * k2 *
                                                         (a * 0 - b)) /
                        (np.cosh((a * 0 - b) * fact) + 1)**2)
    # right boundary
    g[NFine[0]:NpFine:(
        NFine[0] + 1)] = mult * 1.j * k * np.ones_like(yC[NFine[0]:NpFine:(
            NFine[0] + 1)]) / (np.cosh(fact * (a * 1. - b)) + 1) * np.exp(
                -1.j * k2 * (a * 1. - b)) - mult * np.ones_like(
                    yC[NFine[0]:NpFine:(NFine[0] + 1)]) * (
                        a * 1.j * k2 * np.exp(-1.j * k2 * (a * 1. - b)) /
                        (np.cosh(
                            (a * 1. - b) * fact) + 1) + a * fact * np.sinh(
                                (a * 1. - b) * fact) * np.exp(-1.j * k2 *
                                                              (a * 1. - b)) /
                        (np.cosh((a * 1. - b) * fact) + 1)**2)

    # reference solution
    uSol = np.zeros(NpFine, dtype='complex128')

    # boundary conditions
    boundaryConditions = np.array([[1, 1], [1, 1]])  # Robin boundary
    worldFine = World(NFine, np.array([1, 1]), boundaryConditions)

    # fine matrices
    BdFineFEM = fem.assemblePatchBoundaryMatrix(
        NFine, fem.localBoundaryMassMatrixGetter(NFine))
    MFineFEM = fem.assemblePatchMatrix(NFine, fem.localMassMatrix(NFine))
    KFineFEM = fem.assemblePatchMatrix(NFine, fem.localStiffnessMatrix(NFine))

    kBdFine = fem.assemblePatchBoundaryMatrix(
        NFine, fem.localBoundaryMassMatrixGetter(NFine), kFine)
    KFine = fem.assemblePatchMatrix(NFine, fem.localStiffnessMatrix(NFine),
                                    aFine)

    # incident beam
    uInc = mult / (np.cosh(fact * (a * xC - b)) + 1) * np.exp(-1.j * k2 *
                                                              (a * xC - b))

    print('***computing reference solution***')

    uOldFine = np.zeros(NpFine, dtype='complex128')

    for it in np.arange(maxit_Fine):
        print('-- itFine = %d' % it)
        knonlinUpreFine = np.abs(uOldFine)
        knonlinUFine = func.evaluateCQ1(NFine, knonlinUpreFine, xt)

        k2FineUfine = np.copy(k2Fine)
        k2FineUfine[indicesInEps] *= (
            1. + epsFine[indicesInEps] * knonlinUFine[indicesInEps]**2
        )  # full coefficient, including nonlinearity

        k2MFine = fem.assemblePatchMatrix(
            NFine, fem.localMassMatrix(NFine),
            k2FineUfine)  # weighted mass matrix, updated in every iteration

        nodesFine = np.arange(worldFine.NpFine)
        fixFine = util.boundarypIndexMap(NFine, boundaryConditions == 0)
        freeFine = np.setdiff1d(nodesFine, fixFine)

        # right-hand side (including boundary condition)
        fhQuad = MFineFEM * f + BdFineFEM * g

        # fine system
        lhsh = KFine[freeFine][:, freeFine] - k2MFine[
            freeFine][:, freeFine] + 1j * kBdFine[freeFine][:, freeFine]
        rhsh = fhQuad[freeFine]
        xFreeFine = sparse.linalg.spsolve(lhsh, rhsh)

        xFullFine = np.zeros(worldFine.NpFine, dtype='complex128')
        xFullFine[freeFine] = xFreeFine
        uOldFine = np.copy(xFullFine)

        # residual - used as stopping criterion
        knonlinU = np.abs(uOldFine)
        knonlinUFineIt = func.evaluateCQ1(NFine, knonlinU, xt)

        k2FineUfineIt = np.copy(k2Fine)
        k2FineUfineIt[indicesInEps] *= (
            1. + epsFine[indicesInEps] * knonlinUFineIt[indicesInEps]**2
        )  # update full coefficient, including nonlinearity

        k2MFineIt = fem.assemblePatchMatrix(NFine, fem.localMassMatrix(NFine),
                                            k2FineUfineIt)
        Ares = KFine - k2MFineIt + 1j * kBdFine
        residual = np.linalg.norm(Ares * xFullFine - fhQuad) / np.linalg.norm(
            Ares * xFullFine)
        print('---- residual = %.4e' % residual)

        if residual < 1e-12:
            break  # stopping criterion

    uSol = xFullFine  # final fine reference solution

    print('***reference solution computed***\n')

    ######################################################################################

    print('***computing multiscale approximations***')

    relErrEnergy = np.zeros([len(NList), maxit])

    counter = 0
    for N in NList:
        counter += 1
        print('H = %.4e' % (1. / N))
        NWorldCoarse = np.array([N, N])
        NCoarseElement = NFine // NWorldCoarse
        world = World(NWorldCoarse, NCoarseElement, boundaryConditions)
        NpCoarse = np.prod(NWorldCoarse + 1)

        uOldUps = np.zeros(NpFine, dtype='complex128')

        for it in np.arange(maxit):
            print('-- it = %d:' % it)
            knonlinUpre = np.abs(uOldUps)
            knonlinU = func.evaluateCQ1(NFine, knonlinUpre, xt)

            k2FineU = np.copy(k2Fine)
            k2FineU[indicesInEps] *= (
                1. + epsFine[indicesInEps] * knonlinU[indicesInEps]**2)

            print('---- starting computation of correctors')

            def computeLocalContribution(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch, k2Patch)
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch, k2Patch)
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def computeIndicators(TInd):
                k2FineUPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineU)
                k2FineUOldPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineUOld)

                E_vh = lod.computeErrorIndicatorCoarse_helmholtz(
                    patchT[TInd], muTPrime[TInd], k2FineUOldPatch,
                    k2FineUPatch)
                return E_vh

            def UpdateCorrectors(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch, k2Patch)
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch, k2Patch)
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def UpdateElements(tol, E, Kmsij_old, Mmsij_old, Bdmsij_old,
                               correctors_old, mu_old):
                print('---- apply tolerance')
                Elements_to_be_updated = []
                for (i, eps) in E.items():
                    if eps > tol:
                        Elements_to_be_updated.append(i)
                if len(E) > 0:
                    print(
                        '---- total percentage of element correctors to be updated: %.4f'
                        %
                        (100 * np.size(Elements_to_be_updated) / len(mu_old)),
                        flush=True)

                print('---- update local contributions')
                KmsijT_list = list(np.copy(Kmsij_old))
                MmsijT_list = list(np.copy(Mmsij_old))
                BdmsijT_list = list(np.copy(Bdmsij_old))
                muT_list = np.copy(mu_old)
                for T in np.setdiff1d(range(world.NtCoarse),
                                      Elements_to_be_updated):
                    patch = Patch(world, ell, T)
                    aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                    kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                    k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)
                    csi = lod.computeBasisCoarseQuantities_helmholtz(
                        patch, correctors_old[T], aPatch, kPatch, k2Patch)

                    KmsijT_list[T] = csi.Kmsij
                    MmsijT_list[T] = csi.Mmsij
                    BdmsijT_list[T] = csi.Bdmsij
                    muT_list[T] = csi.muTPrime

                if np.size(Elements_to_be_updated) != 0:
                    #print('---- update correctors')
                    patchT_irrelevant, correctorsListTNew, KmsijTNew, MmsijTNew, BdmsijTNew, muTPrimeNew = zip(
                        *mapper(UpdateCorrectors, Elements_to_be_updated))

                    #print('---- update correctorsList')
                    correctorsListT_list = list(np.copy(correctors_old))
                    i = 0
                    for T in Elements_to_be_updated:
                        KmsijT_list[T] = KmsijTNew[i]
                        correctorsListT_list[T] = correctorsListTNew[i]
                        MmsijT_list[T] = MmsijTNew[i]
                        BdmsijT_list[T] = BdmsijTNew[i]
                        muT_list[T] = muTPrimeNew[i]
                        i += 1

                    KmsijT = tuple(KmsijT_list)
                    correctorsListT = tuple(correctorsListT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime
                else:
                    KmsijT = tuple(KmsijT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctors_old, KmsijT, MmsijT, BdmsijT, muTPrime

            if it == 0:
                patchT, correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = zip(
                    *mapper(computeLocalContribution, range(world.NtCoarse)))
            else:
                E_vh = list(mapper(computeIndicators, range(world.NtCoarse)))
                print(
                    '---- maximal value error estimator for basis correctors {}'
                    .format(np.max(E_vh)))
                E = {i: E_vh[i] for i in range(np.size(E_vh)) if E_vh[i] > 0}

                # loop over elements with possible recomputation of correctors
                correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = UpdateElements(
                    tol * np.max(E_vh), E, KmsijT, MmsijT, BdmsijT,
                    correctorsListT,
                    muTPrime)  # tol scaled by maximal error indicator

            print('---- finished computation of correctors')

            KLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, KmsijT)  # ms stiffness matrix
            k2MLOD = pglod.assembleMsStiffnessMatrix(world, patchT,
                                                     MmsijT)  # ms mass matrix
            kBdLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, BdmsijT)  # ms boundary matrix
            MFEM = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)
            BdFEM = fem.assemblePatchBoundaryMatrix(
                NWorldCoarse, fem.localBoundaryMassMatrixGetter(NWorldCoarse))
            print('---- coarse matrices assembled')

            nodes = np.arange(world.NpCoarse)
            fix = util.boundarypIndexMap(NWorldCoarse, boundaryConditions == 0)
            free = np.setdiff1d(nodes, fix)
            assert (nodes.all() == free.all())

            # compute global interpolation matrix
            patchGlobal = Patch(world, NFine[0] + 2, 0)
            IH = interp.L2ProjectionPatchMatrix(patchGlobal,
                                                boundaryConditions)
            assert (IH.shape[0] == NpCoarse)

            basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                   NCoarseElement)

            fHQuad = basis.T * MFineFEM * f + basis.T * BdFineFEM * g

            print('---- solving coarse system')

            # coarse system
            lhsH = KLOD[free][:, free] - k2MLOD[
                free][:, free] + 1j * kBdLOD[free][:, free]
            rhsH = fHQuad[free]
            xFree = sparse.linalg.spsolve(lhsH, rhsH)

            basisCorrectors = pglod.assembleBasisCorrectors(
                world, patchT, correctorsListT)
            modifiedBasis = basis - basisCorrectors

            xFull = np.zeros(world.NpCoarse, dtype='complex128')
            xFull[free] = xFree
            uLodCoarse = basis * xFull
            uLodFine = modifiedBasis * xFull
            uOldUps = np.copy(uLodFine)
            k2FineUOld = np.copy(k2FineU)

            # visualization
            if it == maxit - 1 and N == 2**4:
                grid = uLodFine.reshape(NFine + 1, order='C')

                plt.figure(2)
                plt.title('LOD_ad, Hlvl=4 - Ex 2')
                plt.imshow(grid.real,
                           extent=(xC.min(), xC.max(), yC.min(), yC.max()),
                           cmap=plt.cm.hot,
                           origin='lower',
                           vmin=-.6,
                           vmax=.6)
                plt.colorbar()

                grid2 = uSol.reshape(NFine + 1, order='C')

                plt.figure(1)
                plt.title('reference solution - Ex 2')
                plt.imshow(grid2.real,
                           extent=(xC.min(), xC.max(), yC.min(), yC.max()),
                           cmap=plt.cm.hot,
                           origin='lower',
                           vmin=-.6,
                           vmax=.6)
                plt.colorbar()

                grid3 = uInc.reshape(NFine + 1, order='C')

                plt.figure(6)
                plt.title('incident beam - Ex 2')
                plt.imshow(grid3.real,
                           extent=(xC.min(), xC.max(), yC.min(), yC.max()),
                           cmap=plt.cm.hot,
                           origin='lower',
                           vmin=-.6,
                           vmax=.6)
                plt.colorbar()

            Err = np.sqrt(
                np.dot((uSol - uLodFine).conj(), KFineFEM *
                       (uSol - uLodFine)) + k**2 *
                np.dot((uSol - uLodFine).conj(), MFineFEM * (uSol - uLodFine)))
            ErrEnergy = Err / np.sqrt(
                np.dot((uSol).conj(), KFineFEM *
                       (uSol)) + k**2 * np.dot((uSol).conj(), MFineFEM *
                                               (uSol)))
            print('---- ', np.abs(ErrEnergy),
                  '\n***********************************************')

            # save errors in arrays
            relErrEnergy[counter - 1, it] = ErrEnergy

        print('\n')

######################################################################################

    print(
        '***computing multiscale approximations without updates of correctors***'
    )

    relErrEnergyNoUpdate = np.zeros([len(NList), maxit])

    counter = 0
    for N in NList:
        counter += 1
        print('H = %.4e' % (1. / N))
        NWorldCoarse = np.array([N, N])
        NCoarseElement = NFine // NWorldCoarse
        world = World(NWorldCoarse, NCoarseElement, boundaryConditions)
        NpCoarse = np.prod(NWorldCoarse + 1)

        uOldUps = np.zeros(NpFine, dtype='complex128')

        for it in np.arange(maxit):
            print('-- it = %d:' % it)
            knonlinUpre = np.abs(uOldUps)
            knonlinU = func.evaluateCQ1(NFine, knonlinUpre, xt)

            k2FineU = np.copy(k2Fine)
            k2FineU[indicesInEps] *= (
                1. + epsFine[indicesInEps] * knonlinU[indicesInEps]**2)

            print('---- starting computation of correctors')

            def computeLocalContribution(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def computeIndicators(TInd):
                k2FineUPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineU)
                k2FineUOldPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineUOld)

                E_vh = lod.computeErrorIndicatorCoarse_helmholtz(
                    patchT[TInd], muTPrime[TInd], k2FineUOldPatch,
                    k2FineUPatch)
                return E_vh

            def UpdateCorrectors(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch, k2Patch)
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def UpdateElements(tol, E, Kmsij_old, Mmsij_old, Bdmsij_old,
                               correctors_old, mu_old):
                print('---- apply tolerance')
                Elements_to_be_updated = []
                for (i, eps) in E.items():
                    if eps > tol:
                        Elements_to_be_updated.append(i)
                if len(E) > 0:
                    print(
                        '---- total percentage of element correctors to be updated: %.4f'
                        %
                        (100 * np.size(Elements_to_be_updated) / len(mu_old)),
                        flush=True)

                print('---- update local contributions')
                KmsijT_list = list(np.copy(Kmsij_old))
                MmsijT_list = list(np.copy(Mmsij_old))
                BdmsijT_list = list(np.copy(Bdmsij_old))
                muT_list = np.copy(mu_old)
                for T in np.setdiff1d(range(world.NtCoarse),
                                      Elements_to_be_updated):
                    patch = Patch(world, ell, T)
                    aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                    kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                    k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)
                    csi = lod.computeBasisCoarseQuantities_helmholtz(
                        patch, correctors_old[T], aPatch, kPatch, k2Patch)

                    KmsijT_list[T] = csi.Kmsij
                    MmsijT_list[T] = csi.Mmsij
                    BdmsijT_list[T] = csi.Bdmsij
                    muT_list[T] = csi.muTPrime

                if np.size(Elements_to_be_updated) != 0:
                    #print('---- update correctors')
                    patchT_irrelevant, correctorsListTNew, KmsijTNew, MmsijTNew, BdmsijTNew, muTPrimeNew = zip(
                        *mapper(UpdateCorrectors, Elements_to_be_updated))

                    #print('---- update correctorsList')
                    correctorsListT_list = list(np.copy(correctors_old))
                    i = 0
                    for T in Elements_to_be_updated:
                        KmsijT_list[T] = KmsijTNew[i]
                        correctorsListT_list[T] = correctorsListTNew[i]
                        MmsijT_list[T] = MmsijTNew[i]
                        BdmsijT_list[T] = BdmsijTNew[i]
                        muT_list[T] = muTPrimeNew[i]
                        i += 1

                    KmsijT = tuple(KmsijT_list)
                    correctorsListT = tuple(correctorsListT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime
                else:
                    KmsijT = tuple(KmsijT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctors_old, KmsijT, MmsijT, BdmsijT, muTPrime

            if it == 0:
                patchT, correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = zip(
                    *mapper(computeLocalContribution, range(world.NtCoarse)))
            else:
                E_vh = list(mapper(computeIndicators, range(world.NtCoarse)))
                print(
                    '---- maximal value error estimator for basis correctors {}'
                    .format(np.max(E_vh)))
                E = {i: E_vh[i] for i in range(np.size(E_vh)) if E_vh[i] > 0}

                # loop over elements with possible recomputation of correctors
                correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = UpdateElements(
                    2. * np.max(E_vh), E, KmsijT, MmsijT, BdmsijT,
                    correctorsListT, muTPrime)  # no updates

            print('---- finished computation of correctors')

            KLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, KmsijT)  # ms stiffness matrix
            k2MLOD = pglod.assembleMsStiffnessMatrix(world, patchT,
                                                     MmsijT)  # ms mass matrix
            kBdLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, BdmsijT)  # ms boundary matrix
            MFEM = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)
            BdFEM = fem.assemblePatchBoundaryMatrix(
                NWorldCoarse, fem.localBoundaryMassMatrixGetter(NWorldCoarse))
            print('---- coarse matrices assembled')

            nodes = np.arange(world.NpCoarse)
            fix = util.boundarypIndexMap(NWorldCoarse, boundaryConditions == 0)
            free = np.setdiff1d(nodes, fix)
            assert (nodes.all() == free.all())

            # compute global interpolation matrix
            patchGlobal = Patch(world, NFine[0] + 2, 0)
            IH = interp.L2ProjectionPatchMatrix(patchGlobal,
                                                boundaryConditions)
            assert (IH.shape[0] == NpCoarse)

            basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                   NCoarseElement)

            fHQuad = basis.T * MFineFEM * f + basis.T * BdFineFEM * g

            print('---- solving coarse system')

            # coarse system
            lhsH = KLOD[free][:, free] - k2MLOD[
                free][:, free] + 1j * kBdLOD[free][:, free]
            rhsH = fHQuad[free]
            xFree = sparse.linalg.spsolve(lhsH, rhsH)

            basisCorrectors = pglod.assembleBasisCorrectors(
                world, patchT, correctorsListT)
            modifiedBasis = basis - basisCorrectors

            xFull = np.zeros(world.NpCoarse, dtype='complex128')
            xFull[free] = xFree
            uLodCoarse = basis * xFull
            uLodFine = modifiedBasis * xFull
            uOldUps = np.copy(uLodFine)
            k2FineUOld = np.copy(k2FineU)

            # visualization
            if it == maxit - 1 and N == 2**4:
                grid = uLodFine.reshape(NFine + 1, order='C')

                plt.figure(3)
                plt.title('LOD_inf, Hlvl=4 - Ex 2')
                plt.imshow(grid.real,
                           extent=(xC.min(), xC.max(), yC.min(), yC.max()),
                           cmap=plt.cm.hot,
                           origin='lower',
                           vmin=-.6,
                           vmax=.6)
                plt.colorbar()

            Err = np.sqrt(
                np.dot((uSol - uLodFine).conj(), KFineFEM *
                       (uSol - uLodFine)) + k**2 *
                np.dot((uSol - uLodFine).conj(), MFineFEM * (uSol - uLodFine)))
            ErrEnergy = Err / np.sqrt(
                np.dot((uSol).conj(), KFineFEM *
                       (uSol)) + k**2 * np.dot((uSol).conj(), MFineFEM *
                                               (uSol)))
            print('---- ', np.abs(ErrEnergy),
                  '\n***********************************************')

            # save errors in arrays
            relErrEnergyNoUpdate[counter - 1, it] = ErrEnergy

        print('\n')

######################################################################################

    print(
        '***computing multiscale approximations where all correctors in the part of the domain with active nonlinearity are recomputed***'
    )

    relErrEnergyFullUpdate = np.zeros([len(NList), maxit])

    counter = 0
    for N in NList:
        counter += 1
        print('H = %.4e' % (1. / N))
        NWorldCoarse = np.array([N, N])
        NCoarseElement = NFine // NWorldCoarse
        world = World(NWorldCoarse, NCoarseElement, boundaryConditions)
        NpCoarse = np.prod(NWorldCoarse + 1)

        uOldUps = np.zeros(NpFine, dtype='complex128')

        for it in np.arange(maxit):
            print('-- it = %d:' % it)
            knonlinUpre = np.abs(uOldUps)
            knonlinU = func.evaluateCQ1(NFine, knonlinUpre, xt)

            k2FineU = np.copy(k2Fine)
            k2FineU[indicesInEps] *= (
                1. + epsFine[indicesInEps] * knonlinU[indicesInEps]**2)

            print('---- starting computation of correctors')

            def computeLocalContribution(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def computeIndicators(TInd):
                k2FineUPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineU)
                k2FineUOldPatch = lambda: coef.localizeCoefficient(
                    patchT[TInd], k2FineUOld)

                E_vh = lod.computeErrorIndicatorCoarse_helmholtz(
                    patchT[TInd], muTPrime[TInd], k2FineUOldPatch,
                    k2FineUPatch)
                return E_vh

            def UpdateCorrectors(TInd):
                patch = Patch(world, ell, TInd)
                IPatch = lambda: interp.L2ProjectionPatchMatrix(
                    patch, boundaryConditions)
                aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)

                correctorsList = lod.computeBasisCorrectors_helmholtz(
                    patch, IPatch, aPatch, kPatch, k2Patch)
                csi = lod.computeBasisCoarseQuantities_helmholtz(
                    patch, correctorsList, aPatch, kPatch,
                    k2Patch)  # adapted for Helmholtz setting
                return patch, correctorsList, csi.Kmsij, csi.Mmsij, csi.Bdmsij, csi.muTPrime

            def UpdateElements(tol, E, Kmsij_old, Mmsij_old, Bdmsij_old,
                               correctors_old, mu_old):
                print('---- apply tolerance')
                Elements_to_be_updated = []
                for (i, eps) in E.items():
                    if eps > tol:
                        Elements_to_be_updated.append(i)
                if len(E) > 0:
                    print(
                        '---- total percentage of element correctors to be updated: %.4f'
                        %
                        (100 * np.size(Elements_to_be_updated) / len(mu_old)),
                        flush=True)

                print('---- update local contributions')
                KmsijT_list = list(np.copy(Kmsij_old))
                MmsijT_list = list(np.copy(Mmsij_old))
                BdmsijT_list = list(np.copy(Bdmsij_old))
                muT_list = np.copy(mu_old)
                for T in np.setdiff1d(range(world.NtCoarse),
                                      Elements_to_be_updated):
                    patch = Patch(world, ell, T)
                    aPatch = lambda: coef.localizeCoefficient(patch, aFine)
                    kPatch = lambda: coef.localizeCoefficient(patch, kFine)
                    k2Patch = lambda: coef.localizeCoefficient(patch, k2FineU)
                    csi = lod.computeBasisCoarseQuantities_helmholtz(
                        patch, correctors_old[T], aPatch, kPatch, k2Patch)

                    KmsijT_list[T] = csi.Kmsij
                    MmsijT_list[T] = csi.Mmsij
                    BdmsijT_list[T] = csi.Bdmsij
                    muT_list[T] = csi.muTPrime

                if np.size(Elements_to_be_updated) != 0:
                    #print('---- update correctors')
                    patchT_irrelevant, correctorsListTNew, KmsijTNew, MmsijTNew, BdmsijTNew, muTPrimeNew = zip(
                        *mapper(UpdateCorrectors, Elements_to_be_updated))

                    #print('---- update correctorsList')
                    correctorsListT_list = list(np.copy(correctors_old))
                    i = 0
                    for T in Elements_to_be_updated:
                        KmsijT_list[T] = KmsijTNew[i]
                        correctorsListT_list[T] = correctorsListTNew[i]
                        MmsijT_list[T] = MmsijTNew[i]
                        BdmsijT_list[T] = BdmsijTNew[i]
                        muT_list[T] = muTPrimeNew[i]
                        i += 1

                    KmsijT = tuple(KmsijT_list)
                    correctorsListT = tuple(correctorsListT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime
                else:
                    KmsijT = tuple(KmsijT_list)
                    MmsijT = tuple(MmsijT_list)
                    BdmsijT = tuple(BdmsijT_list)
                    muTPrime = tuple(muT_list)
                    return correctors_old, KmsijT, MmsijT, BdmsijT, muTPrime

            if it == 0:
                patchT, correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = zip(
                    *mapper(computeLocalContribution, range(world.NtCoarse)))
            else:
                E_vh = list(mapper(computeIndicators, range(world.NtCoarse)))
                print(
                    '---- maximal value error estimator for basis correctors {}'
                    .format(np.max(E_vh)))
                E = {i: E_vh[i] for i in range(np.size(E_vh)) if E_vh[i] > 0}

                # loop over elements with possible recomputation of correctors
                correctorsListT, KmsijT, MmsijT, BdmsijT, muTPrime = UpdateElements(
                    0., E, KmsijT, MmsijT, BdmsijT, correctorsListT,
                    muTPrime)  # no updates

            print('---- finished computation of correctors')

            KLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, KmsijT)  # ms stiffness matrix
            k2MLOD = pglod.assembleMsStiffnessMatrix(world, patchT,
                                                     MmsijT)  # ms mass matrix
            kBdLOD = pglod.assembleMsStiffnessMatrix(
                world, patchT, BdmsijT)  # ms boundary matrix
            MFEM = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)
            BdFEM = fem.assemblePatchBoundaryMatrix(
                NWorldCoarse, fem.localBoundaryMassMatrixGetter(NWorldCoarse))
            print('---- coarse matrices assembled')

            nodes = np.arange(world.NpCoarse)
            fix = util.boundarypIndexMap(NWorldCoarse, boundaryConditions == 0)
            free = np.setdiff1d(nodes, fix)
            assert (nodes.all() == free.all())

            # compute global interpolation matrix
            patchGlobal = Patch(world, NFine[0] + 2, 0)
            IH = interp.L2ProjectionPatchMatrix(patchGlobal,
                                                boundaryConditions)
            assert (IH.shape[0] == NpCoarse)

            basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                   NCoarseElement)

            fHQuad = basis.T * MFineFEM * f + basis.T * BdFineFEM * g

            print('---- solving coarse system')

            # coarse system
            lhsH = KLOD[free][:, free] - k2MLOD[
                free][:, free] + 1j * kBdLOD[free][:, free]
            rhsH = fHQuad[free]
            xFree = sparse.linalg.spsolve(lhsH, rhsH)

            basisCorrectors = pglod.assembleBasisCorrectors(
                world, patchT, correctorsListT)
            modifiedBasis = basis - basisCorrectors

            xFull = np.zeros(world.NpCoarse, dtype='complex128')
            xFull[free] = xFree
            uLodCoarse = basis * xFull
            uLodFine = modifiedBasis * xFull
            uOldUps = np.copy(uLodFine)
            k2FineUOld = np.copy(k2FineU)

            # visualization
            if it == maxit - 1 and N == 2**4:
                grid = uLodFine.reshape(NFine + 1, order='C')

                plt.figure(7)
                plt.title('LOD_inf, Hlvl=4 - Ex 2')
                plt.imshow(grid.real,
                           extent=(xC.min(), xC.max(), yC.min(), yC.max()),
                           cmap=plt.cm.hot,
                           origin='lower',
                           vmin=-.6,
                           vmax=.6)
                plt.colorbar()

            Err = np.sqrt(
                np.dot((uSol - uLodFine).conj(), KFineFEM *
                       (uSol - uLodFine)) + k**2 *
                np.dot((uSol - uLodFine).conj(), MFineFEM * (uSol - uLodFine)))
            ErrEnergy = Err / np.sqrt(
                np.dot((uSol).conj(), KFineFEM *
                       (uSol)) + k**2 * np.dot((uSol).conj(), MFineFEM *
                                               (uSol)))
            print('---- ', np.abs(ErrEnergy),
                  '\n***********************************************')

            # save errors in arrays
            relErrEnergyFullUpdate[counter - 1, it] = ErrEnergy

        print('\n')

######################################################################################

    print('***computing FEM approximations***')

    FEMrelErrEnergy = np.zeros([len(NList), maxit])

    counter = 0
    for N in NList:
        counter += 1
        print('H = %.4e' % (1. / N))
        NWorldCoarse = np.array([N, N])
        NCoarseElement = NFine // NWorldCoarse
        world = World(NWorldCoarse, NCoarseElement, boundaryConditions)
        NpCoarse = np.prod(NWorldCoarse + 1)

        xT = util.tCoordinates(NWorldCoarse)
        xP = util.pCoordinates(NWorldCoarse)

        uOld = np.zeros(NpCoarse, dtype='complex128')

        # compute coarse coefficients by averaging
        NtC = np.prod(NWorldCoarse)
        aCoarse = np.zeros(NtC)
        kCoarse = k * np.ones(xT.shape[0])
        k2Coarse = np.zeros(NtC)
        epsCoarse = np.zeros(NtC)
        for Q in range(NtC):
            patch = Patch(world, 0, Q)
            aPatch = coef.localizeCoefficient(patch, aFine)
            epsPatch = coef.localizeCoefficient(patch, epsFine)
            k2Patch = coef.localizeCoefficient(patch, k2Fine)

            aCoarse[Q] = np.sum(aPatch) / (len(aPatch))
            k2Coarse[Q] = np.sum(k2Patch) / (len(k2Patch))
            epsCoarse[Q] = np.sum(epsPatch) / (len(epsPatch))

        # coarse matrices
        KFEM = fem.assemblePatchMatrix(NWorldCoarse,
                                       fem.localStiffnessMatrix(NWorldCoarse),
                                       aCoarse)
        kBdFEM = fem.assemblePatchBoundaryMatrix(
            NWorldCoarse, fem.localBoundaryMassMatrixGetter(NWorldCoarse),
            kCoarse)
        MFEM = fem.assemblePatchMatrix(NWorldCoarse, world.MLocCoarse)
        BdFEM = fem.assemblePatchBoundaryMatrix(
            NWorldCoarse, fem.localBoundaryMassMatrixGetter(NWorldCoarse))

        for it in np.arange(maxit):
            print('-- it = %d:' % it)
            knonlinUpre = np.abs(uOld)
            knonlinU = func.evaluateCQ1(NWorldCoarse, knonlinUpre, xT)

            k2CoarseU = np.copy(k2Coarse)
            k2CoarseU *= (1. + epsCoarse * knonlinU**2)

            # update weighted mass matrix
            k2MFEM = fem.assemblePatchMatrix(NWorldCoarse,
                                             fem.localMassMatrix(NWorldCoarse),
                                             k2CoarseU)

            nodes = np.arange(world.NpCoarse)
            fix = util.boundarypIndexMap(NWorldCoarse, boundaryConditions == 0)
            free = np.setdiff1d(nodes, fix)
            assert (nodes.all() == free.all())

            basis = fem.assembleProlongationMatrix(NWorldCoarse,
                                                   NCoarseElement)

            fHQuad = basis.T * MFineFEM * f + basis.T * BdFineFEM * g

            print('---- solving coarse system')

            # coarse system
            lhsH = KFEM[free][:, free] - k2MFEM[
                free][:, free] + 1j * kBdFEM[free][:, free]
            rhsH = fHQuad[free]
            xFree = sparse.linalg.spsolve(lhsH, rhsH)

            xFull = np.zeros(world.NpCoarse, dtype='complex128')
            xFull[free] = xFree
            uCoarseInt = basis * xFull
            uOld = np.copy(xFull)

            # visualization
            if it == maxit - 1 and N == 2**4:
                grid = uCoarseInt.reshape(NFine + 1, order='C')

                plt.figure(4)
                plt.title('FEM, Hlvl=4 - Ex 2')
                plt.imshow(grid.real,
                           extent=(xC.min(), xC.max(), yC.min(), yC.max()),
                           cmap=plt.cm.hot,
                           origin='lower',
                           vmin=-.6,
                           vmax=.6)
                plt.colorbar()

            Err = np.sqrt(
                np.dot((uSol -
                        uCoarseInt).conj(), KFineFEM * (uSol - uCoarseInt)) +
                k**2 * np.dot(
                    (uSol - uCoarseInt).conj(), MFineFEM *
                    (uSol - uCoarseInt)))
            ErrEnergy = Err / np.sqrt(
                np.dot((uSol).conj(), KFineFEM *
                       (uSol)) + k**2 * np.dot((uSol).conj(), MFineFEM *
                                               (uSol)))
            print('---- ', np.abs(ErrEnergy),
                  '\n***********************************************')

            # save errors in arrays
            FEMrelErrEnergy[counter - 1, it] = ErrEnergy

        print('\n')

    # error plots
    errLOD_2 = np.min(relErrEnergy, 1)
    errLOD0_2 = np.min(relErrEnergyNoUpdate, 1)
    errLODall_2 = np.min(relErrEnergyFullUpdate, 1)
    errFEM_2 = np.min(FEMrelErrEnergy, 1)

    Hs = 0.5**np.arange(1, maxCoarseLvl + 1)

    plt.figure(5)
    plt.title('Relative energy errors w.r.t H - Ex 2')
    plt.plot(Hs, errLOD_2, 'x-', color='blue', label='LOD_ad')
    plt.plot(Hs, errLOD0_2, 'x-', color='green', label='LOD_inf')
    plt.plot(Hs, errLODall_2, 'x-', color='orange', label='LOD_0')
    plt.plot(Hs, errFEM_2, 'x-', color='red', label='FEM')
    plt.plot([0.5, 0.0078125], [0.75, 0.01171875],
             color='black',
             linestyle='dashed',
             label='order 1')
    plt.yscale('log')
    plt.xscale('log')
    plt.legend()

    plt.show()