Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Greybox model DOF counitng in degrees_of_freedom function #1512

Merged
merged 23 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fb0f8fd
first commit
avdudchenko Oct 26, 2024
a719e47
typo fixes
avdudchenko Oct 26, 2024
9a09cc0
Update model_statistics.py
avdudchenko Oct 26, 2024
4a25d72
updates to tests
avdudchenko Oct 28, 2024
2324cbb
moved where we count greybox var
avdudchenko Oct 28, 2024
01c3a8a
add additonal functions to get grayboxblocks
avdudchenko Oct 28, 2024
e67e56b
initial addi tion of grayboxs stats to dignostic tools
avdudchenko Oct 29, 2024
f6a36e6
Update test_model_statistics.py
avdudchenko Oct 29, 2024
880a1be
Merge branch 'main' into add_gray_box_do_counting
avdudchenko Oct 29, 2024
75cc0c1
Merge branch 'main' into add_gray_box_do_counting
avdudchenko Oct 31, 2024
03291fa
udpate to diagonotsics
avdudchenko Nov 1, 2024
4b6de4c
Update test_model_diagnostics.py
avdudchenko Nov 1, 2024
d9ff557
Update model_diagnostics.py
avdudchenko Nov 1, 2024
2cd193a
Merge branch 'main' into add_gray_box_do_counting
avdudchenko Nov 21, 2024
c3498fc
addressign comments #1
avdudchenko Nov 22, 2024
5ce566e
added errors for greybox
avdudchenko Nov 22, 2024
fe052f3
Merge branch 'main' into add_gray_box_do_counting
avdudchenko Nov 22, 2024
695b9cf
add exception tests
avdudchenko Nov 22, 2024
2e01052
Merge branch 'add_gray_box_do_counting' of https://github.com/avdudch…
avdudchenko Nov 22, 2024
1bf6ff2
Update model_statistics.py
avdudchenko Nov 22, 2024
b7bf7e0
Merge branch 'main' into add_gray_box_do_counting
avdudchenko Dec 5, 2024
e972091
Update idaes/core/util/model_statistics.py
avdudchenko Dec 12, 2024
d3d2082
Merge branch 'main' into add_gray_box_do_counting
ksbeattie Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions idaes/core/util/model_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from pyomo.core.expr import identify_variables
from pyomo.common.collections import ComponentSet
from pyomo.common.deprecation import deprecation_warning
from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxBlock

import idaes.logger as idaeslog

Expand Down Expand Up @@ -377,7 +378,32 @@ def number_activated_equalities(block):
Returns:
Number of activated equality Constraint components in block
"""
return sum(1 for _ in activated_equalities_generator(block))
return sum(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring of this function needs to be updated to explain what's going on with greybox models.

1 for _ in activated_equalities_generator(block)
) + number_grey_box_equalities(block)


def number_grey_box_equalities(block) -> int:
"""
Function to compute total number of equality constraints for all GreyBox objects in this block.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"total number of equality constraints" -> "total number of implied equality constraints"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it might be fine leaving as is. I didn't realize GreyBox models have an n_equality_constraints() attribute. The distinction here is probably that they don't exist as Constraint objects.


A GreyBox model is always assumed to be 0DOFs where each output[i]==f(inputs)
where f is GreyBox model, this should be true regardless if
GreyBox model is doing internal optimization or not, as every output
is calculated through a the GreyBox internal model using provided inputs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"through a the GreyBox" -> "through the GreyBox"

Also, is this 0DOFs property of GreyBox models always true, or is it true only if the user has correctly configured their GreyBox model?


Args:
block : pyomo concrete model or pyomo block

