Hello everyone,
I am currently running a multi-objective optimization with the IPOPT solver and pyadjoint. My objective function has the following form:
J =j_1 + \alpha j_2 with j_{1/2} the sub-objective function values and alpha the weight associated with the second sub-objective.
I would like to store and retrieve the values of each individual sub-objective j_1 and j_2 throughout the optimization.
I know that I can define some callbacks that are called at every iterations. However, the arguments passed to the method are only the scalar value of the total objective function j and the design variables a… I cannot access the sub-objective values.
Here is a MWE based on the topology optimization example governed by Poisson equation https://dolfin-adjoint-doc.readthedocs.io/en/latest/documentation/poisson-topology/poisson-topology.html
from __future__ import print_function
from dolfin import *
from dolfin_adjoint import *
try:
from pyadjoint import ipopt # noqa: F401
except ImportError:
print("""This example depends on IPOPT and Python ipopt bindings. \
When compiling IPOPT, make sure to link against HSL, as it \
is a necessity for practical problems.""")
raise
parameters["std_out_all_processes"] = False
alpha = Constant(1.0e-8) # weight for subobjective 2
n = 250
mesh = UnitSquareMesh(n, n)
A = FunctionSpace(mesh, "CG", 1) # function space for control
P = FunctionSpace(mesh, "CG", 1) # function space for solution
Vol = Constant(0.4) # volume bound on the control
class WestNorth(SubDomain):
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 source term for the PDE
def forward(a):
"""Solve the forward problem for a given material distribution a(x)."""
print('Solve residual forward way. ')
T = Function(P, name="Temperature")
v = TestFunction(P)
F = inner(grad(v), (a**Constant(5)) * 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
def objective_function(a,annotate=False):
j1 = f * T * dx # sub-objective 1
j2 = inner(grad(a), grad(a)) * dx # sub-objective 2
return assemble(j1 + alpha*j2)
if __name__ == "__main__":
a = interpolate(Vol, A) # initial guess.
T = forward(a) # solve the forward problem once.
# Callback called at each iteration
total_obj_list = []
def eval_cb(j, a):
total_obj_list.append(j)
J = objective_function(a,annotate=True)
m = Control(a)
Jhat = ReducedFunctional(J, m, eval_cb_post=eval_cb)
volume_constraint = UFLInequalityConstraint((Vol - a)*dx, m) # Some constraint
problem = MinimizationProblem(Jhat, bounds=(0.0, 1.0), constraints=volume_constraint)
parameters = {"acceptable_tol": 1.0e-3, "maximum_iterations": 100}
solver = IPOPTSolver(problem, parameters=parameters)
a_opt = solver.solve()
plot(a_opt)
print("The total objective throughout the optimization: ", total_obj_list)
Let me know if I misunderstood something or if you have some suggestions on how to achieve this.
I would also like to update the weight \alpha during the optimization. Do you have some suggestions regarding this topic as well?
Thank you for your time