Scaling Toolbox#
Note
In v2.0, IDAES is beginning to deploy a new suite of scaling tools. This documentation refers to the new tools. For documentation of the older scaling tools, see here.
Importance of Model Scaling#
Creating well scaled models is important for increasing the efficiency and reliability of solvers. However, depending on units of measure and process scale, variables and constraints for process applications are often badly scaled unless efforts are taken to rescale the problem.
The IDAES-IP takes the approach of writing the problem in the most natural form, and then applying model transformations to convert the problem to a more tractable form for the solver. This is achieved through leveraging the Pyomo Scaling Transformation and model Suffixes
. In this way, users may assign scaling factors to any variable, constraint or objective function in the problem in order to convert it from its natural form into a better scaled equivalent. The advantage of using Suffixes
and transformations over more traditional approaches is that this allows scaling to be adjusted as required without needing to rework the model code.
As a general rule of thumb, all variables, component and objective functions in a problem should be scaled to have a magnitude between 1 and 10 in order to ensure maximum robustness of the problem (although this can vary between different solvers). As quantities within process models can vary significantly (even within the same process), achieving good scaling requires a lot of input from the user to determine the expected magnitude of each variable and constraint and thus the appropriate scaling factor, To assist users with this, the IDAES-IP provides a number of utility functions for setting scaling factors and calculating initial guesses for scaling factors from available information. These tools can be found in the idaes.core.util.scaling
module.
Setting Scaling Factors#
Suffixes are used to specify scaling factors for any component in an IDAES model. These suffixes are created when needed by calling the set_scaling_factor()
function. Using the set_scaling_factor()
, get_scaling_factor()
, and unset_scaling_factor()
eliminates the need for users to deal directly with scaling suffixes, and ensures that scaling factors are stored in the correct location.
- idaes.core.util.scaling.set_scaling_factor(c, v, data_objects=True, overwrite=True)[source]#
Set a scaling factor for a model component. This function creates the scaling_factor suffix if needed.
- Parameters:
c – component to supply scaling factor for
v – scaling factor
data_objects – set scaling factors for indexed data objects (default=True)
overwrite – whether to overwrite an existing scaling factor
- Returns:
None
- idaes.core.util.scaling.get_scaling_factor(c, default=None, warning=False, exception=False, hint=None)[source]#
Get a component scale factor.
- Parameters:
c – component
default – value to return if no scale factor exists (default=None)
warning – whether to log a warning if a scaling factor is not found (default=False)
exception – whether to raise an Exception if a scaling factor is not found (default=False)
hint – (str) a string to add to the warning or exception message to help locate the source.
- Returns:
scaling factor (float)
- idaes.core.util.scaling.unset_scaling_factor(c, data_objects=True)[source]#
Delete a component scaling factor.
- Parameters:
c – component
- Returns:
None
For variables, the set_variable_scaling_from_current_value
can be used to automatically set the scaling factor for a given variable (or all variables in a given model) based on the current value of the variable.
- idaes.core.util.scaling.set_variable_scaling_from_current_value(component, descend_into=True, overwrite=False)[source]#
Set scaling factor for variables based on current value. Component argument can be either a Pyomo Var or Block. In case of a Block, this function will attempt to scale all variables in the block using their current value,
Note
If this function is used on a variable which has a current value of 0 or no current value (i.e., var.value == None
) then a warning will be logged and no scaling factor will be set for the variable.
Default Scaling Factors#
Process models are generally large and contain a large number of components, thus it is often infeasible for a user to manually set scaling factors for all components individually. Additionally, getting good initial values in order to use the set_variable_scaling_from_current_value
function requires solving the model which in turn requires initial scaling of the model. In order to provide a starting point for initialization, all IDAES models contain a default_scaling_factors
dict which allows developers and users to assign default scaling factors for any component in a given model.
The following methods are available on all IDAES models which can be used to access and manipulate the default scaling factors:
- class idaes.core.base.process_base.ProcessBlockData(component)[source]
Base class for most IDAES process models and classes.
The primary purpose of this class is to create the local config block to handle arguments provided by the user when constructing an object and to ensure that these arguments are stored in the config block.
Additionally, this class contains a number of methods common to all IDAES classes.
- get_default_scaling(attribute, index=None)[source]
Returns a default scale factor for a property
- set_default_scaling(attribute, value, index=None)[source]
Set a default scaling factor for a property.
These default scaling factors can be applied to a model using the set_scaling_from_default
utility function.
- idaes.core.util.scaling.set_scaling_from_default(component, missing=None, overwrite=False, descend_into=True, components_to_scale=None)[source]#
Set scaling factor(s) for given component from default scaling factor dictionary associated with the parent model.
This function accepts any type of Pyomo component as an input, and will attempt to apply scaling factors to all attached types of components listed in ‘components_to_scale’ argument. A warning will be logged for any component which does not have a default scaling factor assigned.
- Parameters:
component – Pyomo component to apply scaling factors to.
missing (float) – value to use if a component does not have a default scaling factor assigned (default=None).
overwrite (bool) – bool indicating whether to overwrite existing scaling factors (default=False).
descend_into (bool) – bool indicating whether to descend into child Blocks if component is a Block (default=True).
components_to_scale (List of Pyomo component types) – list of Pyomo component types to apply scaling factors to if component is a Block (default=[Var]).
- Returns:
None
Note
Default scaling factors are NOT automatically applied to a model. If set_scaling_from_default
is not called, then the default scaling factors will not be used.
Calculating Constraint and Objective Scaling Factors#
If all variables in a problem have been assigned scaling factors, it is possible to automatically evaluate the terms in all expression (i.e., constraints and objective functions) using the inverse of the variable scaling factors as a nominal value for each variable. This information can then be used to estimate scaling factors for each expression. The IDAES-IP provides a number of utility functions for automatically calculating scaling factors for constraints and objective functions based on different approaches.
- idaes.core.util.scaling.set_constraint_scaling_harmonic_magnitude(component, warning=True, overwrite=False, descend_into=True)[source]#
Set scaling factors for constraints using the harmonic sum of the expected magnitude of additive terms in expression. Scaling factor for constraints will be 1 / sum(1/abs(nominal value)).
- Parameters:
component – a Pyomo component to set constraint scaling factors for.
warning (bool) – bool indicating whether to log a warning if a missing variable scaling factor is found (default=True).
overwrite (bool) – bool indicating whether to overwrite existing scaling factors (default=False).
descend_into (bool) – bool indicating whether function should descend into child Blocks if component is a Pyomo Block (default=True).
- Returns:
None
- idaes.core.util.scaling.set_constraint_scaling_max_magnitude(component, warning=True, overwrite=False, descend_into=True)[source]#
Set scaling factors for constraints using maximum expected magnitude of additive terms in expression. Scaling factor for constraints will be 1 / max(abs(nominal value)).
- Parameters:
component – a Pyomo component to set constraint scaling factors for.
warning (bool) – bool indicating whether to log a warning if a missing variable scaling factor is found (default=True).
overwrite (bool) – bool indicating whether to overwrite existing scaling factors (default=False).
descend_into (bool) – bool indicating whether function should descend into child Blocks if component is a Pyomo Block (default=True).
- Returns:
None
- idaes.core.util.scaling.set_constraint_scaling_min_magnitude(component, warning=True, overwrite=False, descend_into=True)[source]#
Set scaling factors for constraints using minimum expected magnitude of additive terms in expression. Scaling factor for constraints will be 1 / min(abs(nominal value)).
- Parameters:
component – a Pyomo component to set constraint scaling factors for.
warning (bool) – bool indicating whether to log a warning if a missing variable scaling factor is found (default=True).
overwrite (bool) – bool indicating whether to overwrite existing scaling factors (default=False).
descend_into (bool) – bool indicating whether function should descend into child Blocks if component is a Pyomo Block (default=True).
- Returns:
None
These functions make use of the NominalValueExtractionVisitor
class which automatically walks the entire expression tree and determines the nominal value (expected magnitude and sign) for each additive term in the expression. Given an expression of the form \(f(x) = A(x) + B(x) + C(x)\), this class will return a list of the nominal values of \(A(x)\), \(B(x)\) and \(C(x)\) based on the scaling factors assigned to the variables in each sub-expression. These values can then be used to determine the best scaling factor for the overall expression.
- class idaes.core.util.scaling.NominalValueExtractionVisitor(warning=True)[source]#
Expression walker for collecting scaling factors in an expression and determining the expected value of the expression using the scaling factors as nominal inputs.
Returns a list of expected values for each additive term in the expression.
In order to properly assess the expected value of terms within functions, the sign of each term is maintained throughout thus returned values may be negative. Functions using this walker should handle these appropriately.
- Parameters:
warning (bool) –
Identifying Scaling Issues#
A number of utility functions are available to help identify potential scaling issues in a problem.
- idaes.core.util.scaling.report_scaling_issues(blk, ostream=None, prefix='', large=10000.0, small=0.001, zero=1e-10, descend_into=True, include_fixed=False)[source]#
Write a report on potential scaling issues to a stream.
- Parameters:
blk – block to check for scaling issues
ostream (Stream) – stream object to write results to (default=stdout)
prefix (str) – string to prefix output with
large (float) – Magnitude that is considered to be too large
small (float) – Magnitude that is considered to be too small
zero (float) – Magnitude that is considered to be zero, variables with a value of zero are okay, and not reported.
descend_into (bool) – bool indicating whether to check constraints in sub-blocks
include_fixed (bool) – bool indicating whether to include fixed Vars in list
- Returns:
None
- idaes.core.util.scaling.unscaled_variables_generator(blk, descend_into=True, include_fixed=False)[source]#
Generator for unscaled variables
- Parameters:
block –
- Yields:
variables with no scale factor
- idaes.core.util.scaling.list_unscaled_variables(blk, descend_into=True, include_fixed=False)[source]#
Return a list of variables which do not have a scaling factor assigned :param blk: block to check for unscaled variables :param descend_into: bool indicating whether to check variables in sub-blocks :param include_fixed: bool indicating whether to include fixed Vars in list
- idaes.core.util.scaling.badly_scaled_var_generator(blk, large=10000.0, small=0.001, zero=1e-10, descend_into=True, include_fixed=False)[source]#
This provides a rough check for variables with poor scaling based on their current scale factors and values. For each potentially poorly scaled variable it returns the var and its current scaled value.
Note that while this method is a reasonable heuristic for non-negative variables like (absolute) temperature and pressure, molar flows, etc., it can be misleading for variables like enthalpies and fluxes.
- Parameters:
blk – pyomo block
large – Magnitude that is considered to be too large
small – Magnitude that is considered to be too small
zero – Magnitude that is considered to be zero, variables with a value of zero are okay, and not reported.
- Yields:
variable data object, current absolute value of scaled value
- idaes.core.util.scaling.list_badly_scaled_variables(blk, large=10000.0, small=0.001, zero=1e-10, descend_into=True, include_fixed=False)[source]#
Return a list of variables with poor scaling based on their current scale factors and values. For each potentially poorly scaled variable it returns the var and its current scaled value.
Note that while this method is a reasonable heuristic for non-negative variables like (absolute) temperature and pressure, molar flows, etc., it can be misleading for variables like enthalpies and fluxes.
- Parameters:
blk – pyomo block
large (float) – Magnitude that is considered to be too large
small (float) – Magnitude that is considered to be too small
zero (float) – Magnitude that is considered to be zero, variables with a value of zero are okay, and not reported.
descend_into (bool) – bool indicating whether to check constraints in sub-blocks
include_fixed (bool) – bool indicating whether to include fixed Vars in list
- Returns:
list of tuples containing (variable data object, current absolute value of scaled value)
- idaes.core.util.scaling.unscaled_constraints_generator(blk, descend_into=True)[source]#
Generator for unscaled constraints
- Parameters:
block –
- Yields:
constraints with no scale factor
- idaes.core.util.scaling.list_unscaled_constraints(blk, descend_into=True)[source]#
Return a list of constraints which do not have a scaling factor assigned :param blk: block to check for unscaled constraints :param descend_into: bool indicating whether to check constraints in sub-blocks
- idaes.core.util.scaling.extreme_jacobian_entries(m=None, scaled=True, large=10000.0, small=0.0001, zero=1e-10, jac=None, nlp=None)[source]#
Show very large and very small Jacobian entries.
- Parameters:
m – model
scaled – if true use scaled Jacobian
large – >= to this value is considered large
small – <= to this and >= zero is considered small
- Returns:
(list of tuples), Jacobian entry, Constraint, Variable
- idaes.core.util.scaling.extreme_jacobian_rows(m=None, scaled=True, large=10000.0, small=0.0001, jac=None, nlp=None)[source]#
Show very large and very small Jacobian rows. Typically indicates a badly- scaled constraint.
- Parameters:
m – model
scaled – if true use scaled Jacobian
large – >= to this value is considered large
small – <= to this is considered small
- Returns:
(list of tuples), Row norm, Constraint
- idaes.core.util.scaling.extreme_jacobian_columns(m=None, scaled=True, large=10000.0, small=0.0001, jac=None, nlp=None)[source]#
Show very large and very small Jacobian columns. A more reliable indicator of a badly-scaled variable than badly_scaled_var_generator.
- Parameters:
m – model
scaled – if true use scaled Jacobian
large – >= to this value is considered large
small – <= to this is considered small
- Returns:
(list of tuples), Column norm, Variable
- idaes.core.util.scaling.jacobian_cond(m=None, scaled=True, order=None, pinv=False, jac=None)[source]#
Get the condition number of the scaled or unscaled Jacobian matrix of a model.
- Parameters:
m – calculate the condition number of the Jacobian from this model.
scaled – if True use scaled Jacobian, else use unscaled
order – norm order, None = Frobenius, see scipy.sparse.linalg.norm for more
pinv – Use pseudoinverse, works for non-square matrices
jac – (optional) previously calculated Jacobian
- Returns:
(float) Condition number