PyPSA icon indicating copy to clipboard operation
PyPSA copied to clipboard

Committable extendable

Open FabianHofmann opened this issue 3 months ago • 3 comments

Key Updates

  • Extended the unit-commitment formulation to cover committable + extendable generators/links via a big‑M approach
  • Added a post-processing warning that flags solutions breaching the inferred big‑M bound

Checklist

  • [x] Code changes are sufficiently documented; i.e. new functions contain docstrings and further explanations may be given in doc.
  • [x] Unit tests for new features were added (if applicable).
  • [x] A note for the release notes doc/release_notes.rst of the upcoming release is included.
  • [x] I consent to the release of this PR's code under the MIT license.

FabianHofmann avatar Sep 21 '25 20:09 FabianHofmann

Cool, is 1e6 a good bound or too large?

that's the question. Too low would be bad though... I have a function in the pipeline to infer a big m value from the network, and returns 1e6 only if no reasonable value can be extracted. I'm pushing it and you could have a look?

FabianHofmann avatar Sep 30 '25 10:09 FabianHofmann

I dient take the time to look I to the formulations, but if you are having troubles maybe you can have a look into our formulation (using linopy, so should be easy to understand).

Implementation

@staticmethod
def scaled_bounds_with_state(
    model: Submodel,
    variable: linopy.Variable,
    scaling_variable: linopy.Variable,
    relative_bounds: tuple[TemporalData, TemporalData],
    scaling_bounds: tuple[TemporalData, TemporalData],
    variable_state: linopy.Variable,
    name: str = None,
) -> list[linopy.Constraint]:
    """Constraint a variable by scaling bounds with binary state control.

    variable ∈ {0, [max(ε, lower_relative_bound) * scaling_variable, upper_relative_bound * scaling_variable]}

    Mathematical Formulation (Big-M):
        (variable_state - 1) * M_misc + scaling_variable * rel_lower ≤ variable ≤ scaling_variable * rel_upper
        variable_state * big_m_lower ≤ variable ≤ variable_state * big_m_upper

    Where:
        M_misc = scaling_max * rel_lower
        big_m_upper = scaling_max * rel_upper
        big_m_lower = max(ε, scaling_min * rel_lower)

    Args:
        model: The optimization model instance
        variable: Variable to be bounded
        scaling_variable: Variable that scales the bound factors
        relative_bounds: Tuple of (lower_factor, upper_factor) relative to scaling variable
        scaling_bounds: Tuple of (scaling_min, scaling_max) bounds of the scaling variable
        variable_state: Binary variable for on/off control
        name: Optional name prefix for constraints

    Returns:
        List[linopy.Constraint]: List of constraint objects
    """
    if not isinstance(model, Submodel):
        raise ValueError('BoundingPatterns.scaled_bounds_with_state() can only be used with a Submodel')

    rel_lower, rel_upper = relative_bounds
    scaling_min, scaling_max = scaling_bounds
    name = name or f'{variable.name}'

    big_m_misc = scaling_max * rel_lower

    scaling_lower = model.add_constraints(
        variable >= (variable_state - 1) * big_m_misc + scaling_variable * rel_lower, name=f'{name}|lb2'
    )
    scaling_upper = model.add_constraints(variable <= scaling_variable * rel_upper, name=f'{name}|ub2')

    big_m_upper = rel_upper * scaling_max
    big_m_lower = np.maximum(CONFIG.Modeling.epsilon, rel_lower * scaling_min)

    binary_upper = model.add_constraints(variable_state * big_m_upper >= variable, name=f'{name}|ub1')
    binary_lower = model.add_constraints(variable_state * big_m_lower <= variable, name=f'{name}|lb1')

    return [scaling_lower, scaling_upper, binary_lower, binary_upper]

Usage

BoundingPatterns.scaled_bounds_with_state(
    model=self,
    variable=self.flow_rate,
    scaling_variable=self._investment.size,
    relative_bounds=self.relative_flow_rate_bounds,
    scaling_bounds=(self.element.size.minimum_or_fixed_size, self.element.size.maximum_or_fixed_size),
    variable_state=self.on_off.on,
)

with

Attribute / Variable Meaning / Description
self.flow_rate Power
self._investment.size Installed capacity
self.relative_flow_rate_bounds Availability in percent → flow rate bounds = size × relative_bound
self.element.size.minimum_or_fixed_size, self.element.size.maximum_or_fixed_size Minimum and maximum capacity or size
variable_state Binary indicating the state of the flow rate (on/off)

See flixopt

FBumann avatar Oct 17 '25 09:10 FBumann

As discussed in #1007: This goes in master, but not before #1007 is ready to merge, in which it goes as well, to resolve all conflicts and mainly merge back all the new-opt changes.

Also, better docs are missing, but we can move that in 1007.

@coroa Could you have a final look here?

lkstrp avatar Oct 22 '25 08:10 lkstrp