Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions docs/user-guide/mathematical-notation/features/InvestmentParameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# InvestmentParameters

InvestmentParameters extend [SizingParameters](SizingParameters.md) to model WHEN to invest, not just how much.

!!! info "Relationship to SizingParameters"
- **SizingParameters**: Determines capacity size (how much to build)
- **InvestmentParameters**: Adds timing decisions (when to invest) with fixed lifetime

## Key Concept: Investment Timing

InvestmentParameters tracks:

1. **When** the investment occurs (at most once)
2. **How long** the investment is active (fixed lifetime)
3. **When** decommissioning occurs (lifetime periods after investment)

$$
\sum_{p} x^{invest}_p \leq 1 \quad \text{(invest at most once)}
$$

$$
x^{active}_p = 1 \iff \exists p' \leq p : x^{invest}_{p'} = 1 \land p - p' < L
$$

where $L$ is the lifetime in periods.

---

## Basic Usage

```python
solar_timing = fx.InvestmentParameters(
lifetime=10, # Investment lasts 10 periods
minimum_size=50,
maximum_size=200,
effects_of_size={'costs': 15000},
effects_per_size={'costs': 1200},
)
```
Comment on lines +29 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Basic usage example uses sizing parameter names instead of investment-specific ones

In the “Basic Usage” snippet you configure:

fx.InvestmentParameters(
    lifetime=10,
    minimum_size=50,
    maximum_size=200,
    effects_of_size={'costs': 15000},
    effects_per_size={'costs': 1200},
)

Per the InvestmentParameters API (and the later “Investment-Period Effects” examples), these fields should be effects_of_investment and effects_of_investment_per_size, not the sizing-style effects_of_size/effects_per_size. Otherwise readers will copy an example that does not match the class signature.

Suggestion:

-    effects_of_size={'costs': 15000},
-    effects_per_size={'costs': 1200},
+    effects_of_investment={'costs': 15000},
+    effects_of_investment_per_size={'costs': 1200},
🤖 Prompt for AI Agents
In docs/user-guide/mathematical-notation/features/InvestmentParameters.md around
lines 29-39, the example uses sizing parameter names effects_of_size and
effects_per_size which do not match the InvestmentParameters API; update the
snippet to use effects_of_investment and effects_of_investment_per_size instead
(preserve values and context) so the example matches the class signature and
later examples.


---

## Timing Controls

=== "Allow Investment"

Restrict when investment can occur:

```python
import xarray as xr

fx.InvestmentParameters(
lifetime=10,
allow_investment=xr.DataArray(
[1, 1, 1, 0, 0], # Only allow in first 3 periods
coords=[('period', [2020, 2025, 2030, 2035, 2040])],
),
)
```

=== "Force Investment"

Force investment in a specific period:

```python
fx.InvestmentParameters(
lifetime=10,
force_investment=xr.DataArray(
[0, 0, 1, 0, 0], # Force in 2030
coords=[('period', [2020, 2025, 2030, 2035, 2040])],
),
)
```

=== "Previous Lifetime"

Model existing capacity from before the optimization horizon:

```python
fx.InvestmentParameters(
lifetime=10,
previous_lifetime=3, # 3 periods of lifetime remaining
)
```

---

## Investment-Period Effects

Effects that depend on WHEN the investment is made (technology learning curves, time-varying subsidies):

=== "Fixed by Investment Period"

Effects that vary by investment timing:

```python
# Cost decreases over time (learning curve)
fx.InvestmentParameters(
lifetime=10,
effects_of_investment={
'costs': xr.DataArray(
[50000, 45000, 40000, 35000, 30000],
coords=[('period', [2020, 2025, 2030, 2035, 2040])],
)
},
)
```

=== "Per-Size by Investment Period"

Size-dependent effects that vary by investment timing:

```python
# Cost per kW decreases (technology improvement)
fx.InvestmentParameters(
lifetime=10,
effects_of_investment_per_size={
'costs': xr.DataArray(
[1500, 1200, 1000, 900, 800], # €/kW over time
coords=[('period', [2020, 2025, 2030, 2035, 2040])],
)
},
)
```

---

## Variables Created

| Variable | Description |
|----------|-------------|
| `size` | Capacity size |
| `invested` | Binary: is investment currently active? |
| `investment_occurs` | Binary: does investment happen this period? |
| `decommissioning_occurs` | Binary: does decommissioning happen this period? |
| `size_increase` | Size increase when investing |
| `size_decrease` | Size decrease when decommissioning |

---

## Reference

| Symbol | Type | Description |
|--------|------|-------------|
| $P$ | $\mathbb{R}_{\geq 0}$ | Investment size (capacity) |
| $x^{invest}_p$ | $\{0, 1\}$ | Investment occurs in period $p$ |
| $x^{decom}_p$ | $\{0, 1\}$ | Decommissioning occurs in period $p$ |
| $x^{active}_p$ | $\{0, 1\}$ | Investment is active in period $p$ |
| $L$ | $\mathbb{Z}_{>0}$ | Lifetime in periods |

**Classes:** [`InvestmentParameters`][flixopt.interface.InvestmentParameters], [`InvestmentModel`][flixopt.features.InvestmentModel]
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# InvestParameters
# SizingParameters

InvestParameters make capacity a decision variable — should we build this? How big?
SizingParameters make capacity a decision variable — should we build this? How big?

!!! note "Naming Change"
`SizingParameters` replaces the deprecated `InvestParameters`.
For investment timing with fixed lifetime (when to invest), see [InvestmentParameters](InvestmentParameters.md).

