Exemplo n.º 1
0
def invert_cq1_mapping(N, mapping):
    """Invert a y = psi(x) CQ1 mapping using the iterative method "A
    simple fixed-point approach to invert a deformation field", Chen
    et al 2008.
    """

    coords = util.pCoordinates(N)
    displace = mapping - coords

    inv_displace = np.zeros_like(displace)

    max_iter = 100
    iter = 0

    tolerance = 1e-7
    error = 1

    while iter < max_iter and error > tolerance:
        inv_displace_previous = inv_displace
        points = np.maximum(0, np.minimum(1.0, coords + inv_displace))
        inv_displace = -func.evaluateCQ1(N, displace, points)
        iter += 1
        error = np.max(np.abs(inv_displace - inv_displace_previous))

    return inv_displace + coords
Exemplo n.º 2
0
    def evaluateSolution(self, u):
        NFine = self.world.NWorldFine

        xpFine = util.pCoordinates(NFine)
        xpFine_ref = self.psi.inverse_evaluate(xpFine)

        return func.evaluateCQ1(NFine, u, xpFine_ref)
Exemplo n.º 3
0
    def test_evaluateCQ1_1d(self):
        N = np.array([3])
        cq1 = np.array([10, 20, 25, 0])

        x = np.array([[0],
                      [1./6],
                      [0.5],
                      [2./3],
                      [1.0]])

        cq1OfX = func.evaluateCQ1(N, cq1, x)
        cq1ShouldBe = np.array([10, 15, 22.5, 25, 0])

        self.assertTrue(np.allclose(cq1OfX, cq1ShouldBe))
Exemplo n.º 4
0
    def test_evaluateCQ1_2d(self):
        N = np.array([3, 2])
        cq1 = np.array([10, 10, 30, 30,
                       60, 60, 80, 80,
                       60, 60, 90, 100])

        x = np.array([[0.1,  0.1],
                      [0.9,  0.1],
                      [0.5,  0.5],
                      [0.0,  0.0],
                      [1.0,  1.0],
                      [5./6, 1.0],
                      [5./6,  .75]])

        cq1OfX = func.evaluateCQ1(N, cq1, x)
        cq1ShouldBe = np.array([20, 40, 70, 10, 100, 95, 87.5])

        self.assertTrue(np.allclose(cq1OfX, cq1ShouldBe))
Exemplo n.º 5
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
Exemplo n.º 6
0
def create_psi_function():
    cq1 = np.zeros((int(fine) + 1, int(fine) + 1))
    for c in range(number_of_channels):
        count = 0
        for i in range(np.size(ref_array)):
            if ref_array[i] == 1:
                count += 1
            if count == (c + 1) * thick:
                begin = i + 1 - space // 2
                end = i + 1 + thick + space // 2
                break

        increasing_length = (end - begin) // 2 - thick - 1
        constant_length = (end - begin) - increasing_length * 2
        epsilon = np.random.binomial(increasing_length - 2, 0.2)
        minus = random.sample([-1, 1], 1)[0]
        epsilon *= minus
        #epsilon = random.sample(list(np.arange(-increasing_length+3,increasing_length-2,1)), 1)[0]
        #print(epsilon)
        maximal_walk = increasing_length * walk_with_perturbation
        walk = epsilon * walk_with_perturbation
        for i in range(increasing_length):
            cq1[:, begin + 1 + i] = (i + 1) / increasing_length * walk
            cq1[:, begin + increasing_length + i +
                constant_length] = walk - (i + 1) / increasing_length * walk

        for i in range(constant_length):
            cq1[:, begin + increasing_length + i] = walk

    cq1 = cq1.flatten()

    alpha = 1.

    for_mapping = np.stack(
        (xpFine[:, 0] + alpha * func.evaluateCQ1(Nmapping, cq1, xpFine),
         xpFine[:, 1]),
        axis=1)
    psi = discrete_mapping.MappingCQ1(NFine, for_mapping)
    return psi, cq1
