Optimal Control Poisson Equation on Subdomain

Hey guys,

I’m a little bit frustrated. I have following code which solves an optimal control problem with the poisson equation:

from dolfin import *
from dolfin_adjoint import *
import moola
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

set_log_level(LogLevel.ERROR)

# Speicher für Ergebnisse
results = []

# Verschiedene Gittergrößen
for n in [32, 64, 128, 256]:
    print(f"Löse für n = {n} ...")
    
    mesh = UnitSquareMesh(n, n)

    # Lokale Verfeinerung
   # cf = MeshFunction("bool", mesh, mesh.geometric_dimension())
   # subdomain = CompiledSubDomain("std::abs(x[0]-0.5) < 0.25 && std::abs(x[1]-0.5) < 0.25")
   # subdomain.mark(cf, True)
   # mesh = refine(mesh, cf)

    # Funktionräume
    V = FunctionSpace(mesh, "CG", 1)
    W = FunctionSpace(mesh, "DG", 0)

    f = Function(W, name="Control")
    u = Function(V, name="State")
    v = TestFunction(V)

    # Zustandsgleichung
    F = (inner(grad(u), grad(v)) - f * v) * dx
    bc = DirichletBC(V, 0.0, "on_boundary")
    solve(F == 0, u, bc)

    # Steuerung & Ziel
    x = SpatialCoordinate(mesh)
    w = Expression("sin(pi*x[0])*sin(pi*x[1])", degree=3)
    d = 1 / (2 * pi ** 2)
    d = Expression("d*w", d=d, w=w, degree=3)

    alpha = Constant(1e-6)
    J = assemble((0.5 * inner(u - d, u - d)) * dx + alpha / 2 * f ** 2 * dx)
    control = Control(f)
    
    # reduziertes Zielfunktional
    rf = ReducedFunctional(J, Control(f))

    # Optimierungsproblem lösen mit CG Verfahren
    problem = MoolaOptimizationProblem(rf)
    f_moola = moola.DolfinPrimalVector(f)
    solver = moola.NewtonCG(problem, f_moola, options={
        'gtol': 1e-9,
        'maxiter': 20,
        'display': 0,
        'ncg_hesstol': 0
    })

    sol = solver.solve()
    f_opt = sol["control"].data
   
    # Definition der analytischen Lösung um die Fehler zu berechnen
    f_analytic = Expression("1/(1+alpha*4*pow(pi, 4))*w", w=w, alpha=alpha, degree=3)
    u_analytic = Expression("1/(2*pow(pi, 2))*f", f=f_analytic, degree=3)


    f_analytic_v = interpolate(f_analytic, W)
    f.assign(f_opt)

    # Zustand mit optimierter Steuerung berechnen
    solve(F == 0, u, bc)

    # Fehlerberechnung
    control_error = errornorm(f_analytic, f_opt)
    state_error = errornorm(u_analytic, u)
    h_min = mesh.hmin()

    # Ergebnis speichern
    results.append((n, h_min, state_error, control_error))

# Datenframe erstellen
df = pd.DataFrame(results, columns=["n", "h_min", "state_error", "control_error"])

# Konvergenzraten berechnen
def convergence_rate(errors, hs):
    return [None] + [np.log(errors[i-1]/errors[i]) / np.log(hs[i-1]/hs[i]) for i in range(1, len(errors))]

df["convergence_rate_state"] = convergence_rate(df["state_error"], df["h_min"])
df["convergence_rate_control"] = convergence_rate(df["control_error"], df["h_min"])

# Tabelle anzeigen
print("Konvergenztabelle:")
print(df.to_string(index=False, float_format="%.2e"))

# Plot
plt.figure(figsize=(8, 6))
plt.loglog(df["h_min"], df["state_error"], "o-", label="State-Fehler")
plt.loglog(df["h_min"], df["control_error"], "s--", label="Control-Fehler")
plt.loglog(df["h_min"], df["state_error"].iloc[0]*(df["h_min"]/df["h_min"].iloc[0])**2, "k:", label="O(h²)")
plt.loglog(df["h_min"], df["control_error"].iloc[0]*(df["h_min"]/df["h_min"].iloc[0])**1, "k-.", label="O(h)")
plt.xlabel(r"$h_{\min}$")
plt.ylabel("Fehler")
plt.title("Konvergenzverhalten (Log-Log)")
plt.grid(True, which="both", ls=":")
plt.legend()
plt.tight_layout()
plt.show()



plot(f, title="Optimale Steuerung (f)")

plt.show()


This works fine. But now I am trying to constrain my control on a subdomain, so it only works there and is == 0 outside of the subdomain. I tried to update my code with this class:

from dolfin import *
from dolfin_adjoint import *
import moola
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

set_log_level(LogLevel.ERROR)

# Speicher für Ergebnisse
results = []