## Basic: Size as Variable

Expand All @@ -11,26 +15,26 @@ $$
```python
battery = fx.Storage(
...,
capacity_in_flow_hours=fx.InvestParameters(
capacity_in_flow_hours=fx.SizingParameters(
minimum_size=10,
maximum_size=1000,
specific_effects={'costs': 600}, # €600/kWh
effects_per_size={'costs': 600}, # €600/kWh
),
)
```

---

## Investment Modes
## Sizing Modes

By default, investment is **optional** — the optimizer can choose $P = 0$ (don't invest).
By default, sizing is **optional** — the optimizer can choose $P = 0$ (don't build).

=== "Continuous"

Choose size within range (or zero):

```python
fx.InvestParameters(
fx.SizingParameters(
minimum_size=10,
maximum_size=1000,
)
Expand All @@ -42,18 +46,18 @@ By default, investment is **optional** — the optimizer can choose $P = 0$ (don
Fixed size or nothing:

```python
fx.InvestParameters(
fx.SizingParameters(
fixed_size=100, # 100 kW or 0
)
# → P ∈ {0, 100}
```

=== "Mandatory"

Force investment with `mandatory=True` — zero not allowed:
Force sizing with `mandatory=True` — zero not allowed:

```python
fx.InvestParameters(
fx.SizingParameters(
minimum_size=50,
maximum_size=200,
mandatory=True,
Expand All @@ -63,7 +67,7 @@ By default, investment is **optional** — the optimizer can choose $P = 0$ (don

---

## Investment Effects
## Sizing Effects

=== "Per-Size Cost"

Expand All @@ -72,31 +76,31 @@ By default, investment is **optional** — the optimizer can choose $P = 0$ (don
$E = P \cdot c_{spec}$

```python
fx.InvestParameters(
specific_effects={'costs': 1200}, # €1200/kW
fx.SizingParameters(
effects_per_size={'costs': 1200}, # €1200/kW
)
```

=== "Fixed Cost"

One-time cost if investing:
One-time cost if sizing:

$E = s_{inv} \cdot c_{fix}$
$E = s_{sized} \cdot c_{fix}$

```python
fx.InvestParameters(
effects_of_investment={'costs': 25000}, # €25k
fx.SizingParameters(
effects_of_size={'costs': 25000}, # €25k
)
```

=== "Retirement Cost"

Cost if NOT investing:
Cost if NOT sizing (demolition, opportunity cost):

$E = (1 - s_{inv}) \cdot c_{ret}$
$E = (1 - s_{sized}) \cdot c_{ret}$

```python
fx.InvestParameters(
fx.SizingParameters(
effects_of_retirement={'costs': 8000}, # Demolition
)
```
Expand All @@ -108,8 +112,8 @@ By default, investment is **optional** — the optimizer can choose $P = 0$ (don
$E = f_{piecewise}(P)$

```python
fx.InvestParameters(
piecewise_effects_of_investment=fx.PiecewiseEffects(
fx.SizingParameters(
piecewise_effects_per_size=fx.PiecewiseEffects(
piecewise_origin=fx.Piecewise([
fx.Piece(0, 100),
fx.Piece(100, 500),
Expand All @@ -132,12 +136,12 @@ By default, investment is **optional** — the optimizer can choose $P = 0$ (don

| Symbol | Type | Description |
|--------|------|-------------|
| $P$ | $\mathbb{R}_{\geq 0}$ | Investment size (capacity) |
| $s_{inv}$ | $\{0, 1\}$ | Binary investment decision (0=no, 1=yes) |
| $P$ | $\mathbb{R}_{\geq 0}$ | Size (capacity) |
| $s_{sized}$ | $\{0, 1\}$ | Binary sizing decision (0=no, 1=yes) |
| $P^{min}$ | $\mathbb{R}_{\geq 0}$ | Minimum size (`minimum_size`) |
| $P^{max}$ | $\mathbb{R}_{\geq 0}$ | Maximum size (`maximum_size`) |
| $c_{spec}$ | $\mathbb{R}$ | Per-size effect (`effects_of_investment_per_size`) |
| $c_{fix}$ | $\mathbb{R}$ | Fixed effect (`effects_of_investment`) |
| $c_{spec}$ | $\mathbb{R}$ | Per-size effect (`effects_per_size`) |
| $c_{fix}$ | $\mathbb{R}$ | Fixed effect (`effects_of_size`) |
| $c_{ret}$ | $\mathbb{R}$ | Retirement effect (`effects_of_retirement`) |

**Classes:** [`InvestParameters`][flixopt.interface.InvestParameters], [`InvestmentModel`][flixopt.features.InvestmentModel]
**Classes:** [`SizingParameters`][flixopt.interface.SizingParameters], [`SizingModel`][flixopt.features.SizingModel]
13 changes: 12 additions & 1 deletion flixopt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@
from .effects import PENALTY_EFFECT_LABEL, Effect
from .elements import Bus, Flow
from .flow_system import FlowSystem
from .interface import InvestParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects, StatusParameters
from .interface import (
InvestmentParameters,
InvestParameters,
Piece,
Piecewise,
PiecewiseConversion,
PiecewiseEffects,
SizingParameters,
StatusParameters,
)
from .optimization import ClusteredOptimization, Optimization, SegmentedOptimization

__all__ = [
Expand All @@ -49,6 +58,8 @@
'ClusteredOptimization',
'SegmentedOptimization',
'InvestParameters',
'SizingParameters',
'InvestmentParameters',
'StatusParameters',
'Piece',
'Piecewise',
Expand Down
Loading
Loading