Returns:
Number of equality constraints in all GreyBox objects on the provided block
"""
equalities = 0
for grey_box in _iter_indexed_block_data_objects(
block, ctype=ExternalGreyBoxBlock, active=True, descend_into=True
):
equalities += len(grey_box.outputs)
return equalities
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written, this will be incorrect for grey box models that have a nonzero number of "explicitly defined equality constraints" (as opposed to the equality constraints that are created to define the outputs). I think all that's needed to correct this is:

equalities += grey_box.get_external_model().n_equality_constraints()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, did not think about this scenario! Updated to test graybox with suggestion, this now includes an example constraint that binds 2 inputs together.

        def input_names(self):
            return ["a1", "a2", "a3"]

        def output_names(self):
            return ["o1", "o2"]

        def equality_constraint_names(self):
            return ["a12"]

        def evaluate_equality_constraints(self):
            a1 = self._input_values[0]
            a2 = self._input_values[1]
            return [a1 * 0.5 - a2]



def deactivated_equalities_generator(block):
Expand Down Expand Up @@ -529,7 +555,7 @@ def deactivated_inequalities_generator(block):
block : model to be studied

Returns:
A generator which returns all indeactivated equality Constraint
A generator which returns all in deactivated equality Constraint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "in" should be removed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

components block
"""
for c in total_inequalities_generator(block):
Expand Down Expand Up @@ -1034,6 +1060,19 @@ def unfixed_variables_in_activated_equalities_set(block):
for v in variables_in_activated_equalities_set(block):
if not v.fixed:
var_set.add(v)

# Checks for greyboxes, and if they exist will add
# input and output vars to var_set if they are free
# inputs and outputs are defined names for greybox class and should always exist
for grey_box in _iter_indexed_block_data_objects(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to have a number_of_greybox_variables function to do this so that users can check that separately and so that we can test the code in isolation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added one for variables, and unfixed variables.

block, ctype=ExternalGreyBoxBlock, active=True, descend_into=True
):
for in_var in grey_box.inputs:
if not grey_box.inputs[in_var].fixed:
var_set.add(grey_box.inputs[in_var])
for out_var in grey_box.outputs:
if not grey_box.outputs[out_var].fixed:
var_set.add(grey_box.outputs[out_var])
avdudchenko marked this conversation as resolved.
Show resolved Hide resolved
return var_set


Expand Down
40 changes: 40 additions & 0 deletions idaes/core/util/tests/test_model_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@

from idaes.core.util.model_statistics import *
from idaes.core.util.model_statistics import _iter_indexed_block_data_objects
from pyomo.contrib.pynumero.interfaces.external_grey_box import (
ExternalGreyBoxBlock,
ExternalGreyBoxModel,
)


@pytest.mark.unit
Expand Down Expand Up @@ -685,6 +689,42 @@ def test_degrees_of_freedom(m):
assert degrees_of_freedom(m.b2) == -1


@pytest.mark.unit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also have test for the functions to collect the number of greybox variables and constraints.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added tests

def test_degrees_of_freedom_with_graybox():
"""non functional graybox model added to m fixture, to test DOFs

GreyBoxModel has 3 inputs and 2 outputs calculated an unknown function"""

class BasicGrayBox(ExternalGreyBoxModel):
def input_names(self):
return ["a1", "a2", "a3"]

def output_names(self):
return ["o1", "o2"]

m = ConcreteModel()

m.gb = ExternalGreyBoxBlock(external_model=BasicGrayBox())
# verify DOFS works on stand alone greybox
assert degrees_of_freedom(m) == 3
m.gb.inputs.fix()
assert degrees_of_freedom(m) == 0
m.gb.outputs.fix()
assert degrees_of_freedom(m) == -2
m.gb.outputs.unfix()

# verify DOFs works on greybox connected to other vars on a model via constraints
m.a1 = Var(initialize=1)
m.a1.fix()
m.gb.inputs["a1"].unfix()
m.a1_eq = Constraint(expr=m.a1 == m.gb.inputs["a1"])
assert degrees_of_freedom(m) == 0
m.o1 = Var(initialize=1)
m.o1_eq = Constraint(expr=m.o1 == m.gb.outputs["o1"])
m.o1.fix()
assert degrees_of_freedom(m) == -1


@pytest.mark.unit
def test_large_residuals_set(m):
# Initialize derivative var values so no errors occur
Expand Down
Loading