AttributeError: 'numpy.float64' object has no attribute 'block_variable'

Hi all,

I want my force to reach the values I want with the applied displacement. I determine these desired points in the polynomial. So I minimize my function to find the polynomial coefficients that will allow it to pass my target points in the force-displacement graph.
However I am having an error like:
vector = vector._cpp_object
^^^^^^^^^^^^^^^^^^
AttributeError: ‘float’ object has no attribute ‘_cpp_object’

The error starts with minimization.
I have divided the code into pieces and left my minimal example below. Even though I optimize only the first constant in the example, I will later ask it to optimize all constants and many polynomials will be included in my geometry.
I can provide you more information if needed.
Thank you!

Geometry-Boundary-Function Definitions (No error here)

from dolfin import *
from dolfin_adjoint import *
import gmsh
import numpy as np
import matplotlib.pyplot as plt
import meshio
import moola

a = 0.1
t = 1
h = 1
x_dir = 1.5
y_dir = h + a
scale = 1
N = 10
lc = 1/2
max_disp = -0.3
last_point_of_spline = 0.25
Traclist = []
Traclist2 = []
normal = Constant((0, 1, 0))
recu = []
J = 0

multiply = 10

x_valuesA = [0, 0.1, 0.225, last_point_of_spline]
y_valuesA = [0, multiply*0.32, multiply*0.1, multiply*0.15]


desired_response_x = [0, 0.1, 0.225, last_point_of_spline]
desired_response_y = [0, multiply*0.36, multiply*0.02, multiply*0.07]
def createGeometryAndMesh(a, t, lc):
    # Clear all models and create a new one
    gmsh.initialize()
    gmsh.clear()
    gmsh.model.add("t3")

    def geopoints(dx, dy, surf_point):
        P1 = gmsh.model.occ.addPoint(0 + dx, dy, 0, lc)
        P2 = gmsh.model.occ.addPoint(t + dx, dy, 0, lc)
        P3 = gmsh.model.occ.addPoint(dx, h + dy, 0, lc)
        P4 = gmsh.model.occ.addPoint(t + dx, h + dy, 0, lc)

        L1 = gmsh.model.occ.addLine(P1, P2)
        L2 = gmsh.model.occ.addLine(P2, P4)
        L3 = gmsh.model.occ.addLine(P4, P3)
        L4 = gmsh.model.occ.addLine(P3, P1)

        Curve1 = gmsh.model.occ.addCurveLoop([L1, L2, L3, L4])
        Surface1 = gmsh.model.occ.addPlaneSurface([Curve1], 11 + surf_point)

        return Surface1

    # Entity creation operations
    SurfaceA = geopoints(0, 0, 2000)
    gmsh.model.occ.synchronize()
    # One group for each of the unit squares
    pg1 = gmsh.model.addPhysicalGroup(2, [SurfaceA])
    gmsh.model.setPhysicalName(2, pg1, "main_geometry")

    # Set mesh size at all points
    gmsh.model.mesh.setSize(gmsh.model.getEntities(0), lc)
    gmsh.model.occ.synchronize()

    gmsh.option.setNumber("Mesh.MshFileVersion", 2.2)
    gmsh.model.mesh.generate()
    gmsh.write("./Result--" + str(commit) + "geoforstl" + ".geo_unrolled")
    gmsh.write("./Result--" + str(commit) + "geo.msh")
    # colors:
    gmsh.option.setNumber("Geometry.PointNumbers", 1)
    gmsh.option.setColor("Geometry.Points", 255, 165, 0)
    gmsh.option.setColor("General.Text", 255, 255, 255)
    gmsh.option.setColor("Mesh.Points", 255, 0, 0)
    rr, gg, bb, aaa = gmsh.option.getColor("Geometry.Points")
    gmsh.option.setColor("Geometry.Surfaces", rr, gg, bb, aaa)
    gmsh.onelab.set("""[
    {"type":"number",
    "name":"Parameters/Twisting angle",
    "values":[90],
    "min":0,
    "max":120,
    "step":1}]""")
    
    def checkForEvent():
        action = gmsh.onelab.getString("ONELAB/Action")
        if len(action) and action[0] == "check":
            gmsh.onelab.setString("ONELAB/Action", [""])
            createGeometryAndMesh()
            gmsh.graphics.draw()
        return True  

    # Refine the mesh
    gmsh.model.mesh.refine()
  
    gmsh.write("t3.opt");
    gmsh.finalize()

