optimizationopenmdao

Constraints or objectives cannot be impacted by the design variables of the problem


I've been expanding my OpenMDAO script to include a sub-iteration. Basically, I have a propeller whose chord and twist distribution is determined by a cruise condition (with collective angle) where efficiency is my objective (with a series of constraints). This has been working wonderfully for a while. The sub-iteration includes an optimization on a hover condition where my design variable is only collective and RPM (different than cruise) but the shape is no longer a design variable. The objective function switches from efficiency to figure of merit. I have checked all my partials and everything is as perfect as I can expect. I have checked totals and they also make sense (The hover sub-iteration is simple and I have validated the numbers by hand). The output of compute_totals is the following:

Compute Totals:
{('volume_constraint_comp.figure_of_merit_hvr', 'omega_hvr'): array([[0.00020326]]),
 ('volume_constraint_comp.figure_of_merit_hvr', 'collective_hvr'):array([[-0.08223257]])}

These numbers make sense when compared to hand calculated total derivatives from partials.

I compared this to check_totals and...

  Full Model: 'figure_of_merit_hvr' wrt 'collective_hvr'

     Forward Magnitude: 0.000000e+00
          Fd Magnitude: 8.224099e-02 (fd:forward)

    Absolute Error (Jfor - Jfd) : 8.224099e-02 *

    Relative Error (Jfor - Jfd) / Jfd : 1.000000e+00 *

    Raw Forward Derivative (Jfor)
    [[-0.]]

    Raw FD Derivative (Jfd)
    [[-0.08224099]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  Full Model: 'figure_of_merit_hvr' wrt 'omega_hvr'

     Forward Magnitude: 0.000000e+00
          Fd Magnitude: 2.032554e-04 (fd:forward)

    Absolute Error (Jfor - Jfd) : 2.032554e-04 *

    Relative Error (Jfor - Jfd) / Jfd : 1.000000e+00 *

    Raw Forward Derivative (Jfor)
    [[-0.]]

    Raw FD Derivative (Jfd)
    [[0.00020326]]

The 'Forward Magnitude' is 0 where it should be the value shown in compute_totals. What would cause this? I have checked the way figure_of_merit_hvr is created:

prob.model.add_subsystem(
    "volume_constraint_comp",
    comp,
    promotes_inputs=[
         "Rtip",
         "radii",
         "chord",
         "disk_area",
         "CT_ff",
         "CT_hvr",
         "CP_hvr",
         "theta_cp",
         "omega_hvr",
         "collective_hvr",
         "speedofsound"],
   promotes_outputs=[
         "volume",
         "blade_area",
         "solidity",
         "ct_over_sigma_ff",
         "ct_over_sigma_hvr",
         "theta_at_75",
         "figure_of_merit_hvr",
         "mach_tip_hvr"])

I have also verified that my N2 is correct

n2 diagram showing the 2 design variables affecting the figure of merit through a BEMT calculation that calculates Ct and Cp which are then fed to the calculation that calculates figure of merit.Figure shows Ct/sigma but FM is there as well.

Obviously, I am not connecting up figure_of_merit_hvr correctly somehow. Any advice on something else I could check?

I have tried several different ways to validate that my derivatives are correct and the objective is set properly. However, something is obviously wrong with the setup. I have printed the details of the case:

 print("Design Variables:")
 for name, options in _no_acoustic_constraint_hvr.model.get_design_vars().items():
 print(f"  {name}: {options}")
 print("\nObjectives:")
 for name, options in _no_acoustic_constraint_hvr.model.get_objectives().items():
 print(f"  {name}: {options}")
 print("\nCompute Totals:")
 totals = p_no_acoustic_constraint_hvr.compute_totals(
 of=['volume_constraint_comp.figure_of_merit_hvr'])
 print(totals)
 p_no_acoustic_constraint_hvr.check_totals()

And everything seems setup correctly. I have also gone through the N2 and hand calculate the derivatives and validated then to what is computed.

I have also browsed through similar problems, which pointed to looking where I have already looked, and validated that the suggestions did not point to a solution.


Solution

  • In this case, I exchanged some more information offline and worked out the solution, but I'll summarize it here for others who may be interested:

    The N2 Diagram shown by the user indicates that there are no derivatives for several components...note the lack of black squares in the upper right quadrants of the components that would typically indicate dependencies of the outputs upon the inputs.

    In this case, Len defined these derivatives using the "Matrix-Free" API in OpenMDAO. That is, they're defined using compute_jacvec_product instead of compute_partials. Furthermore, Len built this method with the anticipation that OpenMDAO would only ever run this problem in reverse mode. In forward mode, these partials derivatives within the component are not computed.

    def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
    
        # There is no forward differencing method for this.
        if mode == 'fwd':
            # No doutputs were provided here
            pass
        # We are going to only run in reverse (or backwards) mode.
        elif mode == 'rev':
            # All dinputs were computed here.
    

    The solution is relatively simple. The user can force OpenMDAO to compute derivatives in reverse mode using

    problem.setup(..., mode='rev')
    

    With this change, the derivatives are correctly computed in the compute_jacvec_product methods and the totals are correct.