Configuration#
GreenBubble is configured via three YAML files in the config/ folder.
Each file has a committed *.default.yaml base and an optional
*.yaml user-override that is merged on top at runtime.
See Running the Model with Snakemake for the full override workflow.
Default file |
Purpose |
|---|---|
|
Main settings: demands, technology flags, economics, solver, stochastic scenarios |
|
Per-technology capacity bounds, ramp limits, brownfield initial capacities,
and external market options ( |
|
Which components to export and plot after optimisation |
config.default.yaml#
General#
run_name: H2_meth_dmd_DK
Short label appended to the output folder name. Keep it concise.
CO2_cost: 100 # €/t — CO₂ tax on fossil emissions
CO2_cost_ref_year: 0 # €/t — CO₂ cost already embedded in energy prices
En_price_year: 2023 # year used to download electricity/NG prices and CO₂ intensities
latitude: 56.566 # Skive, Denmark (used for renewable CF retrieval)
longitude: 9.033
max_RE_to_grid: 0.1 # max share of renewable output that can be exported to grid
outputs_folder: outputs/single_analysis
targets#
Controls whether the model is demand-driven or price-driven.
targets:
driver: 'demand' # 'demand' | 'price'
demand_H2: 0 # MWh_H2/y — annual H₂ demand (demand mode)
demand_CH4: 300000 # MWh_CH4/y — annual biomethane demand
demand_meoh: 4000 # MWh_MeOH/y — annual methanol demand
price_H2: 90 # €/MWh — H₂ price target (price mode)
price_bioCH4: 95 # €/MWh — 'NG_based' derives from NG price + CO₂ tax
price_meoh: 110 # €/MWh — methanol price target
In demand mode (driver: 'demand'), annual production targets are fixed constraints.
In price mode (driver: 'price'), demands become upper bounds and the model
maximises revenue at the given prices.
Each product also has a demand shape and optional flexibility store:
targets:
CH4_demand_mode: flat # flat | profile | bins_flat | bins_profile
CH4_bins: 1 # number of equal bins (bins_* modes only)
CH4_flexibility: 0.00 # fraction of annual demand used as store e_nom_max
CH4_profile: null # path to CSV seasonal profile; null = built-in NG_DK
H2_demand_mode: profile
H2_bins: 12
H2_flexibility: 0.1
H2_profile: data/common/NG_demand_DK_profile.csv
MeOH_demand_mode: bins_profile
MeOH_bins: 2
MeOH_flexibility: 0.0
MeOH_profile: data/common/NG_demand_DK_profile.csv
demand_store_buffer: 0.5 # extra headroom for bins_profile stores
See Setting Product Demands for a full explanation of demand modes, flexibility stores, and seasonal profiles.
n_flags — technology activation#
Boolean switches that include or exclude technology groups from the network.
Setting a flag to false removes the corresponding components entirely.
n_flags:
biogas: true # biogas plant and all downstream biogas routes
central_heat: true # central heat supply and district heating connection
renewables: true # onshore wind + solar PV with capacity expansion
electrolysis: true # alkaline electrolysis
meoh: true # methanol synthesis (CO₂ hydrogenation)
methanation: true # catalytic and biological methanation
symbiosis: true # all internal energy/material exchange links
storage: true # all storage technologies (from n_config.default.yaml)
print: true # save SVG of pre-optimisation network
export: false # export pre-optimisation network to .nc
Note
The n_flags combination is encoded into the output folder name,
making each run uniquely identifiable.
n_flags_opt — output control#
Controls which post-solve artefacts are generated.
n_flags_opt:
print: true # save SVG of the optimal network topology
export: true # export the solved network to a .nc file
plot: true # run the full post-processing plot suite
stochastic#
Enables multi-scenario stochastic optimisation.
stochastic:
stochastic: false # true → stochastic LP across all scenario years
scenarios:
'2022': 0.05 # year: probability (must sum to 1)
'2023': 0.25
'2024': 0.35
'2025': 0.35
CO2_cost_s: # per-scenario CO₂ cost (€/t)
'2022': 100
...
CO2_cost_ref_year_s: # per-scenario reference-year CO₂ cost
'2022': 0
...
EVPI: true # compute Expected Value of Perfect Information
When stochastic: true, input data is downloaded for all scenario years
in parallel (one Snakemake job per year) before building the coupled network.
EVPI: true adds one deterministic solve per scenario to compute the EVPI;
automatically disabled when stochastic: false.
clustering#
Reduces the number of snapshots by resampling hourly input data to a coarser time interval before the network is built.
clustering:
temporal:
resolution: false # false | "4h" | "8h" | "24h" | ...
false (default) keeps native 1-hour resolution.
Any pandas offset string that represents an interval ≥ 1 hour is accepted
("4h", "8h", "24h" are the most common choices).
Sub-hourly strings raise NotImplementedError.
Effect on snapshot count:
resolution |
snapshots |
typical speed-up |
|---|---|---|
|
8 760 |
baseline |
|
2 190 |
~4× |
|
1 095 |
~8× |
|
365 |
~20× |
Resampling rules
Prices, capacity factors, and demands →
mean()over the interval.Store upper bounds (
e_max_pu) →min()over the interval (conservative).Store lower bounds (
e_min_pu) →max()over the interval (conservative).Snapshot weightings →
sum()so annual energy totals are preserved.
Incompatibilities
rolling_horizon.enabled: true— resampling is skipped with a warning; the RH solver operates on the full hourly network provided vianetwork_path.stochastic.stochastic: true— allowed but issues a warning; each scenario is resampled independently, so inter-scenario sub-period correlations are lost.
See Temporal Resolution for worked examples and advice on choosing a resolution.
rolling_horizon#
Dispatch-only solve on a pre-existing fixed-capacity network.
When enabled, the capacity-expansion solve_network rule is bypassed entirely.
rolling_horizon:
enabled: false
horizon: 168 # window size in hours (e.g. 168 = 1 week)
overlap: 72 # overlap in hours between consecutive windows
network_path: '' # REQUIRED — path to a solved .nc network
rh_year: null # null = same as En_price_year; or an integer year
network_path is required when enabled: true.
Setting rh_year to a different year than En_price_year replaces all
time-varying inputs (prices, capacity factors) with data from that year.
See Rolling Horizon Dispatch Optimisation for the full workflow, cross-year analysis, committable dispatch, and cost comparison outputs.
optimization#
optimization:
solver: 'gurobi' # 'gurobi' | 'highs'
solver_profile: 'gurobi-barrier-fast' # preset from scripts/solver_profiles.py
collect_all_duals: true # save dual variables for shadow price analysis
return_model: true # return Linopy model object after solving
overrides: null # optional raw solver parameters (dict)
zero_threshold_MW: 0.01 # MW — components built below this are zeroed out
Solver profiles are defined in scripts/solver_profiles.py.
Common Gurobi profiles: gurobi-barrier-fast, gurobi-simplex.
zero_threshold_MW removes solver-noise artefacts: any extendable component
whose p_nom_opt (or e_nom_opt) is strictly below this value is treated
as “not built” — its optimal capacity and result time series are zeroed before
export.
Economics#
year_investment: 2030 # target year for new-capacity technology costs
amortization_period: null # null = use each technology's technical lifetime
discount_rate: 0.07 # real discount rate (constant EUR, excludes inflation)
EUR_to_DKK: 7.46 # EUR → DKK exchange rate
USD_to_EUR: 0.85 # USD → EUR exchange rate
See Economic Assumptions for a full explanation of how these parameters interact with technology costs, brownfield initial conditions, and the annuity formula.
tariffs_dict#
Danish electricity grid tariffs (€/MWh). Applied to all grid imports/exports.
tariffs_dict:
el_transmission_tariff: 9.92 # TSO tariff (Energinet)
el_system_tariff: 6.84
el_afgift: 45 # state electricity tax (øre/kWh)
el_net_tariff_low: 2 # DSO tariff — off-peak
el_net_tariff_high: 6 # DSO tariff — shoulder
el_net_tariff_peak: 12 # DSO tariff — peak
el_tariff_sell: 1.4 # tariff on electricity export
NG_dso_tariff: 1.8 # natural gas DSO tariff
NG_tso_tariff: 0.01 # natural gas TSO tariff
rfnbos_dict#
Controls RFNBO (Renewable Fuels of Non-Biological Origin) compliance constraints.
rfnbos_dict:
limit: 'price' # 'price' | 'emissions' | 'unlimited' | 'disconnected'
price_threshold: 20 # €/MWh — max electricity price for RFNBO compliance
emission_threshold: 0.0648 # tCO₂/MWh — max grid intensity for RFNBO compliance
n_config.default.yaml#
Per-technology configuration for greenfield/brownfield optimisation. Each entry sets the initial installed capacity, expansion allowance, cost factor, and operational constraints (ramp limits, minimum load) for one technology group.
Key investment parameters: initial capacity, expansion, cost factor,
construction_year, remaining_investment_fraction, max capacity.
The options: section at the bottom of this file controls external market
connections: biomass purchase markets, district heating sales, biochar and CO₂
sequestration credits, and electrical transformer sizing.
See Brownfield initial conditions for a full explanation of how brownfield parameters are combined to compute residual annual capital charges.
Greenfield and Brownfield configuration#
Three parameters jointly determine the investment mode for each technology:
|
|
|
Result |
|---|---|---|---|
|
|
any |
Technology absent. Not added to the model. |
|
|
|
Pure greenfield. Only a new expandable component is built. |
|
|
|
Pure brownfield (sunk cost). Existing capacity is fixed; no CAPEX charged. |
|
|
|
Brownfield with residual CAPEX. Existing capacity is fixed; annual charge = |
|
|
|
Mixed — existing free, expandable. Existing capacity at zero capital cost; additional capacity can be built at full cost. |
|
|
|
Mixed — existing with residual CAPEX, expandable. |
Parameter meanings
cost factorMultiplier applied to the capital cost of new capacity (
expansion=truecomponent). Used for cost sensitivity analysis:1.0= tech-data value,0.5= 50% cost reduction scenario.construction_yearYear the existing plant was built. Used to look up the investment cost at the actual build year (technology costs differ between years due to learning curves).
nulldefaults toyear_investment - 10, capped at 2020.remaining_investment_fractionFraction of
investment(construction_year)still to be financially recovered.0= fully amortised / sunk cost (default);1= the full original investment is still outstanding.
PyPSA implementation
Internally, an EXI_<tech> component with remaining_investment_fraction > 0 is built as
p_nom_extendable=True with p_nom_min = p_nom_max = initial_capacity.
This forces the LP variable to its fixed value, moving the capital cost into
n.objective_constant. n.statistics.capex() correctly accounts for the
residual charge in LCOP and TSC outputs.
Example — existing biogas plant, 40 % of original investment still outstanding, built in 2022, no new capacity allowed:
# config/n_config.yaml (user override)
biogas:
initial capacity: 5.0 # MW CH4
expansion: false
construction_year: 2022
remaining_investment_fraction: 0.4
Committable components#
The committable column in n_config enables unit commitment (on/off
binary dispatch) for a technology.
biogas engine:
committable: true # activate unit commitment (dispatch only)
min load: 0.4 # minimum load fraction when online (p_min_pu)
Current GreenBubble behaviour
In prepare_network.py, committable is only activated when
expansion: false — i.e. for fixed-capacity brownfield assets. When a
technology is extendable (expansion: true), the capacity expansion solve
runs as a pure LP regardless of the committable flag.
This is a deliberate choice to keep the investment problem fast and compatible with stochastic mode. PyPSA itself does support committable + extendable simultaneously in deterministic capacity expansion via a big-M MILP formulation — see the PyPSA committable-extendable example.
Committable in rolling horizon
After fix_capacities() fixes all p_nom, enable_committable_for_rh
re-activates committable=True for any technology that has it set in
n_config, so the dispatch-only RH solve can include unit commitment.
Stochastic mode
committable: true is incompatible with stochastic mode — the multi-scenario
LP requires a pure LP (no binary variables). See Stochastic Optimisation.
plots_config.default.yaml#
Note
Full documentation coming soon. See inline comments in config/plots_config.default.yaml.
Defines which network components are extracted and plotted after optimisation, including capacity thresholds and the list of internal buses for shadow price plots.