createGeometryAndMesh(a, t, lc)

# Importing mesh from gmsh and defining surface and boundary markers
msh = meshio.read("./Result--" + str(commit)+ "geo.msh")

for key in msh.cell_data_dict["gmsh:physical"].keys():
    if key == "triangle":
        triangle_data = msh.cell_data_dict["gmsh:physical"][key]

for cell in msh.cells:
    if cell.type == "tetra":
        tetra_cells = cell.data
    elif cell.type == "triangle":
        triangle_cells = cell.data

triangle_mesh = meshio.Mesh(points=msh.points,
                            cells=[("triangle", triangle_cells)],
                            cell_data={"name_to_read": [triangle_data]})

meshio.write("./Result--" + str(commit)+ "mesh.xdmf",
             triangle_mesh)
meshio.write("mesh.xdmf", triangle_mesh)



# Optimization options for the form compiler
parameters["form_compiler"]["cpp_optimize"] = True
ffc_options = {"optimize": True,
               "eliminate_zeros": True,
               "precompute_basis_const": True,
               "precompute_ip_const": True}

# Create mesh and define function space
mesh = Mesh()
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
with XDMFFile("mesh.xdmf") as infile:
    infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
with XDMFFile("mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
mf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
print(mesh.topology().dim() - 1)

File("./Result--" + str(commit)+ "MSH.pvd") << mesh
File("./Result--"+ str(commit) + "/MSH.pvd") << mesh
File("./Result--"+ str(commit) + "MSH2.pvd") << mf
File("./Result--" + str(commit)+ "/MSH2" ".pvd") << mf
boundary_markers = MeshFunction('size_t', mesh, mesh.topology().dim() - 1, 0)

class Boundary(SubDomain):
    def inside(self, x, on_boundary):
        return on_boundary and near(x[1], 0.0)

b_c = Boundary()
b_c.mark(boundary_markers, 3)
File("./Result--" + str(commit)+  "MSH3.pvd") << boundary_markers

# Compiling subdomains
ds = Measure('ds', domain=mesh, subdomain_data=boundary_markers)
dx_A = Measure("dx", domain=mesh, subdomain_data=mf, subdomain_id=1)
V = VectorFunctionSpace(mesh, "CG", 2)  # Lagrange
W_tensor = TensorFunctionSpace(mesh, "CG", 2)
V_tensor = TensorFunctionSpace(mesh, "CG", 2)
W = FunctionSpace(mesh, "R", 0)

def boundary_bot(x, on_boundary):
    tol = 1E-7
    return on_boundary and near(x[1], 0, tol)

bc_bot = DirichletBC(V, [0, 0, 0], boundary_bot)

def boundary_top(x, on_boundary):
    tol = 1E-7
    return on_boundary and near(x[1], (h), tol)


disp = Constant([0.0, max_disp, 0.0])
bc_top = DirichletBC(V, disp, boundary_top)
bcs = [bc_bot, bc_top]

# Define functions
du = TrialFunction(V)  # Incremental displacement
v = TestFunction(V)  # Test function
u = Function(V)  # Displacement from previous iteration
u0 = Function(V)  # Initial displacement
boundary_subdomains = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
boundary_subdomains.set_all(0)
AutoSubDomain(boundary_top).mark(boundary_subdomains, 1)
dss = ds(subdomain_data=boundary_subdomains)

Calculation starts here
Error is also here

def kinematics(u):
    # Kinematics
    d = u.geometric_dimension()  # Space dimension
    I = Identity(d)  # Identity tensor
    F = variable(I + grad(u))  # Deformation gradient
    C = F.T * F  # Right Cauchy-Green tensor 
    # Invariants of deformation tensors
    Ic = tr(C)
    J = det(F)
    E = (C-I)/2

    return Ic, J, F, C, I, E


def poly(x_values, y_values):
    # Fit a cubic polynomial (degree 3) to the given data points
    coeffs = np.polyfit(x_values, y_values, len(x_values) -1)
    print(coeffs)
    # print(coeffs.convert().coef)
    if len(coeffs) == 4:
        c1, c2, c3, c4= coeffs
    if len(coeffs) == 3:
        c1 = 0
        c2 = coeffs[0]
        c3 = coeffs[1]
        c4 = coeffs[2]
    if len(coeffs) == 2:
        c1 = 0
        c2 = 0
        c3 = coeffs[0]
        c4 = coeffs[1]
    
    return c1, c2, c3, c4

for i in np.linspace(0, max_disp, N):
    u.assign(u0)
    disp.assign(Constant([0.0, i, 0.0]))
    print("i", i)
    Ic, J, F, C, I, E = kinematics(u)
    # Define the custom strain energy density function
    def psi(x_values, y_values, num):
        aA, bA, cA, dA= poly(x_values, y_values) 
        aA = interpolate(Expression(("a_constant"), a_constant=aA, degree=1), W) # W = FunctionSpace(mesh, "R", 0)
        Ic, J, F, C, I, E = kinematics(u)
        psi = (aA((0,h,0))/ 4) * (E[1, 1]) ** 4 - (bA / 3) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] + 1E+4 * E[0,0] **2 + 1E+4 * E[0,1] **2
        stress = diff(psi, F)
        return psi, stress, aA, bA, cA, dA 
    
    psiA, stressA, aA, bA, cA, dA  = psi(x_valuesA, y_valuesA, 1)   
    a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y) 
    target_y = (a_d) * (i) ** 3 + (b_d) * (i) ** 2 + (c_d ) * (i) + d_d 
    psi = psiA 
    PiA = psiA * dx_A
    Pi =  PiA
    dPiA = derivative(PiA, u, v)
    dPi = derivative(Pi, u, v)
    stress = stressA 
    solve(dPi == 0, u, bcs, 
          form_compiler_parameters=ffc_options)
    print("u", u((0, h, 0))[1])
    traction = dot(stress, normal)
    stored_traction = assemble((traction[1]) * dss(1))  # Traction in the y-direction
    Traclist.append(stored_traction)
    """
    ERROR STARTS HERE
    """
    J = (target_y - stored_traction) ** 2
    # aA, bA, cA, dA = Function(W), Function(W), Function(W),Function(W)
    # aA = interpolate(Expression(('a_constant'), a_constant=aA, degree=1), W)
    # bA = interpolate(Expression(('b_constant'), b_constant=bA, degree=1), W)
    # cA = interpolate(Expression(('c_constant'), c_constant=cA, degree=1), W)
    # dA = interpolate(Expression(('d_constant'), d_constant=dA, degree=1), W)
    control = Control(aA) # , Control(bA), Control(cA), Control(dA)

    rf = ReducedFunctional(J, control)
    problem = MoolaOptimizationProblem(rf)

    n_moola = moola.DolfinPrimalVector(aA)
    # d_moola = moola.DolfinPrimalVector(bA)
    # k_moola = moola.DolfinPrimalVector(cA)
    # l_moola = moola.DolfinPrimalVector(dA)
    m_moola = moola.DolfinPrimalVectorSet([n_moola]) # , d_moola, k_moola, l_moola
    solver = moola.BFGS(problem, m_moola, options={'jtol': 0,
                                                'gtol': 1e-9,
                                                'Hinit': "default",
                                                'maxiter': 10,
                                                'mem_lim': 10})    
    sol = solver.solve()
    m_opt = sol['control'].data
    print(m_opt)

