Time-Varying Parameters¶
Model equipment with efficiency that changes based on external conditions.
This notebook covers:
- Time-varying conversion factors: Efficiency depends on external conditions
- Temperature-dependent COP: Heat pump performance varies with weather
- Practical application: Using arrays in conversion factor definitions
Setup¶
import numpy as np
import plotly.express as px
import xarray as xr
import flixopt as fx
fx.CONFIG.notebook()
flixopt.config.CONFIG
The Problem: Variable Heat Pump Efficiency¶
A heat pump's COP (Coefficient of Performance) depends on the temperature difference between source and sink:
- Mild weather (10°C outside): COP ≈ 4.5 (1 kWh electricity → 4.5 kWh heat)
- Cold weather (-5°C outside): COP ≈ 2.5 (1 kWh electricity → 2.5 kWh heat)
This time-varying relationship can be modeled directly using arrays in the conversion factors.
When to Use This Approach¶
Use time-varying conversion factors when:
- Efficiency depends on external conditions (temperature, solar irradiance, humidity)
- The relationship is independent of the load level
- You have measured or forecast data for the efficiency profile
Define Time Series Data¶
from data.tutorial_data import get_time_varying_data
data = get_time_varying_data()
timesteps = data['timesteps']
outdoor_temp = data['outdoor_temp']
heat_demand = data['heat_demand']
cop = data['cop']
# Visualize input profiles with plotly
profiles = xr.Dataset(
{
'Outdoor Temp [°C]': xr.DataArray(outdoor_temp, dims=['time'], coords={'time': timesteps}),
'Heat Demand [kW]': xr.DataArray(heat_demand, dims=['time'], coords={'time': timesteps}),
}
)
profiles.plotly.line(x='time', title='Temperature and Heat Demand Profiles', height=300)
Time-Varying COP¶
The COP is pre-calculated based on outdoor temperature using a simplified Carnot-based formula:
$$\text{COP}_{\text{real}} \approx 0.45 \times \text{COP}_{\text{Carnot}} = 0.45 \times \frac{T_{\text{supply}}}{T_{\text{supply}} - T_{\text{source}}}$$
Let's visualize the relationship:
# Visualize COP vs temperature relationship
px.scatter(
x=outdoor_temp,
y=cop,
title='Heat Pump COP vs Outdoor Temperature',
labels={'x': 'Outdoor Temperature [°C]', 'y': 'COP'},
opacity=0.5,
)
Build the Model¶
The key is passing the COP array directly to conversion_factors. The equation becomes:
$$\text{Elec} \times \text{COP}(t) = \text{Heat} \times 1$$
where COP(t) varies at each timestep.
flow_system = fx.FlowSystem(timesteps)
flow_system.add_carriers(
fx.Carrier('electricity', '#f1c40f', 'kW'),
fx.Carrier('heat', '#e74c3c', 'kW'),
)
flow_system.add_elements(
# Buses
fx.Bus('Electricity', carrier='electricity'),
fx.Bus('Heat', carrier='heat'),
# Effect for cost tracking
fx.Effect('costs', '€', 'Operating Costs', is_standard=True, is_objective=True),
# Grid electricity source
fx.Source('Grid', outputs=[fx.Flow('Elec', bus='Electricity', size=500, effects_per_flow_hour=0.30)]),
# Heat pump with TIME-VARYING COP
fx.LinearConverter(
'HeatPump',
inputs=[fx.Flow('Elec', bus='Electricity', size=150)],
outputs=[fx.Flow('Heat', bus='Heat', size=500)],
conversion_factors=[{'Elec': cop, 'Heat': 1}], # <-- Array for time-varying COP
),
# Heat demand
fx.Sink('Building', inputs=[fx.Flow('Heat', bus='Heat', size=1, fixed_relative_profile=heat_demand)]),
)
flow_system.optimize(fx.solvers.HighsSolver());
Analyze Results¶
flow_system.stats.plot.balance('Heat')
flow_system.stats.plot.balance('Electricity')
# Compare electricity consumption vs heat output using xarray for alignment
# Create dataset with solution and input data - xarray auto-aligns by time coordinate
comparison = xr.Dataset(
{
'elec_consumption': flow_system.solution['HeatPump(Elec)|flow_rate'],
'heat_output': flow_system.solution['HeatPump(Heat)|flow_rate'],
'outdoor_temp': xr.DataArray(outdoor_temp, dims=['time'], coords={'time': timesteps}),
}
)
# Calculate effective COP at each timestep
comparison['effective_cop'] = xr.where(
comparison['elec_consumption'] > 0.1, comparison['heat_output'] / comparison['elec_consumption'], np.nan
)
px.scatter(
x=comparison['outdoor_temp'].values,
y=comparison['effective_cop'].values,
title='Actual Operating COP vs Outdoor Temperature',
labels={'x': 'Outdoor Temperature [°C]', 'y': 'Operating COP'},
)
Key Concepts¶
Conversion Factor Syntax¶
The conversion_factors parameter accepts a list of dictionaries where values can be:
- Scalars: Constant efficiency (e.g.,
{'Fuel': 1, 'Heat': 0.9}) - Arrays: Time-varying efficiency (e.g.,
{'Elec': cop_array, 'Heat': 1}) - TimeSeriesData: For more complex data with metadata
fx.LinearConverter(
'HeatPump',
inputs=[fx.Flow('Elec', bus='Electricity', size=150)],
outputs=[fx.Flow('Heat', bus='Heat', size=500)],
conversion_factors=[{'Elec': cop_array, 'Heat': 1}], # Time-varying
)
Physical Interpretation¶
The conversion equation at each timestep: $$\text{Input}_1 \times \text{factor}_1(t) + \text{Input}_2 \times \text{factor}_2(t) + ... = 0$$
For a heat pump: Elec * COP(t) - Heat * 1 = 0 → Heat = Elec * COP(t)
Common Use Cases¶
| Equipment | Varying Parameter | External Driver |
|---|---|---|
| Heat pump | COP | Outdoor temperature |
| Solar PV | Capacity factor | Solar irradiance |
| Cooling tower | Efficiency | Wet bulb temperature |
| Gas turbine | Heat rate | Ambient temperature |
Summary¶
You learned how to:
- Model time-varying efficiency using arrays in conversion factors
- Calculate temperature-dependent COP for heat pumps
- Analyze the resulting operation with varying efficiency
When to Use This vs Other Approaches¶
| Approach | Use When | Example |
|---|---|---|
| Time-varying factors (this notebook) | Efficiency varies with external conditions | Heat pump COP vs temperature |
| PiecewiseConversion | Efficiency varies with load level | Gas engine efficiency curve |
| PiecewiseEffects | Costs vary non-linearly with size | Economies of scale |
Next Steps¶
- 06b-piecewise-conversion: Load-dependent efficiency curves
- 06c-piecewise-effects: Non-linear cost functions