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,

Parameters:
  • component – component to scale

  • 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).

Returns:

None

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

Parameters:
  • attribute (str) – property attribute name

  • index (str) – optional index for indexed properties

Returns:

None

set_default_scaling(attribute, value, index=None)[source]

Set a default scaling factor for a property.

Parameters:
  • attribute (str) – property attribute name

  • value (float) – default scaling factor

  • index (str) – for indexed properties, if this is not provided the default scaling factor applies to all indexed elements where specific indexes are not specified.

Returns:

None

unset_default_scaling(attribute, index=None)[source]

Remove a previously set default value

Parameters:
  • attribute (str) – property attribute name

  • index (str) – optional index for indexed properties

Returns:

None

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) –

exitNode(node, data)[source]#

Callback for pyomo.core.current.StreamBasedExpressionVisitor. This method is called when moving back up the tree in a depth first search.

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

Returns:

list of unscaled variable data objects

Parameters:
  • blk (Block) –

  • descend_into (bool) –

  • include_fixed (bool) –

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

Returns:

list of unscaled constraint data objects

Parameters:
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