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 pandas as pd
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¶
# One winter week
timesteps = pd.date_range('2024-01-22', periods=168, freq='h')
hours = np.arange(168)
hour_of_day = hours % 24
# Outdoor temperature: daily cycle with cold nights
temp_base = 2 # Average temp in °C
temp_amplitude = 5 # Daily variation
outdoor_temp = temp_base + temp_amplitude * np.sin((hour_of_day - 6) * np.pi / 12)
# Add day-to-day variation for realism
np.random.seed(789)
daily_offset = np.repeat(np.random.uniform(-3, 3, 7), 24)
outdoor_temp = outdoor_temp + daily_offset
# Heat demand: inversely related to outdoor temp (higher demand when colder)
heat_demand = 200 - 8 * outdoor_temp
heat_demand = np.clip(heat_demand, 100, 300)
# Visualize input profiles
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}),
}
)
df = profiles.to_dataframe().reset_index().melt(id_vars='time', var_name='variable', value_name='value')
fig = px.line(df, x='time', y='value', facet_col='variable', height=300)
fig.update_yaxes(matches=None, showticklabels=True)
fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1]))
fig
Calculate Time-Varying COP¶
The COP depends on outdoor temperature. We use 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}}}$$
where temperatures are in Kelvin.
# COP calculation
T_supply = 45 + 273.15 # Supply temperature 45°C in Kelvin
T_source = outdoor_temp + 273.15 # Outdoor temp in Kelvin
carnot_cop = T_supply / (T_supply - T_source)
real_cop = 0.45 * carnot_cop
real_cop = np.clip(real_cop, 2.0, 5.0) # Physical limits
# Visualize COP vs temperature relationship
px.scatter(
x=outdoor_temp,
y=real_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': real_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.statistics.plot.balance('Heat')
flow_system.statistics.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