Exemplo n.º 7
0
def Monte_Carlo_simulation():
    print('Computing Monte Carlo step')

    global aFine_ref
    global aFine_trans
    global aFine_pert
    global k
    global KmsijT
    global correctorsListT

    aFine_ref = aFine_ref_shaped.flatten()
    psi, cq1 = create_psi_function()

    # plt.figure('domain mapping')
    # plt.plot(np.arange(0, fine + 1), cq1[0, :], label='$id(x) - \psi(x)$')
    # plt.plot(np.arange(0, fine), ref_array * 0.01)
    # plt.title('Domain mapping')
    # plt.legend()

    xpFine_pert = psi.evaluate(xpFine)
    xpFine_ref = psi.inverse_evaluate(xpFine)

    xtFine_pert = psi.evaluate(xtFine)
    xtFine_ref = psi.inverse_evaluate(xtFine)

    aFine_pert = func.evaluateDQ0(NFine, aFine_ref, xtFine_ref)
    aBack_ref = func.evaluateDQ0(NFine, aFine_pert, xtFine_pert)

    print('Psi is invertible if this is zero: {}'.format(
        np.linalg.norm(aBack_ref - aFine_ref)))
    every_psi_was_valid.append(np.linalg.norm(aBack_ref - aFine_ref))
    #aFine_trans is the transformed perturbed reference coefficient
    aFine_trans = np.einsum('tji, t, tkj, t -> tik', psi.Jinv(xtFine),
                            aFine_ref, psi.Jinv(xtFine), psi.detJ(xtFine))

    f_pert = np.ones(np.prod(NFine + 1))
    f_ref = func.evaluateCQ1(NFine, f_pert, xpFine_pert)
    f_trans = np.einsum('t, t -> t', f_ref, psi.detJ(xpFine))

    uFineFull_pert, AFine_pert, MFine = femsolver.solveFine(
        world, aFine_pert, f_pert, None, boundaryConditions)
    uFineFull_trans, AFine_trans, _ = femsolver.solveFine(
        world, aFine_trans, f_trans, None, boundaryConditions)

    uFineFull_trans_pert = func.evaluateCQ1(NFine, uFineFull_trans, xpFine_ref)

    energy_norm = np.sqrt(np.dot(uFineFull_pert, AFine_pert * uFineFull_pert))
    energy_error = np.sqrt(
        np.dot((uFineFull_trans_pert - uFineFull_pert),
               AFine_pert * (uFineFull_trans_pert - uFineFull_pert)))
    print("Energy norm {}, error {}, rel. error {}".format(
        energy_norm, energy_error, energy_error / energy_norm))

    Aeye = np.tile(np.eye(2), [np.prod(NFine), 1, 1])
    aFine_ref = np.einsum('tji, t-> tji', Aeye, aFine_ref)

    print('compute domain mapping error indicators')
    epsFine, epsCoarse = zip(*map(computeIndicators, range(world.NtCoarse)))

    print('apply tolerance')
    Elements_to_be_updated = []
    TOL = 0.1
    for i in range(world.NtCoarse):
        if epsFine[i] >= TOL:
            Elements_to_be_updated.append(i)

    print('.... to be updated for domain mapping: {}%'.format(
        np.size(Elements_to_be_updated) / np.size(epsFine) * 100))

    print('update correctors')
    if np.size(Elements_to_be_updated) == 0:
        correctorsListTNew, KmsijTNew = correctorsListT, KmsijT
    else:
        patchT_irrelevant, correctorsListTNew, KmsijTNew, csiTNew = zip(
            *map(UpdateCorrectors, Elements_to_be_updated))

    KmsijT_list = list(KmsijT)
    correctorsListT_list = list(correctorsListT)
    i = 0
    for T in Elements_to_be_updated:
        KmsijT_list[T] = KmsijTNew[i]
        correctorsListT_list[T] = correctorsListTNew[i]
        i += 1

    KmsijT = tuple(KmsijT_list)
    correctorsListT = tuple(correctorsListT_list)

    print('solve the system')
    KFull = pglod.assembleMsStiffnessMatrix(world, patchT, KmsijT)

    MFull = fem.assemblePatchMatrix(NFine, world.MLocFine)

    basis = fem.assembleProlongationMatrix(NWorldCoarse, NCoarseElement)
    basisCorrectors = pglod.assembleBasisCorrectors(world, patchT,
                                                    correctorsListT)
    modifiedBasis = basis - basisCorrectors

    bFull = MFull * f_trans
    bFull = basis.T * bFull

    uFull, _ = pglod.solve(world, KFull, bFull, boundaryConditions)

    uLodFine = modifiedBasis * uFull
    uLodFine_METHOD = uLodFine
    newErrorFine = np.sqrt(
        np.dot(uLodFine - uFineFull_trans,
               AFine_trans * (uLodFine - uFineFull_trans)))
    print('Method error: {}'.format(newErrorFine))

    print('update all correctors')
    patchT_irrelevant, correctorsListT, KmsijT, csiTNew = zip(
        *map(UpdateCorrectors, range(world.NtCoarse)))

    print('solve the system')
    KFull = pglod.assembleMsStiffnessMatrix(world, patchT, KmsijT)

    MFull = fem.assemblePatchMatrix(NFine, world.MLocFine)

    basis = fem.assembleProlongationMatrix(NWorldCoarse, NCoarseElement)
    basisCorrectors = pglod.assembleBasisCorrectors(world, patchT,
                                                    correctorsListT)
    modifiedBasis = basis - basisCorrectors

    bFull = MFull * f_trans
    bFull = basis.T * bFull

    uFull, _ = pglod.solve(world, KFull, bFull, boundaryConditions)

    uLodFine = modifiedBasis * uFull
    newErrorFine = np.sqrt(
        np.dot(uLodFine - uFineFull_trans,
               AFine_trans * (uLodFine - uFineFull_trans)))
    print('Exact LOD error: {}'.format(newErrorFine))

    return uLodFine_METHOD, uLodFine, uFineFull_pert, MFine
Exemplo n.º 8
0
 def inverse_evaluate(self, x):
     return func.evaluateCQ1(self.N, self.inv_mapping, x)
Exemplo n.º 9
0
 def evaluate(self, x):
     return func.evaluateCQ1(self.N, self.mapping, x)
Exemplo n.º 10
0
    def create(self):
        NFine = self.world.NWorldFine
        fine = NFine[0]
        xpFine = util.pCoordinates(NFine)

        number_of_perturbed_channels = 4

        now = 0
        count = 0
        for i in range(np.size(self.ref_array)):
            if self.ref_array[i] == 1:
                count += 1
            if count == 8 * self.thick:  # at the 8ths shape (which is the last dot in one line, the cq starts)
                begin = i + 1
                break
        count = 0
        for i in range(np.size(self.ref_array)):
            if self.ref_array[i] == 1:
                count += 1
            if count == 13 * self.thick - 3:  # it ends after the last channel
                end = i
                break

        # Discrete mapping
        Nmapping = np.array([int(fine), int(fine)])
        cq1 = np.zeros((int(fine) + 1, int(fine) + 1))

        # I only want to perturb on the fine mesh.
        size_of_an_element = 1. / fine
        walk_with_perturbation = size_of_an_element

        channels_position_from_zero = self.space
        channels_end_from_zero = channels_position_from_zero + self.thick

        # The next only have the purpose to make the psi invertible.
        increasing_length = (end - begin) // (number_of_perturbed_channels +
                                              1) - self.thick - 2
        constant_length = (end - begin) - increasing_length * 2
        maximum_walk = (increasing_length - 6) * walk_with_perturbation
        walk_with_perturbation = maximum_walk
        for i in range(increasing_length):
            cq1[:, begin + 1 +
                i] = (i + 1) / increasing_length * walk_with_perturbation
            cq1[:, begin + increasing_length + i +
                constant_length] = walk_with_perturbation - (
                    i + 1) / increasing_length * walk_with_perturbation
        for i in range(constant_length):
            cq1[:, begin + increasing_length + i] = walk_with_perturbation

        # Check what purtubation I have
        if self.plot_mapping:
            plt.figure('DomainMapping')
            plt.plot(np.arange(0, fine + 1),
                     cq1[self.space, :],
                     label='$id(x) - \psi(x)$')
            plt.title('Domain mapping')
            plt.legend()

        cq1 = cq1.flatten()

        xpFine = util.pCoordinates(NFine)

        alpha = 1.

        for_mapping = np.stack(
            (xpFine[:, 0] + alpha * func.evaluateCQ1(Nmapping, cq1, xpFine),
             xpFine[:, 1]),
            axis=1)
        self.psi = discrete_mapping.MappingCQ1(NFine, for_mapping)
