|
10 | 10 | # ___________________________________________________________________________
|
11 | 11 |
|
12 | 12 | from pyomo.common.collections import ComponentMap
|
13 |
| -from pyomo.core.base import Var, Constraint, Objective, _ConstraintData, _ObjectiveData, Suffix, value |
| 13 | +from pyomo.core.base import ( |
| 14 | + Block, Var, Constraint, Objective, _ConstraintData, _ObjectiveData, |
| 15 | + Suffix, value, |
| 16 | +) |
14 | 17 | from pyomo.core.plugins.transform.hierarchy import Transformation
|
15 | 18 | from pyomo.core.base import TransformationFactory
|
16 | 19 | from pyomo.core.expr.current import replace_expressions
|
@@ -81,16 +84,91 @@ def _create_using(self, original_model, **kwds):
|
81 | 84 | self._apply_to(scaled_model, **kwds)
|
82 | 85 | return scaled_model
|
83 | 86 |
|
| 87 | + def _suffix_finder(self, component_data, suffix_name, root=None): |
| 88 | + """Find suffix value for a given component data object in model tree |
| 89 | +
|
| 90 | + Suffixes are searched by traversing the model hierarchy in three passes: |
| 91 | +
|
| 92 | + 1. Search for a Suffix matching the specific component_data, |
| 93 | + starting at the `root` and descending down the tree to |
| 94 | + the component_data. Return the first match found. |
| 95 | + 2. Search for a Suffix matching the component_data's container, |
| 96 | + starting at the `root` and descending down the tree to |
| 97 | + the component_data. Return the first match found. |
| 98 | + 3. Search for a Suffix with key `None`, starting from the |
| 99 | + component_data and working up the tree to the `root`. |
| 100 | + Return the first match found. |
| 101 | + 4. Return None |
| 102 | +
|
| 103 | + Parameters |
| 104 | + ---------- |
| 105 | + component_data: ComponentDataBase |
| 106 | +
|
| 107 | + Component or component data object to find suffix value for. |
| 108 | +
|
| 109 | + suffix_name: str |
| 110 | +
|
| 111 | + Name of Suffix to search for. |
| 112 | +
|
| 113 | + root: BlockData |
| 114 | +
|
| 115 | + When searching up the block hierarchy, stop at this |
| 116 | + BlockData instead of traversing all the way to the |
| 117 | + `component_data.model()` Block. If the `component_data` is |
| 118 | + not in the subtree defined by `root`, then the search will |
| 119 | + proceed up to `component_data.model()`. |
| 120 | +
|
| 121 | + Returns |
| 122 | + ------- |
| 123 | + The value for Suffix associated with component data if found, else None. |
| 124 | +
|
| 125 | + """ |
| 126 | + # Prototype for Suffix finder |
| 127 | + |
| 128 | + # We want to *include* the root (if it is not None), so if |
| 129 | + # it is not None, we want to stop as soon as we get to its |
| 130 | + # parent. |
| 131 | + if root is not None: |
| 132 | + if root.ctype is not Block and not issubclass(root.ctype, Block): |
| 133 | + raise ValueError("_find_suffix: root must be a BlockData " |
| 134 | + f"(found {root.ctype.__name__}: {root})") |
| 135 | + if root.is_indexed(): |
| 136 | + raise ValueError( |
| 137 | + "_find_suffix: root must be a BlockData " |
| 138 | + f"(found {type(root).__name__}: {root})") |
| 139 | + root = root.parent_block() |
| 140 | + # Walk parent tree and search for suffixes |
| 141 | + parent = component_data.parent_block() |
| 142 | + suffixes = [] |
| 143 | + while parent is not root: |
| 144 | + s = parent.component(suffix_name) |
| 145 | + if s is not None and s.ctype is Suffix: |
| 146 | + suffixes.append(s) |
| 147 | + parent = parent.parent_block() |
| 148 | + # Pass 1: look for the component_data, working root to leaf |
| 149 | + for s in reversed(suffixes): |
| 150 | + if component_data in s: |
| 151 | + return s[component_data] |
| 152 | + # Pass 2: look for the component container, working root to leaf |
| 153 | + parent_comp = component_data.parent_component() |
| 154 | + if parent_comp is not component_data: |
| 155 | + for s in reversed(suffixes): |
| 156 | + if parent_comp in s: |
| 157 | + return s[parent_comp] |
| 158 | + # Pass 3: look for None, working leaf to root |
| 159 | + for s in suffixes: |
| 160 | + if None in s: |
| 161 | + return s[None] |
| 162 | + return None |
| 163 | + |
84 | 164 | def _get_float_scaling_factor(self, instance, component_data):
|
85 |
| - scaling_factor = None |
86 |
| - if component_data in instance.scaling_factor: |
87 |
| - scaling_factor = instance.scaling_factor[component_data] |
88 |
| - elif component_data.parent_component() in instance.scaling_factor: |
89 |
| - scaling_factor = instance.scaling_factor[component_data.parent_component()] |
| 165 | + scaling_factor = self._suffix_finder(component_data, "scaling_factor") |
90 | 166 |
|
| 167 | + # If still no scaling factor, return 1.0 |
91 | 168 | if scaling_factor is None:
|
92 | 169 | return 1.0
|
93 | 170 |
|
| 171 | + # Make sure scaling factor is a float |
94 | 172 | try:
|
95 | 173 | scaling_factor = float(scaling_factor)
|
96 | 174 | except ValueError:
|
|
0 commit comments