Source code for watertap.core.util.infeasible

###############################################################################
# WaterTAP Copyright (c) 2021, The Regents of the University of California,
# through Lawrence Berkeley National Laboratory, Oak Ridge National
# Laboratory, National Renewable Energy Laboratory, and National Energy
# Technology Laboratory (subject to receipt of any required approvals from
# the U.S. Dept. of Energy). All rights reserved.
#
# Please see the files COPYRIGHT.md and LICENSE.md for full copyright and license
# information, respectively. These files are also available online at the URL
# "https://github.com/watertap-org/watertap/"
#
###############################################################################
"""
This module contains utility functions for infeasibility diagnostics of WaterTAP models.
"""
import logging, sys

from contextlib import contextmanager
from math import isclose
from pyomo.environ import Var, Constraint, value
from pyomo.util.infeasible import log_infeasible_constraints, log_infeasible_bounds

_logger = logging.getLogger("watertap.core.util.infeasible.print")
_logger.setLevel(logging.DEBUG)


@contextmanager
def _logging_handler(output_file):
    """
    helper for removing the complexity of dealing with loggers for the
    utility functions in this file
    """
    if output_file is None:
        _handler = logging.StreamHandler(stream=sys.stdout)
    else:
        _handler = logging.FileHandler(output_file, mode="w")
    _handler.setFormatter(logging.Formatter("%(message)s"))
    _logger.addHandler(_handler)
    try:
        yield _logger
    finally:
        _logger.removeHandler(_handler)

















def _eval_close(obj, val, rel_tol, abs_tol):
    """
    Helper for evaluating bounds and printing Pyomo variable or constraint.
    Prints if the Pyomo object is close to one or both of its bounds.

    Args:
        obj : Pyomo object (_VarData or _ConstraintData)
        val : the reference value
        rel_tol : the relative tolerance for math.isclose
        abs_tol : the absolute tolerance for math.isclose

    Returns:
        None
    """
    lb = value(obj.lb)
    ub = value(obj.ub)
    if (
        lb is not None
        and ub is not None
        and isclose(lb, ub, rel_tol=rel_tol, abs_tol=abs_tol)
    ):
        return
    if lb is not None and isclose(lb, val, rel_tol=rel_tol, abs_tol=abs_tol):
        print(f"{obj.name} near LB of {lb}")
    if ub is not None and isclose(val, ub, rel_tol=rel_tol, abs_tol=abs_tol):
        print(f"{obj.name} near UB of {ub}")