Exemplo n.º 11
0
    def create(self):
        NFine = self.world.NWorldFine
        fine = NFine[0]
        xpFine = util.pCoordinates(NFine)

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

        size_of_an_element = 1. / fine
        print('the size of a fine element is {}'.format(size_of_an_element))
        walk_with_perturbation = size_of_an_element

        epsilonT = []
        cq1 = np.zeros((int(fine) + 1, int(fine) + 1))
        random.seed(20)
        cs = np.random.randint(0, 2, self.number_of_channels)
        cs = [c * random.sample([-1, 1], 1)[0] for c in cs]

        ## or manually
        cs[2] = 0
        cs[3] = -1
        cs[4] = 0
        cs[5] = 0

        print(cs)

        last_value = 0
        for i, c in enumerate(cs):
            platform = self.space // 2 + 2 * self.thick
            begin = platform // 2 + i * (self.space + self.thick)
            end = begin + self.space - platform + self.thick

            epsilon = c * walk_with_perturbation
            epsilonT.append(epsilon)
            walk = epsilon - last_value

            constant_length = platform + self.thick
            increasing_length = end - begin

            for i in range(increasing_length):
                cq1[:, begin +
                    i] = last_value + (i + 1) / increasing_length * walk

            for i in range(constant_length):
                cq1[:, begin + increasing_length + i] = epsilon

            last_value = epsilon

        # ending
        begin += self.space + self.thick
        end = begin + self.space - platform + self.thick
        epsilon = 0
        walk = epsilon - last_value
        increasing_length = end - begin
        for i in range(increasing_length):
            cq1[:, begin + i] = last_value + (i + 1) / increasing_length * walk

        if self.plot_mapping:
            plt.plot(np.arange(0, fine + 1),
                     cq1[self.space, :],
                     label='$id(x) - \psi(x)$')
            plt.title('Domain mapping')
            plt.legend()
            plt.show()

        print('These are the results of the shift epsilon', epsilonT)
        cq1 = cq1.flatten()

        alpha = 1.

        for_mapping = np.stack(
            (xpFine[:, 0] + alpha * func.evaluateCQ1(Nmapping, cq1, xpFine),
             xpFine[:, 1]),
            axis=1)
        self.psi = discrete_mapping.MappingCQ1(NFine, for_mapping)
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()
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()
aBack_ref = func.evaluateDQ0(NFine, aFine_pert, xtFine_pert)

plt.figure("Coefficient")
drawCoefficient_origin(NFine, aFine_ref)

plt.figure("a_perturbed")
drawCoefficient_origin(NFine, aFine_pert)

plt.figure("a_back")
drawCoefficient_origin(NFine, aBack_ref)

# aFine_trans is the transformed perturbed reference coefficient
aFine_trans = np.einsum('tji, t, tkj, t -> tik', psi.Jinv(xtFine), aFine_ref, psi.Jinv(xtFine), psi.detJ(xtFine))

f_pert = np.ones(np.prod(NFine+1))
f_ref = func.evaluateCQ1(NFine, f_pert, xpFine_pert)
f_trans = np.einsum('t, t -> t', f_ref, psi.detJ(xpFine))

#d3sol(NFine,f, 'right hand side NT')
d3sol(NFine, f_trans, 'right hand side T')


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

NCoarseElement = NFine // NWorldCoarse
world = World(NWorldCoarse, NCoarseElement, boundaryConditions)

# Naming of solutions
# ._pert        is a solution in the perturbed domain
# ._trans       is a solution in the reference domain, after transformation