How to create a Functional graph in dolfin-adjoint?

In the example in the link below, I would like to make an evaluation, a graph, between the objective function value and the iteration number. I made a few attempts but without success. Can anybody help me?

How I transform, using the example of the link, the Functional J in a vector that has all the values of each iteration and can be used in a graph.

Here’s the link for the example:


I managed to generate a code that generates a curve between Functional J and the number of iterations. Now I would like some help to also insert the volume reduction in the graph. @dokken , could you help me?

I believe it could be the generation of a “vector” of the “volume_constraint”, but I couldn’t.

Follow the code below:

from fenics import *
from dolfin_adjoint import *
import ipopt
import matplotlib.pyplot as plt
import numpy as np

parameters["std_out_all_processes"] = False

Vol = Constant(0.4)  # volume bound on the control
p = Constant(5)  # power used in the solid isotropic material
eps = Constant(1.0e-3)  # epsilon used in the solid isotropic material
alpha = Constant(1.0e-8)  # regularisation coefficient in functional

def k(a):
    """Solid isotropic material with penalisation (SIMP) conductivity
  rule, equation (11)."""
    return eps + (1 - eps) * a ** p

n = 250
mesh = UnitSquareMesh(n, n)
A = FunctionSpace(mesh, "CG", 1)  # function space for control
P = FunctionSpace(mesh, "CG", 1)  # function space for solution

class WestNorth(SubDomain):
    """The top and left boundary of the unitsquare, used to enforce the Dirichlet boundary condition."""

    def inside(self, x, on_boundary):
        return (x[0] == 0.0 or x[1] == 1.0) and on_boundary

bc = [DirichletBC(P, 0.0, WestNorth())]
f = interpolate(Constant(1.0e-2), P)  # the volume source term for the PDE

def forward(a):
    """Solve the forward problem for a given material distribution a(x)."""
    T = Function(P, name="Temperature")
    v = TestFunction(P)

    F = inner(grad(v), k(a) * grad(T)) * dx - f * v * dx
    solve(F == 0, T, bc, solver_parameters={"newton_solver": {"absolute_tolerance": 1.0e-7,
                                                              "maximum_iterations": 20}})

    return T

if __name__ == "__main__":
    a = interpolate(Vol, A)  # initial guess.
    T = forward(a)  # solve the forward problem once.
# Now we define the functional, compliance with a weak regularisation
# term on the gradient of the material

    J = assemble(f * T * dx + alpha * inner(grad(a), grad(a)) * dx)
    # Callback called at each iteration
    total_obj_list = []
    def eval_cb(j, a):
    m = Control(a)
    Jhat = ReducedFunctional(J, m, eval_cb_post=eval_cb)

    lb = 0.0
    ub = 1.0
    volume_constraint = UFLInequalityConstraint((Vol - a)*dx, m) # Volume Constraint
    problem = MinimizationProblem(Jhat, bounds=(lb, ub), constraints=volume_constraint)
    parameters = {"acceptable_tol": 1.0e-3, "maximum_iterations": 100}
    solver = IPOPTSolver(problem, parameters=parameters)
    a_opt = solver.solve()
    print("The total objective throughout the optimization: ", total_obj_list)
    compliance = total_obj_list
    maxiter = len(compliance)
    plt.plot(np.arange(1, maxiter+1), compliance)
    plt.xlabel("Number of iterations")
    plt.title("Convergence history", fontsize=16);
    File("output/final_solution.pvd") << a_opt
    xdmf_filename = XDMFFile(MPI.comm_world, "output/final_solution.xdmf")