OpenMDAO / dymos

Open Source Optimization of Dynamic Multidisciplinary Systems
Apache License 2.0
208 stars 66 forks source link

Boundary constraint is shifted when specifying non-zero `ref0` in `add_state()` #737

Closed kanekosh closed 2 years ago

kanekosh commented 2 years ago

Issue Type

Description

The boundary constraint on state variables seems not to work as it should when specifying ref0 in phase.add_state(). Specifically, the state boundary constraint seems to be shifted when ref0 is declared in add_state.

Example

Here, I will try to demonstrate the potential bug using the supersonic minimum-time climb example.

Case 1 Please see here for the full running script.
For this case, I added ref0=10 in phase.add_state('h', ...) line. I believe this slight change in scaling should not change the optimization behavior that much. However, the optimizer fails saying the problem is infeasible (SNOPT exit code 10/14).

Case 2 Please see here for the full running script, and this link for the pyOptSparse output.
For this case, I also modified the final boundary constraint of h to be 19990 <= h <= 20000 (originally it was an equality h=20000). Then optimization converges to the same solution as the original example, where the final altitude is h=20000. Therefore it hit the upper bound of the final boundary constraint of h.
However, pyOptSparse output says it is hitting the lower bound of the boundary constraint:

Constraints (i - inequality, e - equality)
  Index  Name                                                               Type          Lower           Value           Upper    Status  Lagrange Multiplier (N/A)
    175  traj.phases.phase0.final_boundary_constraints.final_value:h           i   1.999000E+01    1.999000E+01    2.000000E+01         l    9.00000E+100

Environment

Operating System: Ubuntu 20.04 Python environment: 3.7.4 Packages: Dymos 1.4.0, OpenMDAO 3.17.0, pyOptSparse 2.6.2.

robfalck commented 2 years ago

Thanks for pointing this out. Definitely something odd going on, looking into it.

On my system the lower/upper for the boundary constraints are unaffected by the state ref0, but the value is unable to be satisfied, offset by the ref0.

robfalck commented 2 years ago

This has to do with OpenMDAO providing an incorrect jacobian for linear constraints due to the interplay between the scaling on the design variable (state h) and the boundary constraint on state h. Please try to make the ref and ref0 the same for both of those, and I think it will work.

We should offer the ability to override the linearity of a constraint (at least to turn a linear constraint into a nonlinear one). An issue is posted for that and we'll address it shortly.

Hopefully either of those two approaches can serve as a workaround until we address the core issue.

kanekosh commented 2 years ago

Thank you for a quick response. Yes, it worked by setting the same ref0 for both add_state and add_boundary_constraint. Actually ref doesn't seem to matter here: setting the same ref0 but different ref also worked.

robfalck commented 2 years ago

Yes we noticed that too. It has to do with ref0 (or adder if you're using scaler/adder instead of ref/ref0). Thanks for bringing this to our attention and the good example cases.

Kenneth-T-Moore commented 2 years ago

It turns out that there is a fundamental problem with linear constraints in pyoptsparse, namely that it assumes that con = A * desvar instead of con = A * desvar + b. This explains why giving the state and the boundary constraint the same adder worked, as it removed the bias.

I've opened an issue on the pyoptsparse repo. In the meantime, we will have to be careful how we use linear constraints (particularly, we'll have to check the ones we use in dymos).