Hi Hailey,

I’m having trouble understanding what exactly is going wrong in your code. Could you directly copy the error trace that you encounter and specify in which exact line of your code it pops up?
Right now the bar is pretty high for people to help you, since they’d need to run your code themselves. Supplying the exact error trace and line in which it happens lowers that bar.

2 Likes

Hello @spcvanschie , thank you a lot for your answer!
I mentioned in the code where the error starts in

sol = solver.solve()

and also here is the full error:

No Jacobian form specified for nonlinear variational problem.
Differentiating residual form F to obtain Jacobian J = F'.
Solving nonlinear variational problem.
  Newton iteration 0: r (abs) = 7.678e-29 (tol = 1.000e-10) r (rel) = 1.000e+00 (tol = 1.000e-09)
  Newton solver finished in 0 iterations and 0 linear solver iterations.
Traceback (most recent call last):
  File "/home/hailey/trial_opt.py", line 295, in <module>
    sol = solver.solve()
          ^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/tape.py", line 46, in wrapper
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/moola/algorithms/bfgs.py", line 172, in solve
    J = objective(xk)
        ^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/tape.py", line 46, in wrapper
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/optimization/moola_problem.py", line 50, in __call__
    self.latest_eval_eval.append(rf(x.data))
                                 ^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/tape.py", line 46, in wrapper
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/reduced_functional.py", line 137, in __call__
    func_value = self.scale * self.functional.block_variable.checkpoint
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'numpy.float64' object has no attribute 'block_variable'

