Skip to content

How should calculate_variable_from_constraint handle bounds? #3985

Description

@dallan-keylogic

Summary

The function calculate_variable_from_constraint presently ignores bounds. "Small" bound violations frequently occur with solve_strongly_connected_components, which leads to an enormous amount of warnings in the solver logs. Should the behavior of calculate_variable_from_constraint be modified to clip variables to their bounds if doing so doesn't result in a "large" residual?

Rationale

When assisting a collaborator in using PrOMMiS, he mentioned that he was getting a ton of warnings. I had to show him an output log from a PrOMMiS example:

2026-05-26 15:11:43 [INFO] idaes.__main__: Initializing fs.solex_rougher_scrub
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].conc_mass_comp[Al]' to a
numeric value `-8.813627952784652e-08` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].conc_mol_comp[Al]' to a
numeric value `-3.266472030144476e-12` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].flow_mol_comp[Al]' to a
numeric value `-3.266484305264328e-13` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].conc_mass_comp[Ca]' to a
numeric value `-1.5760625470793493e-08` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].conc_mol_comp[Ca]' to a
numeric value `-3.9324758829293394e-13` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].flow_mol_comp[Ca]' to a
numeric value `-3.9324880157787234e-14` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].conc_mass_comp[Ce]' to a
numeric value `-3.6579450075327737e-07` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].conc_mol_comp[Ce]' to a
numeric value `-2.6106543788949036e-12` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
WARNING (W1002): Setting Var
'fs.solex_rougher_scrub.mscontactor.aqueous[0.0,1].flow_mol_comp[Ce]' to a
numeric value `-2.610654747936536e-13` outside the bounds (1e-30, None).
    See also https://pyomo.readthedocs.io/en/stable/errors.html#w1002
2026-05-26 15:11:43 [INFO] idaes.init.fs.solex_rougher_scrub.mscontactor: Stream Initialization Completed.
2026-05-26 15:11:43 [INFO] idaes.init.fs.solex_rougher_scrub.mscontactor: Initialization Completed, optimal - <undefined>

and tell him to ignore all the warnings about setting variables to values outside of their bounds. It's problematic emit dozens of lines of warning messages for something that is typically not a problem, and it's also problematic that experienced users of IDAES+ ecosystem tools have learned to ignore warning messages as a matter of course.

We get these messages as a result of solve_strongly_connected_components calling calculate_variable_from_constraint, which does not take bounds into account.

Description

A simple solution would be for calculate_variable_from_constraint to clip variables back to their bounds if Newton's method terminates with a value outside of their bounds. If the constraint residual tolerance remains satisfied, the function can return without emitting any warnings. If the constraint residual tolerance is not satisfied, we can return to Newton's method. If the Newton step calculated would take the variable outside of its bounds again, then we can either reset to the value outside of its bounds that had a sufficiently small constraint residual or emit an exception.

If this method is deemed unsuitable for Pyomo, we could knock off calculate_variable_from_constraint in IDAES, modify it to use the above algorithm, and pass it to solve_strongly_connected_components as a 1x1 solver. Alternatively, we could capture all Pyomo output in the BlockTriangularizationInitializer and filter out those spurious warnings.

Additional information

This is related to point 4 in #3571. That issue deals with the problem on the solve_strongly_connected_components side, handling the ramifications of an infeasible solution in that method.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions