# -*- coding: utf-8 -*-
#################################################################################
# The Institute for the Design of Advanced Energy Systems Integrated Platform
# Framework (IDAES IP) was produced under the DOE Institute for the
# Design of Advanced Energy Systems (IDAES).
#
# Copyright (c) 2018-2024 by the software owners: The Regents of the
# University of California, through Lawrence Berkeley National Laboratory,
# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon
# University, West Virginia University Research Corporation, et al.
# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md
# for full copyright and license information.
#################################################################################
"""
This module contains utility functions for reporting structural statistics of
IDAES models.
"""
__author__ = "Andrew Lee"
import sys
from pyomo.environ import Block, Constraint, Expression, Objective, Var, value
from pyomo.dae import DerivativeVar
from pyomo.core.expr import identify_variables
from pyomo.common.collections import ComponentSet
from pyomo.common.deprecation import deprecation_warning
import idaes.logger as idaeslog
_log = idaeslog.getLogger(__name__)
# -------------------------------------------------------------------------
# Generator to handle cases where the input is an indexed Block
# Indexed blocks do not have component_data_objects, so we need to iterate
# over the indexed block first.
def _iter_indexed_block_data_objects(block, ctype, active, descend_into):
if block.is_indexed():
for bd in block.values():
for c in bd.component_data_objects(
ctype=ctype, active=active, descend_into=descend_into
):
yield c
else:
for c in block.component_data_objects(
ctype=ctype, active=active, descend_into=descend_into
):
yield c
# -------------------------------------------------------------------------
# Block methods
[docs]
def total_blocks_set(block):
"""
Method to return a ComponentSet of all Block components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Block components in block (including block
itself)
"""
total_blocks_set = ComponentSet(
_iter_indexed_block_data_objects(
block, ctype=Block, active=None, descend_into=True
)
)
total_blocks_set.add(block)
return total_blocks_set
[docs]
def number_total_blocks(block):
"""
Method to return the number of Block components in a model.
Args:
block : model to be studied
Returns:
Number of Block components in block (including block itself)
"""
# +1 to include main model
return (
sum(
1
for _ in _iter_indexed_block_data_objects(
block, ctype=Block, active=None, descend_into=True
)
)
+ 1
)
[docs]
def activated_blocks_set(block):
"""
Method to return a ComponentSet of all activated Block components in a
model.
Args:
block : model to be studied
Returns:
A ComponentSet including all activated Block components in block
(including block itself)
"""
block_set = ComponentSet()
if block.active:
block_set.add(block)
for b in _iter_indexed_block_data_objects(
block, ctype=Block, active=True, descend_into=True
):
block_set.add(b)
return block_set
[docs]
def number_activated_blocks(block):
"""
Method to return the number of activated Block components in a model.
Args:
block : model to be studied
Returns:
Number of activated Block components in block (including block itself)
"""
b = 0
if block.active:
b = 1
b += sum(
1
for _ in _iter_indexed_block_data_objects(
block, ctype=Block, active=True, descend_into=True
)
)
return b
[docs]
def deactivated_blocks_set(block):
"""
Method to return a ComponentSet of all deactivated Block components in a
model.
Args:
block : model to be studied
Returns:
A ComponentSet including all deactivated Block components in block
(including block itself)
"""
# component_data_objects active=False does not seem to work as expected
# Use difference of total and active block sets
return total_blocks_set(block) - activated_blocks_set(block)
[docs]
def number_deactivated_blocks(block):
"""
Method to return the number of deactivated Block components in a model.
Args:
block : model to be studied
Returns:
Number of deactivated Block components in block (including block
itself)
"""
# component_data_objects active=False does not seem to work as expected
# Use difference of total and active block sets
return number_total_blocks(block) - number_activated_blocks(block)
# -------------------------------------------------------------------------
# Basic Constraint methods
[docs]
def total_constraints_set(block):
"""
Method to return a ComponentSet of all Constraint components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Constraint components in block
"""
return ComponentSet(activated_block_component_generator(block, ctype=Constraint))
[docs]
def number_total_constraints(block):
"""
Method to return the total number of Constraint components in a model.
Args:
block : model to be studied
Returns:
Number of Constraint components in block
"""
return sum(1 for _ in activated_block_component_generator(block, ctype=Constraint))
[docs]
def activated_constraints_generator(block):
"""
Generator which returns all activated Constraint components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all activated Constraint components block
"""
for c in activated_block_component_generator(block, ctype=Constraint):
if c.active:
yield c
[docs]
def activated_constraints_set(block):
"""
Method to return a ComponentSet of all activated Constraint components in a
model.
Args:
block : model to be studied
Returns:
A ComponentSet including all activated Constraint components in block
"""
return ComponentSet(activated_constraints_generator(block))
[docs]
def number_activated_constraints(block):
"""
Method to return the number of activated Constraint components in a model.
Args:
block : model to be studied
Returns:
Number of activated Constraint components in block
"""
return sum(1 for _ in activated_constraints_generator(block))
[docs]
def deactivated_constraints_generator(block):
"""
Generator which returns all deactivated Constraint components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all deactivated Constraint components block
"""
for c in activated_block_component_generator(block, ctype=Constraint):
if not c.active:
yield c
[docs]
def deactivated_constraints_set(block):
"""
Method to return a ComponentSet of all deactivated Constraint components in
a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all deactivated Constraint components in block
"""
return ComponentSet(deactivated_constraints_generator(block))
[docs]
def number_deactivated_constraints(block):
"""
Method to return the number of deactivated Constraint components in a
model.
Args:
block : model to be studied
Returns:
Number of deactivated Constraint components in block
"""
return sum(1 for _ in deactivated_constraints_generator(block))
# -------------------------------------------------------------------------
# Equality Constraints
[docs]
def total_equalities_generator(block):
"""
Generator which returns all equality Constraint components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all equality Constraint components block
"""
for c in activated_block_component_generator(block, ctype=Constraint):
if c.upper is not None and c.lower is not None and c.upper == c.lower:
yield c
[docs]
def total_equalities_set(block):
"""
Method to return a ComponentSet of all equality Constraint components in a
model.
Args:
block : model to be studied
Returns:
A ComponentSet including all equality Constraint components in block
"""
return ComponentSet(total_equalities_generator(block))
[docs]
def number_total_equalities(block):
"""
Method to return the total number of equality Constraint components in a
model.
Args:
block : model to be studied
Returns:
Number of equality Constraint components in block
"""
return sum(1 for _ in total_equalities_generator(block))
[docs]
def activated_equalities_generator(block):
"""
Generator which returns all activated equality Constraint components in a
model.
Args:
block : model to be studied
Returns:
A generator which returns all activated equality Constraint components
block
"""
for c in _iter_indexed_block_data_objects(
block, Constraint, active=True, descend_into=True
):
if (
c.upper is not None
and c.lower is not None
and value(c.upper) == value(c.lower)
):
yield c
[docs]
def activated_equalities_set(block):
"""
Method to return a ComponentSet of all activated equality Constraint
components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all activated equality Constraint components
in block
"""
return ComponentSet(activated_equalities_generator(block))
[docs]
def number_activated_equalities(block):
"""
Method to return the number of activated equality Constraint components in
a model.
Args:
block : model to be studied
Returns:
Number of activated equality Constraint components in block
"""
return sum(1 for _ in activated_equalities_generator(block))
[docs]
def deactivated_equalities_generator(block):
"""
Generator which returns all deactivated equality Constraint components in a
model.
Args:
block : model to be studied
Returns:
A generator which returns all deactivated equality Constraint
components block
"""
for c in total_equalities_generator(block):
if not c.active:
yield c
[docs]
def deactivated_equalities_set(block):
"""
Method to return a ComponentSet of all deactivated equality Constraint
components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all deactivated equality Constraint components
in block
"""
return ComponentSet(deactivated_equalities_generator(block))
[docs]
def number_deactivated_equalities(block):
"""
Method to return the number of deactivated equality Constraint components
in a model.
Args:
block : model to be studied
Returns:
Number of deactivated equality Constraint components in block
"""
return sum(1 for _ in deactivated_equalities_generator(block))
# -------------------------------------------------------------------------
# Inequality Constraints
[docs]
def total_inequalities_generator(block):
"""
Generator which returns all inequality Constraint components in a
model.
Args:
block : model to be studied
Returns:
A generator which returns all inequality Constraint components block
"""
for c in activated_block_component_generator(block, ctype=Constraint):
if c.upper is None or c.lower is None:
yield c
[docs]
def total_inequalities_set(block):
"""
Method to return a ComponentSet of all inequality Constraint components in
a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all inequality Constraint components in block
"""
return ComponentSet(total_inequalities_generator(block))
[docs]
def number_total_inequalities(block):
"""
Method to return the total number of inequality Constraint components in a
model.
Args:
block : model to be studied
Returns:
Number of inequality Constraint components in block
"""
return sum(1 for _ in total_inequalities_generator(block))
[docs]
def activated_inequalities_generator(block):
"""
Generator which returns all activated inequality Constraint components in a
model.
Args:
block : model to be studied
Returns:
A generator which returns all activated inequality Constraint
components block
"""
for c in _iter_indexed_block_data_objects(
block, Constraint, active=True, descend_into=True
):
if c.upper is None or c.lower is None:
yield c
[docs]
def activated_inequalities_set(block):
"""
Method to return a ComponentSet of all activated inequality Constraint
components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all activated inequality Constraint components
in block
"""
return ComponentSet(activated_inequalities_generator(block))
[docs]
def number_activated_inequalities(block):
"""
Method to return the number of activated inequality Constraint components
in a model.
Args:
block : model to be studied
Returns:
Number of activated inequality Constraint components in block
"""
return sum(1 for _ in activated_inequalities_generator(block))
[docs]
def deactivated_inequalities_generator(block):
"""
Generator which returns all deactivated inequality Constraint components in
a model.
Args:
block : model to be studied
Returns:
A generator which returns all indeactivated equality Constraint
components block
"""
for c in total_inequalities_generator(block):
if not c.active:
yield c
[docs]
def deactivated_inequalities_set(block):
"""
Method to return a ComponentSet of all deactivated inequality Constraint
components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all deactivated inequality Constraint
components in block
"""
return ComponentSet(deactivated_inequalities_generator(block))
[docs]
def number_deactivated_inequalities(block):
"""
Method to return the number of deactivated inequality Constraint components
in a model.
Args:
block : model to be studied
Returns:
Number of deactivated inequality Constraint components in block
"""
return sum(1 for _ in deactivated_inequalities_generator(block))
# -------------------------------------------------------------------------
# Basic Variable Methods
# Always use ComponentSets for Vars to avoid duplication of References
# i.e. number methods should always use the ComponentSet, not a generator
[docs]
def variables_set(block):
"""
Method to return a ComponentSet of all Var components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components in block
"""
return ComponentSet(
_iter_indexed_block_data_objects(
block, ctype=Var, active=True, descend_into=True
)
)
[docs]
def number_variables(block):
"""
Method to return the number of Var components in a model.
Args:
block : model to be studied
Returns:
Number of Var components in block
"""
return len(variables_set(block))
[docs]
def fixed_variables_generator(block):
"""
Generator which returns all fixed Var components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all fixed Var components block
"""
for v in _iter_indexed_block_data_objects(
block, ctype=Var, active=True, descend_into=True
):
if v.fixed:
yield v
[docs]
def fixed_variables_set(block):
"""
Method to return a ComponentSet of all fixed Var components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all fixed Var components in block
"""
return ComponentSet(fixed_variables_generator(block))
[docs]
def number_fixed_variables(block):
"""
Method to return the number of fixed Var components in a model.
Args:
block : model to be studied
Returns:
Number of fixed Var components in block
"""
return len(fixed_variables_set(block))
[docs]
def unfixed_variables_generator(block):
"""
Generator which returns all unfixed Var components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all unfixed Var components block
"""
for v in _iter_indexed_block_data_objects(
block, ctype=Var, active=True, descend_into=True
):
if not v.fixed:
yield v
[docs]
def unfixed_variables_set(block):
"""
Method to return a ComponentSet of all unfixed Var components in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all unfixed Var components in block
"""
return ComponentSet(unfixed_variables_generator(block))
[docs]
def number_unfixed_variables(block):
"""
Method to return the number of unfixed Var components in a model.
Args:
block : model to be studied
Returns:
Number of unfixed Var components in block
"""
return len(unfixed_variables_set(block))
[docs]
def variables_near_bounds_generator(
block,
tol=None,
relative=None,
skip_lb=False,
skip_ub=False,
abs_tol=1e-4,
rel_tol=1e-4,
):
"""
Generator which returns all Var components in a model which have a value
within tol (default: relative) of a bound.
Args:
block : model to be studied
abs_tol : absolute tolerance for inclusion in generator (default = 1e-4)
rel_tol : relative tolerance for inclusion in generator (default = 1e-4)
skip_lb: Boolean to skip lower bound (default = False)
skip_ub: Boolean to skip upper bound (default = False)
Returns:
A generator which returns all Var components block that are close to a
bound
"""
# Check for deprecated arguments
if relative is not None:
msg = (
"variables_near_bounds_generator has deprecated the relative argument. "
"Please set abs_tol and rel_tol arguments instead."
)
deprecation_warning(msg=msg, logger=_log, version="2.2.0", remove_in="3.0.0")
if tol is not None:
msg = (
"variables_near_bounds_generator has deprecated the tol argument. "
"Please set abs_tol and rel_tol arguments instead."
)
deprecation_warning(msg=msg, logger=_log, version="2.2.0", remove_in="3.0.0")
# Set tolerances using the provided value
abs_tol = tol
rel_tol = tol
for v in _iter_indexed_block_data_objects(
block, ctype=Var, active=True, descend_into=True
):
# To avoid errors, check that v has a value
if v.value is None:
continue
# First, magnitude of variable
if v.ub is not None and v.lb is not None:
# Both upper and lower bounds, apply tol to (upper - lower)
mag = value(v.ub - v.lb)
elif v.ub is not None:
# Only upper bound, apply tol to bound value
mag = abs(value(v.ub))
elif v.lb is not None:
# Only lower bound, apply tol to bound value
mag = abs(value(v.lb))
else:
mag = 0
# Calculate largest tolerance from absolute and relative
tol = max(abs_tol, mag * rel_tol)
if v.ub is not None and not skip_ub and value(v.ub - v.value) <= tol:
yield v
elif v.lb is not None and not skip_lb and value(v.value - v.lb) <= tol:
yield v
[docs]
def variables_near_bounds_set(
block,
tol=None,
relative=None,
skip_lb=False,
skip_ub=False,
abs_tol=1e-4,
rel_tol=1e-4,
):
"""
Method to return a ComponentSet of all Var components in a model which have
a value within tolerance of a bound.
Args:
block : model to be studied
abs_tol : absolute tolerance for inclusion in generator (default = 1e-4)
rel_tol : relative tolerance for inclusion in generator (default = 1e-4)
skip_lb: Boolean to skip lower bound (default = False)
skip_ub: Boolean to skip upper bound (default = False)
Returns:
A ComponentSet including all Var components block that are close to a
bound
"""
return ComponentSet(
variables_near_bounds_generator(
block, tol, relative, skip_lb, skip_ub, abs_tol, rel_tol
)
)
[docs]
def number_variables_near_bounds(block, tol=None, abs_tol=1e-4, rel_tol=1e-4):
"""
Method to return the number of all Var components in a model which have
a value within tol (relative) of a bound.
Args:
block : model to be studied
abs_tol : absolute tolerance for inclusion in generator (default = 1e-4)
rel_tol : relative tolerance for inclusion in generator (default = 1e-4)
Returns:
Number of components block that are close to a bound
"""
return len(
variables_near_bounds_set(block, tol=tol, abs_tol=abs_tol, rel_tol=rel_tol)
)
# -------------------------------------------------------------------------
# Variables in Constraints
[docs]
def variables_in_activated_constraints_set(block):
"""
Method to return a ComponentSet of all Var components which appear within a
Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components which appear within
activated Constraints in block
"""
var_set = ComponentSet()
for c in _iter_indexed_block_data_objects(
block, ctype=Constraint, active=True, descend_into=True
):
for v in identify_variables(c.body):
var_set.add(v)
return var_set
[docs]
def number_variables_in_activated_constraints(block):
"""
Method to return the number of Var components that appear within active
Constraints in a model.
Args:
block : model to be studied
Returns:
Number of Var components which appear within active Constraints in
block
"""
return len(variables_in_activated_constraints_set(block))
[docs]
def variables_not_in_activated_constraints_set(block):
"""
Method to return a ComponentSet of all Var components which do not appear within a
Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components which do not appear within
activated Constraints in block
"""
var_set = ComponentSet()
active_vars = variables_in_activated_constraints_set(block)
for v in _iter_indexed_block_data_objects(
block, ctype=Var, active=True, descend_into=True
):
if v not in active_vars:
var_set.add(v)
return var_set
[docs]
def number_variables_not_in_activated_constraints(block):
"""
Method to return the number of Var components that do not appear within active
Constraints in a model.
Args:
block : model to be studied
Returns:
Number of Var components which do not appear within active Constraints in
block
"""
return len(variables_not_in_activated_constraints_set(block))
[docs]
def variables_in_activated_equalities_set(block):
"""
Method to return a ComponentSet of all Var components which appear within
an equality Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components which appear within
activated equality Constraints in block
"""
var_set = ComponentSet()
for c in activated_equalities_generator(block):
for v in identify_variables(c.body):
var_set.add(v)
return var_set
[docs]
def number_variables_in_activated_equalities(block):
"""
Method to return the number of Var components which appear within activated
equality Constraints in a model.
Args:
block : model to be studied
Returns:
Number of Var components which appear within activated equality
Constraints in block
"""
return len(variables_in_activated_equalities_set(block))
[docs]
def variables_in_activated_inequalities_set(block):
"""
Method to return a ComponentSet of all Var components which appear within
an inequality Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components which appear within
activated inequality Constraints in block
"""
var_set = ComponentSet()
for c in activated_inequalities_generator(block):
for v in identify_variables(c.body):
var_set.add(v)
return var_set
[docs]
def number_variables_in_activated_inequalities(block):
"""
Method to return the number of Var components which appear within activated
inequality Constraints in a model.
Args:
block : model to be studied
Returns:
Number of Var components which appear within activated inequality
Constraints in block
"""
return len(variables_in_activated_inequalities_set(block))
[docs]
def variables_only_in_inequalities(block):
"""
Method to return a ComponentSet of all Var components which appear only
within inequality Constraints in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components which appear only within
inequality Constraints in block
"""
return variables_in_activated_inequalities_set(
block
) - variables_in_activated_equalities_set(block)
[docs]
def number_variables_only_in_inequalities(block):
"""
Method to return the number of Var components which appear only within
activated inequality Constraints in a model.
Args:
block : model to be studied
Returns:
Number of Var components which appear only within activated inequality
Constraints in block
"""
return len(variables_only_in_inequalities(block))
# -------------------------------------------------------------------------
# Fixed Variables in Constraints
[docs]
def fixed_variables_in_activated_equalities_set(block):
"""
Method to return a ComponentSet of all fixed Var components which appear
within an equality Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all fixed Var components which appear within
activated equality Constraints in block
"""
var_set = ComponentSet()
for v in variables_in_activated_equalities_set(block):
if v.fixed:
var_set.add(v)
return var_set
[docs]
def number_fixed_variables_in_activated_equalities(block):
"""
Method to return the number of fixed Var components which appear within
activated equality Constraints in a model.
Args:
block : model to be studied
Returns:
Number of fixed Var components which appear within activated equality
Constraints in block
"""
return len(fixed_variables_in_activated_equalities_set(block))
[docs]
def unfixed_variables_in_activated_equalities_set(block):
"""
Method to return a ComponentSet of all unfixed Var components which appear
within an activated equality Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all unfixed Var components which appear within
activated equality Constraints in block
"""
var_set = ComponentSet()
for v in variables_in_activated_equalities_set(block):
if not v.fixed:
var_set.add(v)
return var_set
[docs]
def number_unfixed_variables_in_activated_equalities(block):
"""
Method to return the number of unfixed Var components which appear within
activated equality Constraints in a model.
Args:
block : model to be studied
Returns:
Number of unfixed Var components which appear within activated equality
Constraints in block
"""
return len(unfixed_variables_in_activated_equalities_set(block))
[docs]
def fixed_variables_only_in_inequalities(block):
"""
Method to return a ComponentSet of all fixed Var components which appear
only within activated inequality Constraints in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all fixed Var components which appear only
within activated inequality Constraints in block
"""
var_set = ComponentSet()
for v in variables_only_in_inequalities(block):
if v.fixed:
var_set.add(v)
return var_set
[docs]
def number_fixed_variables_only_in_inequalities(block):
"""
Method to return the number of fixed Var components which only appear
within activated inequality Constraints in a model.
Args:
block : model to be studied
Returns:
Number of fixed Var components which only appear within activated
inequality Constraints in block
"""
return len(fixed_variables_only_in_inequalities(block))
# -------------------------------------------------------------------------
# Unused and un-Transformed Variables
[docs]
def unused_variables_set(block):
"""
Method to return a ComponentSet of all Var components which do not appear
within any activated Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Var components which do not appear within
any Constraints in block
"""
return variables_set(block) - variables_in_activated_constraints_set(block)
[docs]
def number_unused_variables(block):
"""
Method to return the number of Var components which do not appear within
any activated Constraint in a model.
Args:
block : model to be studied
Returns:
Number of Var components which do not appear within any activagted
Constraints in block
"""
return len(unused_variables_set(block))
[docs]
def fixed_unused_variables_set(block):
"""
Method to return a ComponentSet of all fixed Var components which do not
appear within any activated Constraint in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all fixed Var components which do not appear
within any Constraints in block
"""
var_set = ComponentSet()
for v in unused_variables_set(block):
if v.fixed:
var_set.add(v)
return var_set
[docs]
def number_fixed_unused_variables(block):
"""
Method to return the number of fixed Var components which do not appear
within any activated Constraint in a model.
Args:
block : model to be studied
Returns:
Number of fixed Var components which do not appear within any activated
Constraints in block
"""
return len(fixed_unused_variables_set(block))
[docs]
def derivative_variables_set(block):
"""
Method to return a ComponentSet of all DerivativeVar components which
appear in a model. Users should note that DerivativeVars are converted to
ordinary Vars when a DAE transformation is applied. Thus, this method is
useful for detecting any DerivativeVars which were do transformed.
Args:
block : model to be studied
Returns:
A ComponentSet including all DerivativeVar components which appear in
block
"""
return ComponentSet(
_iter_indexed_block_data_objects(
block, ctype=DerivativeVar, active=True, descend_into=True
)
)
[docs]
def number_derivative_variables(block):
"""
Method to return the number of DerivativeVar components which
appear in a model. Users should note that DerivativeVars are converted to
ordinary Vars when a DAE transformation is applied. Thus, this method is
useful for detecting any DerivativeVars which were do transformed.
Args:
block : model to be studied
Returns:
Number of DerivativeVar components which appear in block
"""
return len(derivative_variables_set(block))
# -------------------------------------------------------------------------
# Objective methods
[docs]
def total_objectives_generator(block):
"""
Generator which returns all Objective components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all Objective components block
"""
for o in activated_block_component_generator(block, ctype=Objective):
yield o
[docs]
def total_objectives_set(block):
"""
Method to return a ComponentSet of all Objective components which appear
in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Objective components which appear in block
"""
return ComponentSet(total_objectives_generator(block))
[docs]
def number_total_objectives(block):
"""
Method to return the number of Objective components which appear in a model
Args:
block : model to be studied
Returns:
Number of Objective components which appear in block
"""
return sum(1 for _ in total_objectives_generator(block))
[docs]
def activated_objectives_generator(block):
"""
Generator which returns all activated Objective components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all activated Objective components block
"""
for o in activated_block_component_generator(block, ctype=Objective):
if o.active:
yield o
[docs]
def activated_objectives_set(block):
"""
Method to return a ComponentSet of all activated Objective components which
appear in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all activated Objective components which
appear in block
"""
return ComponentSet(activated_objectives_generator(block))
[docs]
def number_activated_objectives(block):
"""
Method to return the number of activated Objective components which appear
in a model.
Args:
block : model to be studied
Returns:
Number of activated Objective components which appear in block
"""
return sum(1 for _ in activated_objectives_generator(block))
[docs]
def deactivated_objectives_generator(block):
"""
Generator which returns all deactivated Objective components in a model.
Args:
block : model to be studied
Returns:
A generator which returns all deactivated Objective components block
"""
for o in activated_block_component_generator(block, ctype=Objective):
if not o.active:
yield o
[docs]
def deactivated_objectives_set(block):
"""
Method to return a ComponentSet of all deactivated Objective components
which appear in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all deactivated Objective components which
appear in block
"""
return ComponentSet(deactivated_objectives_generator(block))
[docs]
def number_deactivated_objectives(block):
"""
Method to return the number of deactivated Objective components which
appear in a model.
Args:
block : model to be studied
Returns:
Number of deactivated Objective components which appear in block
"""
return sum(1 for _ in deactivated_objectives_generator(block))
# -------------------------------------------------------------------------
# Expression methods
# Always use ComponentsSets here to avoid duplication of References
[docs]
def expressions_set(block):
"""
Method to return a ComponentSet of all Expression components which appear
in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including all Expression components which appear in
block
"""
return ComponentSet(
_iter_indexed_block_data_objects(
block, ctype=Expression, active=True, descend_into=True
)
)
[docs]
def number_expressions(block):
"""
Method to return the number of Expression components which appear in a
model.
Args:
block : model to be studied
Returns:
Number of Expression components which appear in block
"""
return len(expressions_set(block))
# -------------------------------------------------------------------------
# Other model statistics
[docs]
def degrees_of_freedom(block):
"""
Method to return the degrees of freedom of a model.
Args:
block : model to be studied
Returns:
Number of degrees of freedom in block.
"""
return number_unfixed_variables_in_activated_equalities(
block
) - number_activated_equalities(block)
[docs]
def large_residuals_set(block, tol=1e-5, return_residual_values=False):
"""
Method to return a ComponentSet of all Constraint components with a
residual greater than a given threshold which appear in a model.
Args:
block : model to be studied
tol : residual threshold for inclusion in ComponentSet
return_residual_values: boolean, if true return dictionary with
residual values
Returns:
large_residual_set: A ComponentSet including all Constraint components
with a residual greater than tol which appear in block (if
return_residual_values is false) residual_values: dictionary with
constraint as key and residual (float) as value (if
return_residual_values is true)
"""
large_residuals_set = ComponentSet()
if return_residual_values:
residual_values = dict()
for c in _iter_indexed_block_data_objects(
block, ctype=Constraint, active=True, descend_into=True
):
try:
r = 0.0 # residual
# skip if no lower bound set
if c.lower is None:
r_temp = 0
else:
r_temp = value(c.lower - c.body())
# update the residual
if r_temp > r:
r = r_temp
# skip if no upper bound set
if c.upper is None:
r_temp = 0
else:
r_temp = value(c.body() - c.upper)
# update the residual
if r_temp > r:
r = r_temp
# save residual if it is above threshold
if r > tol:
large_residuals_set.add(c)
if return_residual_values:
residual_values[c] = r
except (AttributeError, TypeError, ValueError):
large_residuals_set.add(c)
if return_residual_values:
residual_values[c] = None
if return_residual_values:
return residual_values
else:
return large_residuals_set
[docs]
def number_large_residuals(block, tol=1e-5):
"""
Method to return the number Constraint components with a residual greater
than a given threshold which appear in a model.
Args:
block : model to be studied
tol : residual threshold for inclusion in ComponentSet
Returns:
Number of Constraint components with a residual greater than tol which
appear in block
"""
lr = 0
for c in _iter_indexed_block_data_objects(
block, ctype=Constraint, active=True, descend_into=True
):
if c.active and value(c.lower - c.body()) > tol:
lr += 1
elif c.active and value(c.body() - c.upper) > tol:
lr += 1
return lr
[docs]
def active_variables_in_deactivated_blocks_set(block):
"""
Method to return a ComponentSet of any Var components which appear within
an active Constraint but belong to a deactivated Block in a model.
Args:
block : model to be studied
Returns:
A ComponentSet including any Var components which belong to a
deactivated Block but appear in an activate Constraint in block
"""
var_set = ComponentSet()
block_set = activated_blocks_set(block)
for v in variables_in_activated_constraints_set(block):
if v.parent_block() not in block_set:
var_set.add(v)
return var_set
[docs]
def number_active_variables_in_deactivated_blocks(block):
"""
Method to return the number of Var components which appear within an active
Constraint but belong to a deactivated Block in a model.
Args:
block : model to be studied
Returns:
Number of Var components which belong to a deactivated Block but appear
in an activate Constraint in block
"""
return len(active_variables_in_deactivated_blocks_set(block))
# -------------------------------------------------------------------------
# Reporting methods
[docs]
def report_statistics(block, ostream=None):
"""
Method to print a report of the model statistics for a Pyomo Block
Args:
block : the Block object to report statistics from
ostream : output stream for printing (defaults to sys.stdout)
Returns:
Printed output of the model statistics
"""
if ostream is None:
ostream = sys.stdout
tab = " " * 4
header = "=" * 72
if block.name == "unknown":
name_str = ""
else:
name_str = f"- {block.name}"
ostream.write("\n")
ostream.write(header + "\n")
ostream.write(f"Model Statistics {name_str} \n")
ostream.write("\n")
ostream.write(f"Degrees of Freedom: " f"{degrees_of_freedom(block)} \n")
ostream.write("\n")
ostream.write(f"Total No. Variables: " f"{number_variables(block)} \n")
ostream.write(
f"{tab}No. Fixed Variables: " f"{number_fixed_variables(block)}" f"\n"
)
ostream.write(
f"{tab}No. Unused Variables: "
f"{number_unused_variables(block)} (Fixed):"
f"{number_fixed_unused_variables(block)})"
f"\n"
)
nv_alias = number_variables_only_in_inequalities
nfv_alias = number_fixed_variables_only_in_inequalities
ostream.write(
f"{tab}No. Variables only in Inequalities:"
f" {nv_alias(block)}"
f" (Fixed: {nfv_alias(block)}) \n"
)
ostream.write("\n")
ostream.write(f"Total No. Constraints: " f"{number_total_constraints(block)} \n")
ostream.write(
f"{tab}No. Equality Constraints: "
f"{number_total_equalities(block)}"
f" (Deactivated: "
f"{number_deactivated_equalities(block)})"
f"\n"
)
ostream.write(
f"{tab}No. Inequality Constraints: "
f"{number_total_inequalities(block)}"
f" (Deactivated: "
f"{number_deactivated_inequalities(block)})"
f"\n"
)
ostream.write("\n")
ostream.write(
f"No. Objectives: "
f"{number_total_objectives(block)}"
f" (Deactivated: "
f"{number_deactivated_objectives(block)})"
f"\n"
)
ostream.write("\n")
ostream.write(
f"No. Blocks: {number_total_blocks(block)}"
f" (Deactivated: "
f"{number_deactivated_blocks(block)}) \n"
)
ostream.write(f"No. Expressions: " f"{number_expressions(block)} \n")
ostream.write(header + "\n")
ostream.write("\n")
# -------------------------------------------------------------------------
# Common sub-methods
[docs]
def activated_block_component_generator(block, ctype):
"""
Generator which returns all the components of a given ctype which exist in
activated Blocks within a model.
Args:
block : model to be studied
ctype : type of Pyomo component to be returned by generator.
Returns:
A generator which returns all components of ctype which appear in
activated Blocks in block
"""
# Yield local components first
for c in _iter_indexed_block_data_objects(
block, ctype=ctype, active=None, descend_into=False
):
yield c
# Then yield components in active sub-blocks
for b in _iter_indexed_block_data_objects(
block, ctype=Block, active=True, descend_into=True
):
for c in b.component_data_objects(ctype=ctype, active=None, descend_into=False):
yield c