pypsa-eur icon indicating copy to clipboard operation
pypsa-eur copied to clipboard

Endogenise industry heat supply in steam, medium and high T segments

Open fneum opened this issue 2 years ago • 17 comments

https://github.com/PyPSA/pypsa-eur-sec/pull/316

fneum avatar Mar 06 '23 15:03 fneum

For the split between low, medium and high temperature we can use data from https://www.agora-industry.org/publications/direct-electrification-of-industrial-process-heat, 50% high temperature, 13% low temperature, 37% medium temperature

Screenshot from 2024-07-05 10-34-28

lisazeyen avatar Jul 05 '24 11:07 lisazeyen

For medium temperature heat the biomass route has an efficiency=costs.at["direct firing solid fuels", "efficiency"] which equals 1. The low temperature heat biomass route has an efficiency=costs.at["solid biomass boiler steam", "efficiency"] which equals 0.89. This would mean that biomass to low temperature heat is less efficient than biomass to medium temperature heat which is not making too much sense in my eyes.

toniseibold avatar Jul 19 '24 14:07 toniseibold

For medium temperature heat the biomass route has an efficiency=costs.at["direct firing solid fuels", "efficiency"] which equals 1. The low temperature heat biomass route has an efficiency=costs.at["solid biomass boiler steam", "efficiency"] which equals 0.89. This would mean that biomass to low temperature heat is less efficient than biomass to medium temperature heat which is not making too much sense in my eyes.

I couldn't find a better source. I would suggest to merge it as it is and add an issue. The medium and high temperature split in the best case should be done per country and not based on an EU average.

lisazeyen avatar Aug 05 '24 10:08 lisazeyen

Data cross check with the Agora report in Agora report EU 27 2019 - total process heating 1861 TWh

  • 50% high T ~ 930 TWh high T demand
  • 13% low T ~ 240 TWh
  • 37% medium T ~ 690 TWh space heating 224 TWh should be added to central heat demand

Currently, we have (including GB, Norway, CH)

  • low T 754 TWh (> than Agora 240+224=464 TWh)
  • medium T 100 TWh (< Agora 690 TWh, too small)
  • high T 135 TWh (< Agora 930 TWh, too small)

lisazeyen avatar Aug 05 '24 14:08 lisazeyen

Other sources to look at metis 3 study s5

lisazeyen avatar Aug 06 '24 15:08 lisazeyen

For medium temperature heat the biomass route has an efficiency=costs.at["direct firing solid fuels", "efficiency"] which equals 1. The low temperature heat biomass route has an efficiency=costs.at["solid biomass boiler steam", "efficiency"] which equals 0.89. This would mean that biomass to low temperature heat is less efficient than biomass to medium temperature heat which is not making too much sense in my eyes.

I couldn't find a better source. I would suggest to merge it as it is and add an issue. The medium and high temperature split in the best case should be done per country and not based on an EU average.

I agree with @toniseibold that it is inconsistent, I'd suggest adding it as an issue in technology-data. In DEA there is also a condensing boiler option with higher efficiency, but it does not make much sense for industrial steam which is >150oC.

Added an issue in technology data.

millingermarkus avatar Aug 14 '24 08:08 millingermarkus

Hi, actually I'm looking into using this (thanks as always to those that have put in the effort), but was wondering if this should be combined with a little bit of code in add_existing_baseyear.py for myopic planning? I haven't looked into this in detail but it seems like otherwise you might get a very different process heat energy supply already at the first planning horizon (e.g. 2025) compared to the current situation. And if I understand this correctly, today's situation is already reflected in the industrial demand data. I guess the only data which I don't know if we have readily available are the build-years and phase-out dates of existing firing capacities. If anyone has a sensible suggestion for how to deal with that, I'd be happy to contribute by coding something up.

koen-vg avatar Sep 13 '24 15:09 koen-vg

I will also quickly note that in the context of myopic foresight, I quickly ran into infeasibilities related to the must_run condition. This makes sense: the model might invest, for example, in non-CC firing capacities in one time horizon, leading to forced emissions in the next time horizon, and potentially an infeasibility due to decreasing carbon cap.

Unsurpringly, there are some similarities to https://github.com/PyPSA/pypsa-eur/pull/960. Allowing the model to phase out and replace existing capacities at some cost might make sense (in order to guarantee feasibility at least).

koen-vg avatar Sep 13 '24 15:09 koen-vg

Data cross check with the Agora report in Agora report EU 27 2019 - total process heating 1861 TWh

* 50% high T ~ 930 TWh high T demand

* 13% low T ~ 240 TWh

* 37% medium T ~ 690 TWh
  space heating 224 TWh should be added to central heat demand

