Skip to content

Commit

Permalink
Minor improvements to scaling API (#1507)
Browse files Browse the repository at this point in the history
* Adding infrastructure to support ipopt_v2

* Moving core/util to ipopt_v2

* Moving MH initializer to ipopt_v2

* Fixing pint version issue

* Set TSA to use old IPOPT interface

* Trying to resolve Windows failures

* Working on platofrm dependent failure

* BTInitializer with presolve

* Moving last bits of core code to ipopt_v2

* Starting on idaes/models

* Removing ma57_automatic_scaling default and updating idaes/models/control

* idaes/model/properties part 1

* Remaining parts of idaes/models/proeprties

* Fixing typo

* Switching idaes/models/unit_models to ipopt_v2

* Attempt to work around HXLC issues for now

* Some clean up

* Switching modular properties initializer to solver indexed blocks

* Addressing comments

* Recovering from previous branch

* Some clean up

* Adding ScalerBase class and tests

* Working on CustomScalerBase

* Nominal value constraint scaling

* Adding some initial integration tests for scaling

* Some more nominal magnitude scaling approaches

* Prototyping pseudojacobian scaler

* Trying to debug pseudojacobian

* Removing unnecessary import

* Addressing pylint issues

* Cleaning up nominal jacobian

* More methods for CustomScalerBase

* Prototyping Gibbs reactor scaler

* Gibbs reactor constraint scaling

* Working on testing and profiling

* Refining Gibbs scaler

* Refining nominal value walker

* Fixing walker tests

* Testing GibbsScaler with initialization

* Fixing auto norm scaling on indexed blocks

* Testing scaling profiler

* Fixing typos

* Fixing pylint issue

* Imrpoving some doc strings

* Apply suggestions from code review

Co-authored-by: MarcusHolly <[email protected]>

* Fixing issue with autoscaling vars with value None

* Adding profiler to __init__

* Fixing name for RSS method

* Fixing import of pyomo.environ

* Allowing default scaling for indexed components

* Adding catch for critical solver failure in profiler

* Starting on docs

* Finishing docs and unifiying method names

* Profiler report methods and docs

* Fixing typos

* Pylint: fix unnecessary f-string

* Working on equilibrium reactor scaler

* Finishing tests for equilibrium reactor scaler

* Updaing requil scaler

* Fixing typo

* Addressing pylint issue

* Improving guess for heat duty scaling

* Updating test value for heat duty scaling

* Minor fix

* Adding Enum to __init__

* Adding doc strings for saponification scalers

* Fixing typo

* Minor clean up

* Removing need for string constants

---------

Co-authored-by: MarcusHolly <[email protected]>
  • Loading branch information
andrewlee94 and MarcusHolly authored Oct 25, 2024
1 parent 96237b9 commit d931060
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 78 deletions.
29 changes: 10 additions & 19 deletions idaes/core/scaling/custom_scaler_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,7 @@ def propagate_state_scaling(

def call_submodel_scaler_method(
self,
model,
submodel: str,
submodel,
method: str,
submodel_scalers: ComponentMap = None,
overwrite: bool = False,
Expand All @@ -567,55 +566,47 @@ def call_submodel_scaler_method(
default scaler for the submodel is used.
Args:
model: parent model of submodel
submodel: local name of submodel to be scaled as str
submodel: submodel to be scaled
submodel_scalers: user provided ComponentMap of Scalers to use for submodels
method: name of method to call from submodel (as string)
overwrite: whether to overwrite existing scaling factors
Returns:
None
"""
# Get actual submodel object from name
# For this method, we have to use the component name as the Scaler is written
# before the model is constructed.
sm_obj = model.find_component(submodel)

if submodel_scalers is None:
submodel_scalers = {}

# Iterate over indices of submodel
for smdata in sm_obj.values():
for smdata in submodel.values():
# Get Scaler for submodel
if sm_obj in submodel_scalers:
scaler = submodel_scalers[sm_obj]
if submodel in submodel_scalers:
scaler = submodel_scalers[submodel]
if callable(scaler):
# Check to see if Scaler is callable - this implies it is a class and not an instance
# Call the class to create an instance
scaler = scaler()
_log.debug(f"Using user-defined Scaler for {model}.{submodel}.")
_log.debug(f"Using user-defined Scaler for {submodel}.")
else:
try:
scaler = smdata.default_scaler
_log.debug(f"Using default Scaler for {model}.{submodel}.")
_log.debug(f"Using default Scaler for {submodel}.")
except AttributeError:
_log.debug(
f"No default Scaler set for {model}.{submodel}. Cannot call {method}."
f"No default Scaler set for {submodel}. Cannot call {method}."
)
return
if scaler is not None:
scaler = scaler()
else:
_log.debug(
f"No Scaler found for {model}.{submodel}. Cannot call {method}."
)
_log.debug(f"No Scaler found for {submodel}. Cannot call {method}.")

# If a Scaler is found, call desired method
if scaler is not None:
try:
smeth = getattr(scaler, method)
except AttributeError:
raise AttributeError(
f"Scaler for {model}.{submodel} does not have a method named {method}."
f"Scaler for {submodel} does not have a method named {method}."
)
smeth(smdata, overwrite=overwrite)
44 changes: 8 additions & 36 deletions idaes/core/scaling/tests/test_custom_scaler_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,15 +608,12 @@ def test_call_submodel_scaler_method_no_scaler(self, caplog):
m.b = Block([1, 2, 3])

sb = CustomScalerBase()
sb.call_submodel_scaler_method(m, "b", method="dummy_method", overwrite=True)
sb.call_submodel_scaler_method(m.b, method="dummy_method", overwrite=True)

for bd in m.b.values():
assert not hasattr(bd, "_dummy_scaler_test")

assert (
"No default Scaler set for unknown.b. Cannot call dummy_method."
in caplog.text
)
assert "No default Scaler set for b. Cannot call dummy_method." in caplog.text

@pytest.mark.unit
def test_call_submodel_scaler_method_default_scaler(self, caplog):
Expand All @@ -629,12 +626,12 @@ def test_call_submodel_scaler_method_default_scaler(self, caplog):
bd.default_scaler = DummyScaler

sb = CustomScalerBase()
sb.call_submodel_scaler_method(m, "b", method="dummy_method", overwrite=True)
sb.call_submodel_scaler_method(m.b, method="dummy_method", overwrite=True)

for bd in m.b.values():
assert bd._dummy_scaler_test

assert "Using default Scaler for unknown.b." in caplog.text
assert "Using default Scaler for b." in caplog.text

@pytest.mark.unit
def test_call_submodel_scaler_method_user_scaler(self, caplog):
Expand All @@ -649,8 +646,7 @@ def test_call_submodel_scaler_method_user_scaler(self, caplog):

sb = CustomScalerBase()
sb.call_submodel_scaler_method(
m,
"b",
m.b,
method="dummy_method",
submodel_scalers=scaler_map,
overwrite=False,
Expand All @@ -659,7 +655,7 @@ def test_call_submodel_scaler_method_user_scaler(self, caplog):
for bd in m.b.values():
assert not bd._dummy_scaler_test

assert "Using user-defined Scaler for unknown.b." in caplog.text
assert "Using user-defined Scaler for b." in caplog.text

@pytest.mark.unit
def test_call_submodel_scaler_method_user_scaler_class(self, caplog):
Expand All @@ -674,8 +670,7 @@ def test_call_submodel_scaler_method_user_scaler_class(self, caplog):

sb = CustomScalerBase()
sb.call_submodel_scaler_method(
m,
"b",
m.b,
method="dummy_method",
submodel_scalers=scaler_map,
overwrite=False,
Expand All @@ -684,27 +679,4 @@ def test_call_submodel_scaler_method_user_scaler_class(self, caplog):
for bd in m.b.values():
assert not bd._dummy_scaler_test

assert "Using user-defined Scaler for unknown.b." in caplog.text

@pytest.mark.unit
def test_call_submodel_scaler_method_invalid_method(self):
# Dummy up a nested model
m = ConcreteModel()
m.b = Block([1, 2, 3])

scaler_map = ComponentMap()
scaler_map[m.b] = DummyScaler()

sb = CustomScalerBase()

with pytest.raises(
AttributeError,
match="Scaler for unknown.b does not have a method named foo.",
):
sb.call_submodel_scaler_method(
m,
"b",
method="foo",
submodel_scalers=scaler_map,
overwrite=False,
)
assert "Using user-defined Scaler for b." in caplog.text
22 changes: 9 additions & 13 deletions idaes/models/unit_models/equilibrium_reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ def variable_scaling_routine(
"""
# Call scaling methods for sub-models
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_in",
submodel=model.control_volume.properties_in,
method="variable_scaling_routine",
submodel_scalers=submodel_scalers,
overwrite=overwrite,
Expand All @@ -89,15 +88,13 @@ def variable_scaling_routine(
)

self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_out",
submodel=model.control_volume.properties_out,
method="variable_scaling_routine",
submodel_scalers=submodel_scalers,
overwrite=overwrite,
)
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.reactions",
submodel=model.control_volume.reactions,
method="variable_scaling_routine",
submodel_scalers=submodel_scalers,
overwrite=overwrite,
Expand All @@ -121,6 +118,8 @@ def variable_scaling_routine(
for t in model.flowsheet().time:
h_in = 0
for p in model.control_volume.properties_in.phase_list:
# The expression for enthalpy flow might include multiple terms,
# so we will sum over all the terms provided
h_in += sum(
self.get_expression_nominal_values(
model.control_volume.properties_in[
Expand All @@ -130,7 +129,7 @@ def variable_scaling_routine(
)
# Scale for heat is general one order of magnitude less than enthalpy flow
self.set_variable_scaling_factor(
model.control_volume.heat[t], 1 / (0.1 * h_in)
model.control_volume.heat[t], abs(1 / (0.1 * h_in))
)

def constraint_scaling_routine(
Expand All @@ -152,22 +151,19 @@ def constraint_scaling_routine(
"""
# Call scaling methods for sub-models
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_in",
submodel=model.control_volume.properties_in,
method="constraint_scaling_routine",
submodel_scalers=submodel_scalers,
overwrite=overwrite,
)
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_out",
submodel=model.control_volume.properties_out,
method="constraint_scaling_routine",
submodel_scalers=submodel_scalers,
overwrite=overwrite,
)
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.reactions",
submodel=model.control_volume.reactions,
method="constraint_scaling_routine",
submodel_scalers=submodel_scalers,
overwrite=overwrite,
Expand Down
12 changes: 4 additions & 8 deletions idaes/models/unit_models/gibbs_reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,14 @@ def variable_scaling_routine(
# Step 1b: Call Scalers for state blocks
# Inlet properties
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_in",
submodel=model.control_volume.properties_in,
submodel_scalers=submodel_scalers,
method="variable_scaling_routine",
overwrite=overwrite,
)
# Outlet properties
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_out",
submodel=model.control_volume.properties_out,
submodel_scalers=submodel_scalers,
method="variable_scaling_routine",
overwrite=overwrite,
Expand Down Expand Up @@ -152,16 +150,14 @@ def constraint_scaling_routine(
# Step 1: Call Scalers for state blocks
# Inlet properties
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_in",
submodel=model.control_volume.properties_in,
submodel_scalers=submodel_scalers,
method="constraint_scaling_routine",
overwrite=overwrite,
)
# Outlet properties
self.call_submodel_scaler_method(
model=model,
submodel="control_volume.properties_out",
submodel=model.control_volume.properties_out,
submodel_scalers=submodel_scalers,
method="constraint_scaling_routine",
overwrite=overwrite,
Expand Down
4 changes: 2 additions & 2 deletions idaes/models/unit_models/tests/test_gibbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,8 @@ def model(self):
scaler.scale_model(
m.fs.unit,
submodel_scalers={
"control_volume.properties_in": PropertyScaler,
"control_volume.properties_out": PropertyScaler,
m.fs.unit.control_volume.properties_in: PropertyScaler,
m.fs.unit.control_volume.properties_out: PropertyScaler,
},
)

Expand Down

0 comments on commit d931060

Please sign in to comment.