@spcvanschie please let me know if I need to add more details

So a disclaimer here is that I do not have much experience with Dolfin-adjoint. Nonetheless, I see an issue by comparing your code with this example case. Your functional J seems to be incorrect. Right now you assemble a component of the traction form, then subtract target_y (which is a Numpy float) and then square that difference. I don’t know what exactly your goal functional is, but in any case it is currently defined as a Numpy float64, which is probably what’s causing your error to be thrown. You should formulate your functional J as a Dolfin object, so that Dolfin-adjoint can actually interpret it. You could try defining J as a Dolfin Expression.

Thank you so much! I tried that but it did not work. @spcvanschie @nate

This is indeed one issue. DOLFIN-adjoint relies on operator overloading, and has not overloaded all of numpy. Thus, you should wrap

as
J = (stored_traction- AdjFloat(target_y)) ** 2 to ensure that the numpy float is overloaded

Secondly, your function u is not dependent of aA, as you are evaluating aA at a specific point, which only has very specific support in dolfin-adjoint:

        psi = (aA(np.array([0,h,0]))/ 4) * (E[1, 1]) ** 4 - (bA / 3.) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] + 1.E+4 * E[0,0] **2 + 1.E+4 * E[0,1] **2

i.e. wrapping the eval in a numpy array allows annotation.

As your example is far from minimal, I’m having issues with getting further in debugging your remaining issues. Please try to simplify your example.
I’ve for instance hit a general issue with older versions of ufl (Sanitise unicode input by dham · Pull Request #111 · FEniCS/ufl · GitHub) where non-linear solver output is not sanitized, so I cannot visualize the computational graph to see what is not overloaded correctly.

I believe that having a point evaluation of a function as the control variable doesn’t make much sense to me, as this would just be a scalar value (i.e a constant as control variable).

2 Likes

@dokken Thank you so much!
The reason I use dolfin_adjoint is that I will add hyperelastic materials later and my aim is to find the constants of the material that actually behaves like a polynomial. As you said, my Control value does not depend on u, but I do not know how I can achieve my goal by finding a value that depends on u.

I am still having errors and I minimized my code below:

def kinematics(u):
    # Kinematics
    d = u.geometric_dimension()  # Space dimension
    I = Identity(d)  # Identity tensor
    F = variable(I + grad(u))  # Deformation gradient
    C = F.T * F  # Right Cauchy-Green tensor 
    # Invariants of deformation tensors
    Ic = tr(C)
    J = det(F)
    E = (C-I)/2

    return Ic, J, F, C, I, E


def poly(x_values, y_values):
    # Fit a cubic polynomial (degree 3) to the given data points
    coeffs = np.polyfit(x_values, y_values, len(x_values) -1)
    c1, c2, c3, c4= coeffs
    return c1, c2, c3, c4

disp.assign(Constant([0.0, max_disp, 0.0]))
Ic, J, F, C, I, E = kinematics(u)
# Define the custom strain energy density function
aA, bA, cA, dA= poly(x_valuesA, y_valuesA) 
aA = project(Expression(("a_constant"), a_constant=aA, degree=1), W) # W = FunctionSpace(mesh, "R", 0)
psi = (aA(np.array([0,h,0]))/ 4) * (E[1, 1]) ** 4 - (bA / 3.) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] + 1.E+4 * E[0,0] **2 + 1.E+4 * E[0,1] **2
stress = diff(psi, F)
traction = dot(stress, normal)
stored_traction = assemble((traction[1]) * dss(1)) 
Traclist.append(stored_traction)

"""
ERROR STARTS HERE
"""
target_y = 1e-2
J = (stored_traction- AdjFloat(target_y)) ** 2
control = Control(aA)

rf = ReducedFunctional(J, control)
problem = MoolaOptimizationProblem(rf)

n_moola = moola.DolfinPrimalVector(aA)
m_moola = moola.DolfinPrimalVectorSet([n_moola]) # , d_moola, k_moola, l_moola
solver = moola.BFGS(problem, m_moola, options={'jtol': 0,
                                            'gtol': 1e-9,
                                            'Hinit': "default",
                                            'maxiter': 10,
                                            'mem_lim': 10})    
sol = solver.solve()
m_opt = sol['control'].data
print(m_opt)
   

Please let me know if it is not clear enough.

Hello @dokken, I changed my code as below and now I do not have the error. However the result is not correct, it is giving me the initial guess back.

def kinematics(u):
    # Kinematics
    d = u.geometric_dimension()  # Space dimension
    I = Identity(d)  # Identity tensor
    F = variable(I + grad(u))  # Deformation gradient
    C = F.T * F  # Right Cauchy-Green tensor 
    # Invariants of deformation tensors
    Ic = tr(C)
    J = det(F)
    E = (C-I)/2

    return Ic, J, F, C, I, E


def poly(x_values, y_values):
    # Fit a cubic polynomial (degree 3) to the given data points
    coeffs = np.polyfit(x_values, y_values, len(x_values) -1)
    if len(coeffs) == 4:
        c1, c2, c3, c4= coeffs
    return c1, c2, c3, c4

for i in np.linspace(0, -0.25, 6):
    disp.assign(Constant([0.0, i, 0.0]))
    Ic, J, F, C, I, E = kinematics(u)

    def psi(x_values, y_values, num):
        aA, bA, cA, dA= poly(x_values, y_values) 
        psi = (aA / 4) * (E[1, 1]) ** 4 - (bA / 3) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] 
        stress = diff(psi, F)
        return psi, stress, aA, bA, cA, dA 

    psiA, stress, aA, bA, cA, dA  = psi(x_valuesA, y_valuesA, 1)   
    a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y) 
    target_y = [0,1,1,2,3,4]

    traction = dot(stress, normal)
    stored_traction = assemble((traction[1]) * dss(1))
    Traclist.append(stored_traction)

    
J = 0  
def objective(J, i):
    for i in range(len(Traclist)):
        J += (Traclist[i] - AdjFloat(target_y[i])) ** 2  
        print(J)
    return J

J = objective(J, i)

control = Control(Constant(aA))
control2 = Control(Constant(bA)) 
control3 = Control(Constant(cA)) 
control4 = Control(Constant(dA)) 
rf = ReducedFunctional(J, [control, control2, control3, control4])
# const = UFLInequalityConstraint(dJdx - 1e-5, control)

problem = MinimizationProblem(rf)  # , constraints=const
parameters_ipopt = {"acceptable_tol": 1e-10, "maximum_iterations": 10}
solver = IPOPTSolver(problem, parameters=parameters_ipopt)
f_opt = solver.solve()
print("f_opt", f_opt[1]((0,h,0)))

I can see two possible issues in this block of code:

for i in np.linspace(0, -0.25, 6):
    disp.assign(Constant([0.0, i, 0.0]))
    Ic, J, F, C, I, E = kinematics(u)

    def psi(x_values, y_values, num):
        aA, bA, cA, dA= poly(x_values, y_values) 
        psi = (aA / 4) * (E[1, 1]) ** 4 - (bA / 3) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] 
        stress = diff(psi, F)
        return psi, stress, aA, bA, cA, dA 

    psiA, stress, aA, bA, cA, dA  = psi(x_valuesA, y_valuesA, 1)   
    a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y) 
    target_y = [0,1,1,2,3,4]

    traction = dot(stress, normal)
    stored_traction = assemble((traction[1]) * dss(1))
    Traclist.append(stored_traction)

    
J = 0  
def objective(J, i):
    for i in range(len(Traclist)):
        J += (Traclist[i] - AdjFloat(target_y[i])) ** 2  
        print(J)
    return J

J = objective(J, i)

Both are Python issues and have nothing to do with Dolfin:

  • All lines after J = 0 are not indented in the same way as the preceding lines, and so are not a part of your for loop.
  • Within your function definition objective() you use i both as an input argument and as the argument of the for loop.

Thanks! I changed that and I am still having the same error. @spcvanschie