Currently, we have (including GB, Norway, CH)

* low T 754 TWh (> than Agora 240+224=464 TWh)

* medium T 100 TWh (< Agora 690 TWh, too small)

* high T  135 TWh (< Agora 930 TWh, too small)

It looks to me like there is a significant discrepancy between the definitions used for low-, medium- and high-temperature heat between the Agora report and this PR. This PR would certainly benefit from clear definitions in terms of where the temperature-boundaries go between low / medium / high.

If you look at the paper industry, for example, pypsa-eur currently (pre this PR) assumes a significant uptake of solid biomass energy demand towards 2050 as almost all heat required by this industry (>200TWh) is assumed to be supplied by biomass by then. In this PR, the biomass-supplied heating demand is translated to low-temperature heat.

The way this Agora report is currently read implies an assumption that low-temperature heat is <100C. However, the same report implies that the vast majority of heat demand for the paper industry is between 100C and 200C (i.e. "medium heat"?).

This confusion between low/medium heat also leads to a presumably unwanted result: the total demand for low-temperature heat goes up significantly as the planning horizon goes from 2020 to 2050 (as far as I can understand, a result of large fractions of heat demand in 2050 being assumed to be supplied by solid biomass initially in build_industry_sector_ratios.py and then being re-classified as low-temperature heat demand), with a corresponding drop in medium- and high-temperature heat demand.

For more credible results, relative demands for low-, medium- and high-temperature should not change so much between 2020 and 2050. Probably what is needed is some work precisely in build_industry_sector_ratios.py in order to directly categorise the heating demand given by IDEES data as low / medium / high (possibly using industry-specific fractions such as given in the Agora report where needed, because IDEES does not distinguish directly between these temperatures as far as I see). A new configuration block under industry: could set exogenous assumptions on which fuels will supply this heat by 2050 (these assumptions being largely implicitly implemented in build_industry_sector_ratios.py currently), whereas endogenous optimisation could choose more freely. Moving the implementation this way could also significantly reduce the difference in code between exogenous and endogenous modelling of industry heat demand, a little like how road transport is handled now.

(Heat demand is already classified directly in build_industry_sector_ratios.py in the case of the "Low-enthalpy heat" category; this demand is later added to urban heat buses as low-temperature heat for industry load; that load should also be merged with low-temperature industry heat in this PR.)

I hope this all makes sense; I got sucked in to the topic a little bit while trying to understand industrial demand for heat and biomass better. It actually started because I noticed that pypsa-eur currently produces an infeasible model for "Low" ENSPRESO biomass scenario, because so much industrial biomass demand is hard-coded.

I don't have all that much time to spend on this, but let me know if some suggestions in the form of code would be appreciated, and I might have a go at it.

koen-vg avatar Sep 20 '24 09:09 koen-vg