# Verschiedene Gittergrößen
for n in [32, 64, 128, 256]:
    print(f"Löse für n = {n} ...")
    
    mesh = UnitSquareMesh(n, n)
    class ControlRegion(SubDomain):
        def inside(self, x, on_boundary):
            return (0.25 < x[0] < 0.75) and (0.25 < x[1] < 0.75)

    subdomain_markers = MeshFunction("size_t", mesh, mesh.topology().dim())
    subdomain_markers.set_all(0)
    omega = ControlRegion()
    omega.mark(subdomain_markers, 1)
    dx_sub = Measure("dx", domain=mesh, subdomain_data=subdomain_markers)
    plot(subdomain_markers)
    plt.title("Control Region (omega)")
    plt.show()

    # Lokale Verfeinerung
   # cf = MeshFunction("bool", mesh, mesh.geometric_dimension())
   # subdomain = CompiledSubDomain("std::abs(x[0]-0.5) < 0.25 && std::abs(x[1]-0.5) < 0.25")
   # subdomain.mark(cf, True)
   # mesh = refine(mesh, cf)

    # Funktionräume
    V = FunctionSpace(mesh, "CG", 1)
    W = FunctionSpace(mesh, "DG", 0)

    f = Function(W, name="Control")
    u = Function(V, name="State")
    v = TestFunction(V)

    # Zustandsgleichung
    F = (inner(grad(u), grad(v)) - f * v) * dx
    bc = DirichletBC(V, 0.0, "on_boundary")
    solve(F == 0, u, bc)

    # Steuerung & Ziel
    x = SpatialCoordinate(mesh)
    w = Expression("sin(pi*x[0])*sin(pi*x[1])", degree=3)
    d = 1 / (2 * pi ** 2)
    d = Expression("d*w", d=d, w=w, degree=3)

    alpha = Constant(1e-6)
    J = 0.5 * inner(u - d, u - d) * dx + alpha / 2 * f**2 * dx_sub(1)

    control = Control(f)
    
    # reduziertes Zielfunktional
    rf = ReducedFunctional(J, Control(f))

    # Optimierungsproblem lösen mit CG Verfahren
    problem = MoolaOptimizationProblem(rf)
    f_moola = moola.DolfinPrimalVector(f)
    solver = moola.NewtonCG(problem, f_moola, options={
        'gtol': 1e-9,
        'maxiter': 20,
        'display': 0,
        'ncg_hesstol': 0
    })

    sol = solver.solve()
    f_opt = sol["control"].data
   
    # Definition der analytischen Lösung um die Fehler zu berechnen
    f_analytic = Expression("1/(1+alpha*4*pow(pi, 4))*w", w=w, alpha=alpha, degree=3)
    u_analytic = Expression("1/(2*pow(pi, 2))*f", f=f_analytic, degree=3)


    f_analytic_v = interpolate(f_analytic, W)
    f.assign(f_opt)

    # Zustand mit optimierter Steuerung berechnen
    solve(F == 0, u, bc)

    # Fehlerberechnung
    control_error = errornorm(f_analytic, f_opt)
    state_error = errornorm(u_analytic, u)
    h_min = mesh.hmin()

    # Ergebnis speichern
    results.append((n, h_min, state_error, control_error))

# Datenframe erstellen
df = pd.DataFrame(results, columns=["n", "h_min", "state_error", "control_error"])

# Konvergenzraten berechnen
def convergence_rate(errors, hs):
    return [None] + [np.log(errors[i-1]/errors[i]) / np.log(hs[i-1]/hs[i]) for i in range(1, len(errors))]

df["convergence_rate_state"] = convergence_rate(df["state_error"], df["h_min"])
df["convergence_rate_control"] = convergence_rate(df["control_error"], df["h_min"])

# Tabelle anzeigen
print("Konvergenztabelle:")
print(df.to_string(index=False, float_format="%.2e"))

# Plot
plt.figure(figsize=(8, 6))
plt.loglog(df["h_min"], df["state_error"], "o-", label="State-Fehler")
plt.loglog(df["h_min"], df["control_error"], "s--", label="Control-Fehler")
plt.loglog(df["h_min"], df["state_error"].iloc[0]*(df["h_min"]/df["h_min"].iloc[0])**2, "k:", label="O(h²)")
plt.loglog(df["h_min"], df["control_error"].iloc[0]*(df["h_min"]/df["h_min"].iloc[0])**1, "k-.", label="O(h)")
plt.xlabel(r"$h_{\min}$")
plt.ylabel("Fehler")
plt.title("Konvergenzverhalten (Log-Log)")
plt.grid(True, which="both", ls=":")
plt.legend()
plt.tight_layout()
plt.show()



plot(f, title="Optimale Steuerung (f)")

plt.show()

The plot of the subdomain markers shows exactly what I want but I can’t get it to run through the optimization.
Does somebody maybe have a similiar problem and can assisst me?
Thanks in advance