The issue is that you are defining the control as a constant at the end, you should define them as constants before sending then into the variational form, i.e

    def psi(x_values, y_values, num):
        aA, bA, cA, dA= poly(x_values, y_values) 
        aA = Constant(aA)
        bA = Constant(bA)
        cA = Constant(cA)
        dA = Constant(dA)
        psi = (aA / 4) * (E[1, 1]) ** 4 - (bA / 3) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] 
        stress = diff(psi, F)
        return psi, stress, aA, bA, cA, dA 

    psiA, stress, aA, bA, cA, dA  = psi(x_valuesA, y_valuesA, 1)   
    a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y) 
    target_y = [0,1,1,2,3,4]

    traction = dot(stress, normal)
    stored_traction = assemble((traction[1]) * dss(1))
    Traclist.append(stored_traction)

    
J = 0  
def objective(J, i):
    for i in range(len(Traclist)):
        J += (Traclist[i] - AdjFloat(target_y[i])) ** 2  
        print(J)
    return J

J = objective(J, i)

control = Control(aA)
control2 = Control(bA) 
control3 = Control(cA) 
control4 = Control(dA) 
rf = ReducedFunctional(J, [control, control2, control3, control4])

1 Like

thank you so much! @dokken But then it gives me this error:

  File "/home/hailey/Desktop/hailey/research/datageneration/trial_opt.py", line 310, in <module>
    f_opt = solver.solve()
            ^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/optimization/ipopt_solver.py", line 199, in solve
    results = self.ipopt_problem.solve(guess)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "cyipopt/cython/ipopt_wrapper.pyx", line 642, in ipopt_wrapper.Problem.solve
  File "cyipopt/cython/ipopt_wrapper.pyx", line 676, in ipopt_wrapper.objective_cb
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/reduced_functional_numpy.py", line 36, in __call__
    return self.rf.__call__(self.set_local(m_copies, m_array))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/tape.py", line 46, in wrapper
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/hailey/miniconda3/envs/fenicsproject/lib/python3.11/site-packages/pyadjoint/reduced_functional.py", line 137, in __call__
    func_value = self.scale * self.functional.block_variable.checkpoint
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Sum' object has no attribute 'block_variable'

Please make a reproducible example, as the code I presented you ran on my system.

1 Like

When I change the location of J = 0 (I showed in the code below), I am having the error.
Also, I expect my optimized coefficients to change but they still the same after the optimization.
Thanks!
Here I shared it @dokken

def poly(x_values, y_values):
    # Fit a cubic polynomial (degree 3) to the given data points
    coeffs = np.polyfit(x_values, y_values, len(x_values) -1)
    c1, c2, c3, c4= coeffs

    return c1, c2, c3, c4

J = 0 # it does not work if I move it here
a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y)
for i in np.linspace(0, max_disp, N):
    disp.assign(Constant([0.0, i, 0.0]))
    Ic, J, F, C, I, E = kinematics(u)
    # Define the custom strain energy density function
    def psi(x_values, y_values, num):
            aA, bA, cA, dA= poly(x_values, y_values) 
            aA = Constant(aA)
            bA = Constant(bA)
            cA = Constant(cA)
            dA = Constant(dA)
            psi = (aA / 4) * (E[1, 1]) ** 4 - (bA / 3) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] 
            stress = diff(psi, F)
            return psi, stress, aA, bA, cA, dA 

    psiA, stress, aA, bA, cA, dA  = psi(x_valuesA, y_valuesA, 1)   
    target_y = (a_d) * (-i) ** 3 + (b_d) * (-i) ** 2 + (c_d ) * (-i) + d_d 
    traction = dot(stress, normal)
    stored_traction = assemble((traction[1]) * dss(1))
    Traclist.append(stored_traction)
    target_y_list.append(target_y)

# J = 0 # it works when J=0 is here
def objective(J):
    for k in range(len(Traclist)):
        J += (Traclist[k] - AdjFloat(target_y_list[k])) ** 2  
        print(J)
    return J

J = objective(J)
control = Control(aA)
control2 = Control(bA) 
control3 = Control(cA) 
control4 = Control(dA) 
rf = ReducedFunctional(J, [control, control2, control3, control4])
problem = MinimizationProblem(rf)  
parameters_ipopt = {"acceptable_tol": 1e-10, "maximum_iterations": 10}
solver = IPOPTSolver(problem, parameters=parameters_ipopt)
f_opt = solver.solve()
print("f_opt a", f_opt[0]((0,h,0)))
print("f_opt b", f_opt[1]((0,h,0)))
print("f_opt c", f_opt[2]((0,h,0)))
print("f_opt d", f_opt[3]((0,h,0)))