(On another note, the supplementary material to https://doi.org/10.1002/ente.202300981 also contains a very nice spreadsheet with process heat distributions (<100C, 100-200C, 200-500C, >1000C) that are "ready to go".)

koen-vg avatar Sep 20 '24 11:09 koen-vg

By the way, I found a couple of errors in the implementation with regards to CO2 management, which you can find in my "fork" of this branch: https://github.com/koen-vg/pypsa-eur/commit/4e43daa9b750106a81e1c6222c6881f2fc881202

koen-vg avatar Oct 10 '24 08:10 koen-vg

Data cross check with the Agora report in Agora report EU 27 2019 - total process heating 1861 TWh

* 50% high T ~ 930 TWh high T demand

* 13% low T ~ 240 TWh

* 37% medium T ~ 690 TWh
  space heating 224 TWh should be added to central heat demand

Currently, we have (including GB, Norway, CH)

* low T 754 TWh (> than Agora 240+224=464 TWh)

* medium T 100 TWh (< Agora 690 TWh, too small)

* high T  135 TWh (< Agora 930 TWh, too small)

It looks to me like there is a significant discrepancy between the definitions used for low-, medium- and high-temperature heat between the Agora report and this PR. This PR would certainly benefit from clear definitions in terms of where the temperature-boundaries go between low / medium / high.

If you look at the paper industry, for example, pypsa-eur currently (pre this PR) assumes a significant uptake of solid biomass energy demand towards 2050 as almost all heat required by this industry (>200TWh) is assumed to be supplied by biomass by then. In this PR, the biomass-supplied heating demand is translated to low-temperature heat.

The way this Agora report is currently read implies an assumption that low-temperature heat is <100C. However, the same report implies that the vast majority of heat demand for the paper industry is between 100C and 200C (i.e. "medium heat"?).

This confusion between low/medium heat also leads to a presumably unwanted result: the total demand for low-temperature heat goes up significantly as the planning horizon goes from 2020 to 2050 (as far as I can understand, a result of large fractions of heat demand in 2050 being assumed to be supplied by solid biomass initially in build_industry_sector_ratios.py and then being re-classified as low-temperature heat demand), with a corresponding drop in medium- and high-temperature heat demand.

For more credible results, relative demands for low-, medium- and high-temperature should not change so much between 2020 and 2050. Probably what is needed is some work precisely in build_industry_sector_ratios.py in order to directly categorise the heating demand given by IDEES data as low / medium / high (possibly using industry-specific fractions such as given in the Agora report where needed, because IDEES does not distinguish directly between these temperatures as far as I see). A new configuration block under industry: could set exogenous assumptions on which fuels will supply this heat by 2050 (these assumptions being largely implicitly implemented in build_industry_sector_ratios.py currently), whereas endogenous optimisation could choose more freely. Moving the implementation this way could also significantly reduce the difference in code between exogenous and endogenous modelling of industry heat demand, a little like how road transport is handled now.

(Heat demand is already classified directly in build_industry_sector_ratios.py in the case of the "Low-enthalpy heat" category; this demand is later added to urban heat buses as low-temperature heat for industry load; that load should also be merged with low-temperature industry heat in this PR.)

I hope this all makes sense; I got sucked in to the topic a little bit while trying to understand industrial demand for heat and biomass better. It actually started because I noticed that pypsa-eur currently produces an infeasible model for "Low" ENSPRESO biomass scenario, because so much industrial biomass demand is hard-coded.

I don't have all that much time to spend on this, but let me know if some suggestions in the form of code would be appreciated, and I might have a go at it.

In this PR, the temperatures start at 100C (originally termed steam, might be good to change back to avoid this confusion), so the Agora low temperature is not included. It was assumed electrified in build_industry_sector_ratios, so that demand exists elsewhere.

The idea was that the current industrial biomass heat demand is dominated by steam generation (say in pulp/paper, food etc), at 100-200C. The exogenously set gas demand was split between medium (~200-500C) and high (>500C). Makes sense to adapt that split accd. to newer data for the current situation. For the future, some of the high temp demand was electrified too, for example EAFs, which is treated outside of this PR as well (so reduces the highT heat demand to be considered here, looking at the Agora data that is quite a chunk).

millingermarkus avatar Aug 18 '25 09:08 millingermarkus

Heya @lisazeyen @fneum, after an initial nudge from @Irieo I took up this PR, and aim to push it through while with the group.

I wanted to make an initial proposition of what that could look like to discuss.

What seems to be there looks quite strong already, but as @lisazeyen pointed out, the total heat demands (when compared to Agora) are maybe a bit too off for comfort (I suspect because only methane demand is interpreted as heat demand), and both @fneum and @koen-vg have suggested actually going into JRC for a more refined integration.

Also, to prevent double counting with methanol, chlorine, ammonia, etc., it seems sensible to implement this already in the build_industrial_ene... scripts.

To get a feeling, I went through build_industry_sector_ratios, and best-guessed all heat demands in terms of their T-band, multiplied them with production, and got this figure which has a satisfying total (compared to Agora), but the shares are still a bit off — work on adjusting this will go on; there is some ambiguity in guessing the temperature level.

image

This is a snippet of what the respective code looks like, just going through the very transparent and clear industry_ratios script

key = "Glass: Annealing - electric"
eff_elec = s_ued[key] / s_fec[key]

sel = ["Glass: Forming"]
df.loc[">500", sector] += s_ued[sel].sum() / eff_elec

sel = ["Glass: Annealing"]
df.loc["200-500", sector] += s_ued[sel].sum() / eff_elec

sel = ["Glass: Finishing processes"]
df.loc["<100", sector] += s_ued[sel].sum() / eff_elec

s_out = idees["out"][9:10]
assert sector in str(s_out.index)

df.loc[:, sector] = df.loc[:, sector] * toe_to_MWh / s_out.values

(This already highlights issues relating to which technology's efficiencies to use)

Summing up, I would

  1. largely leave the existing PR untouched in config and prepare_sector_network.
  2. Add a somewhat detailed disaggregation of temperature band demands probably mostly in build_industrial_energy_demand_per_country_today in the spirit of the build_industry_sector_ratios. This would both make the overall heat demand more accurate, but also add country-level granularity instead of assuming country-uniform T-band width.
  3. retain downstream functionality with regards to low-temp industry heat in urban central heat etc.

We also have project partners in RESILIENT with perfect expertise to check some of the assumptions I would be making along the way, so I could rope them in.

@fneum @lisazeyen @Irieo @koen-vg @millingermarkus I would be very keen to hear your thoughts about the plan, the less I hear the more I will be knocking on Fabian's door on a daily basis, he approved that >:)

LukasFrankenQ avatar Oct 20 '25 12:10 LukasFrankenQ

@LukasFrankenQ that sounds like a good plan! I think the main issue is the discrepancy with the demands in the different heat segments and the confusion with low temp heat (see my comment above), and improving that upstream of this PR is a good idea, since this problem is there also in the current version, and the endogenous industrial heat should be optional. Then one could also endogenise the choice of electrification, which is now hard coded in the build_industry scripts.

Please beware of the steam demand (generally ~160-200C), which is not suitable to connect with urban central heat (at the moment "low temp" in this PR, but originally "steam", and not compatible with the low enthalpy heat demand).

The <100, 100-200, 200-500 and >500 are suitable in my mind, but maybe a higher one is needed too (>1000 or so), and I have also played around with <120C steam which is easy to obtain with current heat pump technology and needed for S-DAC, but my impression is that industrial heat pumps are developing fast, so maybe the latter is redundant. I think some of the Resilient colleagues can inform this decision.

millingermarkus avatar Oct 20 '25 13:10 millingermarkus

Great that someone is picking this up :)

However, I did only later find that the model does not actually use the industry_sector_ratio_{investment_year}.csv file D:

I'm a little confused by this; aren't the sector ratios used to prepare sector-coupled networks? It's a little indirect, but the industrial_demand input to the prepare_sector_network rule (in build_sector.smk) comes from the build_industrial_energy_demand_per_node rule, which in turn uses industry_sector_ratios_{planning_horizons}.csv as an input. It takes some mental effort to parse the workflow structure, that's for sure.

I think the initial work you've done on directly classifying JRC heat energy demand by sector/use into explicit temperature ranges is great.

At a high level, implementation-wise, is the idea to track heat at different temperature bands directly as distinct energy/fuel inputs in the industry sector ratios, along the same lines as electricity, coal, methane, etc.? I.e. where currently the rows of industry_sector_ratios.csv are indexed elec,coal,coke,[...],heat,[...], we could add some entries like heat-low,heat-steam,heat-medium,heat-high or similar (with clearly defined temperature bands). This would then filter down to new heat-related columns in industrial_energy_demand_base_s_{clusters}_{planning_horizon}.csv. The question is then a little bit how the option to endogenise/exogenise is going to look like. One option would be to add various links to supply the various heat ranges in prepare_sector_network regardless of endogenous or exogenous configuration, but then fix those links to set capacities (a little like how shipping fuels and transportation are dealt with I think?).

Do with this feedback what you want!

koen-vg avatar Oct 21 '25 08:10 koen-vg

@koen-vg , thank you, valid points!

You are right about the ratios, I mixed up the scripts, good to see its used and deleted that part of the comment.

Yes, that is the kind of indexing I am aiming for, however to the preserve the exogenous option, it might be best to make a multiindex like [(coal, high-temp), (elec, high-temp), (biomass, medium-temp), ...] to then get either option from a simple groupby..

LukasFrankenQ avatar Oct 22 '25 07:10 LukasFrankenQ

Something like that would certainly be an option. Just another few loose thought/considerations on this matter:

  • When determining current and future fuel mixes for heat supply (at various temperatures), for the purpose of exogenous specification, does it make most sense to differentiate by product/industry, or is it good enough (or indeed more "correct") to use blanket fuel mixes? In the former, it makes sense to track those fuel mixes already in industry_sector_ratios.csv. In the latter case, however, it might make more sense to only track heat demand (by temp) in industry_sector_ratios.csv (i.e. no multiindex) and put the fuel mix in the config (like with shipping fuel mixes) or possibly introduce a separate rule/script to compute exogenous fuel mixes? The latter approach would in a sense resemble an endogenous-first approach where the exogenous setting is more of a add-on.
  • It would make sense to me to consider these choices while keeping in mind how it's going to be implemented in prepare_sector_network, and how the split endogenous/exogenous is going to be implemented there. It might also make sense to consider at the same time how other endogenisation is implemented (e.g. https://github.com/PyPSA/pypsa-eur/pull/960, https://github.com/PyPSA/pypsa-eur/pull/1719). At a high level, it would be nice for the purposes of understandability and clarity to have these things implemented in somewhat similar ways.

Anyway, you are the one who is spending time on this now and is doing the work of implementing it, so don't put too much weight on my thoughts :)

koen-vg avatar Oct 22 '25 08:10 koen-vg