When I change the location of J = 0 (I showed in the code below), I am having the error.

Well yes. That makes sense, since you define another variable with the name J four lines further down:

J = 0 # it does not work if I move it here
a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y)
for i in np.linspace(0, max_disp, N):
    disp.assign(Constant([0.0, i, 0.0]))
    Ic, J, F, C, I, E = kinematics(u)

Also, I expect my optimized coefficients to change but they still the same after the optimization.

What initial values are you using? Did you already try using a different set of initial values to see whether those are changed in any way by the optimization process?

1 Like

Thanks! @spcvanschie Yes you are right, I totally missed this!
I tried with different initial values, with the code below, I am getting the results as:
image

I think it is only optimizing the last control value.

x_spline = np.linspace(0, 0.25, N, endpoint=True)  
y_spline = f_opt[0]((0,h,0)) * x_spline ** 3 + f_opt[1]((0,h,0)) * x_spline ** 2 + f_opt[2]((0,h,0)) * x_spline + f_opt[3]((0,h,0))
y_spline_target = a_d * x_spline ** 3 + b_d * x_spline ** 2 + c_d * x_spline + d_d
# Plot the data points and the spline
plt.plot(x_spline, y_spline, 'o-', label='Optimized Curve')
plt.plot(x_spline, y_spline_target, label='Target Curve')
aA, bA, cA, dA= poly(x_valuesA, y_valuesA) 
y_spline_initial_guess = aA * x_spline ** 3 + bA * x_spline ** 2 + cA * x_spline + dA
plt.plot(x_spline, y_spline_initial_guess, label='Initial Guess Curve')
plt.legend()
plt.grid(True)
plt.xlabel("Displacement (mm)")
plt.ylabel("Force (N)")
plt.title("Force-Displacement Curve")

it is only changing the 1 control value. @dokken @spcvanschie

As stated above in: AttributeError: 'numpy.float64' object has no attribute 'block_variable' - #17 by spcvanschie
you are overwriting J inside your loop

I fixed it @dokken, now it is working but the result is just a shifted version of the initial guess. So it is only optimizing the Control(dA). Thanks!

objective_J = 0
a_d, b_d, c_d, d_d = poly(desired_response_x, desired_response_y)
for i in np.linspace(0, max_disp, N):
    u.assign(u0)
    disp.assign(Constant([0.0, i, 0.0]))
    Ic, J, F, C, I, E = kinematics(u)
    def psi(x_values, y_values, num):
        aAA, bAA, cAA, dAA = poly(x_values, y_values) 
        aA = Constant(aAA) 
        bA = Constant(bAA)
        cA = Constant(cAA)
        dA = Constant(dAA)
        psi = (aA/ 4) * (E[1, 1]) ** 4 - (bA/ 3) * (E[1, 1]) ** 3 + (cA / 2) * (E[1, 1]) ** 2 - dA * E[1, 1] ** 1
        stress = diff(psi, F)
        return psi, stress, aA, bA, cA, dA 
    
    psiA, stressA, aA, bA, cA, dA  = psi(x_valuesA, y_valuesA, 1)    
    target_y = (a_d) * (-i) ** 3 + (b_d) * (-i) ** 2 + (c_d ) * (-i) + d_d 
    stress = stressA 
    solve(dPi == 0, u, bcs, 
          form_compiler_parameters=ffc_options)
    print("u", u((0, h, 0))[1])
    traction = dot(stress, normal)
    stored_traction = assemble((traction[1]) * dss(1))  # Traction in the y-direction
    Traclist.append(stored_traction)
    target_y_list.append(target_y)
    
    objective_J +=  (stored_traction - AdjFloat(target_y)) ** 2 
    
control = [Control(aA), Control(bA), Control(cA), Control(dA)]

rf = ReducedFunctional(objective_J, control)
problem = MinimizationProblem(rf)  # , constraints=const
parameters_ipopt = {"acceptable_tol": 1e-10, "maximum_iterations": 10}
solver = IPOPTSolver(problem, parameters=parameters_ipopt)
f_opt = solver.solve()
print("f_opt a", f_opt[0]((0,h,0)))
print("f_opt b", f_opt[1]((0,h,0)))
print("f_opt c", f_opt[2]((0,h,0)))
print("f_opt d", f_opt[3]((0,h,0)))