diff --git a/dev/Untitled1.ipynb b/dev/Untitled1.ipynb index e80d539d..d6e9667b 100644 --- a/dev/Untitled1.ipynb +++ b/dev/Untitled1.ipynb @@ -2,63 +2,32 @@ "cells": [ { "cell_type": "code", + "execution_count": 1, "id": "5018b7ee-0169-49d7-9455-2f1aea562e9e", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-12T17:04:00.715857Z", - "start_time": "2024-07-12T17:03:59.205224Z" - } - }, + "metadata": {}, + "outputs": [], "source": [ "from premise import *\n", "from datapackage import Package\n", "import bw2io as bw" - ], - "outputs": [], - "execution_count": 1 + ] }, { "cell_type": "code", + "execution_count": 2, "id": "0ca27c06-948c-4584-bebe-d505cb72d4f9", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-12T17:04:01.762440Z", - "start_time": "2024-07-12T17:04:01.531095Z" - } - }, + "metadata": {}, + "outputs": [], "source": [ "import bw2data\n", "bw2data.projects.set_current(\"ei310\")" - ], - "outputs": [], - "execution_count": 2 + ] }, { "cell_type": "code", + "execution_count": 3, "id": "bee86950-ac96-49e0-8a9c-43920ae26096", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-12T17:04:06.171795Z", - "start_time": "2024-07-12T17:04:02.324492Z" - } - }, - "source": [ - "ndb = NewDatabase(\n", - " scenarios=[\n", - " #{\"model\":\"remind\", \"pathway\":\"SSP2-Base\", \"year\":2050},\n", - " #{\"model\":\"remind\", \"pathway\":\"SSP2-Base\", \"year\":2010},\n", - " #{\"model\":\"image\", \"pathway\":\"SSP2-Base\", \"year\":2020},\n", - " #{\"model\":\"remind\", \"pathway\":\"SSP2-RCP26\", \"year\":2050},\n", - " #{\"model\":\"remind\", \"pathway\":\"SSP2-Base\", \"year\":2040},\n", - " #{\"model\":\"image\", \"pathway\":\"SSP2-RCP19\", \"year\":2050},\n", - " {\"model\":\"image\", \"pathway\":\"SSP2-RCP26\", \"year\":2050},\n", - " ],\n", - " source_db=\"ecoinvent-3.10-cutoff\", # <-- name of the database in the BW2 project. Must be a string.\n", - " source_version=\"3.10\", # <-- version of ecoinvent. Can be \"3.5\", \"3.6\", \"3.7\" or \"3.8\". Must be a string.\n", - " key=\"tUePmX_S5B8ieZkkM7WUU2CnO8SmShwmAeWK9x2rTFo=\",\n", - " biosphere_name=\"ecoinvent-3.10-biosphere\"\n", - ")" - ], + "metadata": {}, "outputs": [ { "name": "stdout", @@ -105,217 +74,127 @@ ] } ], - "execution_count": 3 + "source": [ + "ndb = NewDatabase(\n", + " scenarios=[\n", + " #{\"model\":\"remind\", \"pathway\":\"SSP2-Base\", \"year\":2050},\n", + " #{\"model\":\"remind\", \"pathway\":\"SSP2-Base\", \"year\":2010},\n", + " #{\"model\":\"image\", \"pathway\":\"SSP2-Base\", \"year\":2020},\n", + " #{\"model\":\"remind\", \"pathway\":\"SSP2-Base\", \"year\":2050},\n", + " #{\"model\":\"remind\", \"pathway\":\"SSP2-PkBudg1150\", \"year\":2050},\n", + " {\"model\":\"image\", \"pathway\":\"SSP2-RCP26\", \"year\":2040},\n", + " {\"model\":\"image\", \"pathway\":\"SSP2-RCP26\", \"year\":2060},\n", + " {\"model\":\"image\", \"pathway\":\"SSP2-RCP19\", \"year\":2050},\n", + " #{\"model\":\"image\", \"pathway\":\"SSP2-Base\", \"year\":2050},\n", + " ],\n", + " source_db=\"ecoinvent-3.10-cutoff\", # <-- name of the database in the BW2 project. Must be a string.\n", + " source_version=\"3.10\", # <-- version of ecoinvent. Can be \"3.5\", \"3.6\", \"3.7\" or \"3.8\". Must be a string.\n", + " key=\"tUePmX_S5B8ieZkkM7WUU2CnO8SmShwmAeWK9x2rTFo=\",\n", + " biosphere_name=\"ecoinvent-3.10-biosphere\"\n", + ")" + ] }, { "cell_type": "code", + "execution_count": 4, "id": "0c80994c-cbac-4143-81ee-1de1531a6f95", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-12T17:04:21.365439Z", - "start_time": "2024-07-12T17:04:06.172553Z" - } - }, - "source": "ndb.update(\"cement\")", + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Processing scenarios: 0%| | 0/1 [00:00. at 0x3e0545480>,\n", - " {'market for hard coal': {'amount': 0.0354,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 934.56,\n", - " 'fossil CO2': 91.58688},\n", - " 'market for heavy fuel oil': {'amount': 0.0255,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 981.7499999999999,\n", - " 'fossil CO2': 72.55132499999998},\n", - " 'market for light fuel oil': {'amount': 0.000374,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 15.9324,\n", - " 'fossil CO2': 1.0929626399999999},\n", - " 'market for meat and bone meal': {'amount': 0.00961,\n", - " 'biogenic CO2': 11.186232200000003,\n", - " 'energy': 137.42300000000003,\n", - " 'fossil CO2': 0.0},\n", - " 'market for petroleum coke': {'amount': 0.00391,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 122.38300000000001,\n", - " 'fossil CO2': 11.9323425},\n", - " 'market group for natural gas, high pressure': {'amount': 0.00017462,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 6.28632,\n", - " 'fossil CO2': 0.3771792}})\n", - "2832.788501512605\n", - "0.97397502595931\n", - "new 2759.065254297974\n", - "new scaling factor 1.0943280793270356\n", - "WEU 2198.33472\n", - "defaultdict(. at 0x3e0545480>,\n", - " {'market for hard coal': {'amount': 0.0354,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 934.56,\n", - " 'fossil CO2': 91.58688},\n", - " 'market for heavy fuel oil': {'amount': 0.0255,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 981.7499999999999,\n", - " 'fossil CO2': 72.55132499999998},\n", - " 'market for light fuel oil': {'amount': 0.000374,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 15.9324,\n", - " 'fossil CO2': 1.0929626399999999},\n", - " 'market for meat and bone meal': {'amount': 0.00961,\n", - " 'biogenic CO2': 11.186232200000003,\n", - " 'energy': 137.42300000000003,\n", - " 'fossil CO2': 0.0},\n", - " 'market for petroleum coke': {'amount': 0.00391,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 122.38300000000001,\n", - " 'fossil CO2': 11.9323425},\n", - " 'market group for natural gas, high pressure': {'amount': 0.00017462,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 6.28632,\n", - " 'fossil CO2': 0.3771792}})\n", - "2832.788501512605\n", - "0.8335910807752474\n", - "new 2361.3872285835855\n", - "new scaling factor 1.0943280793270356\n", - "WEU 2198.33472\n", - "defaultdict(. at 0x3e05455a0>,\n", - " {'market for hard coal': {'amount': 0.0354,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 934.56,\n", - " 'fossil CO2': 91.58688},\n", - " 'market for heavy fuel oil': {'amount': 0.0255,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 981.7499999999999,\n", - " 'fossil CO2': 72.55132499999998},\n", - " 'market for light fuel oil': {'amount': 0.000374,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 15.9324,\n", - " 'fossil CO2': 1.0929626399999999},\n", - " 'market for meat and bone meal': {'amount': 0.00961,\n", - " 'biogenic CO2': 11.186232200000003,\n", - " 'energy': 137.42300000000003,\n", - " 'fossil CO2': 0.0},\n", - " 'market for petroleum coke': {'amount': 0.00391,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 122.38300000000001,\n", - " 'fossil CO2': 11.9323425},\n", - " 'market group for natural gas, high pressure': {'amount': 0.00017462,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 6.28632,\n", - " 'fossil CO2': 0.3771792}})\n", - "2832.788501512605\n", - "1.0\n", - "new 2832.788501512605\n", - "new scaling factor 1.0943280793270356\n", - "WEU 2198.33472\n", - "defaultdict(. at 0x3a6cb9f30>,\n", - " {'market for hard coal': {'amount': 0.0354,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 934.56,\n", - " 'fossil CO2': 91.58688},\n", - " 'market for heavy fuel oil': {'amount': 0.0255,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 981.7499999999999,\n", - " 'fossil CO2': 72.55132499999998},\n", - " 'market for light fuel oil': {'amount': 0.000374,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 15.9324,\n", - " 'fossil CO2': 1.0929626399999999},\n", - " 'market for meat and bone meal': {'amount': 0.00961,\n", - " 'biogenic CO2': 11.186232200000003,\n", - " 'energy': 137.42300000000003,\n", - " 'fossil CO2': 0.0},\n", - " 'market for petroleum coke': {'amount': 0.00391,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 122.38300000000001,\n", - " 'fossil CO2': 11.9323425},\n", - " 'market group for natural gas, high pressure': {'amount': 0.00017462,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 6.28632,\n", - " 'fossil CO2': 0.3771792}})\n", - "2832.788501512605\n", - "1.0\n", - "new 2832.788501512605\n", - "new scaling factor 1.0943280793270356\n", - "WEU 2198.33472\n", - "defaultdict(. at 0x3e05455a0>,\n", - " {'market for hard coal': {'amount': 0.0354,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 934.56,\n", - " 'fossil CO2': 91.58688},\n", - " 'market for heavy fuel oil': {'amount': 0.0255,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 981.7499999999999,\n", - " 'fossil CO2': 72.55132499999998},\n", - " 'market for light fuel oil': {'amount': 0.000374,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 15.9324,\n", - " 'fossil CO2': 1.0929626399999999},\n", - " 'market for meat and bone meal': {'amount': 0.00961,\n", - " 'biogenic CO2': 11.186232200000003,\n", - " 'energy': 137.42300000000003,\n", - " 'fossil CO2': 0.0},\n", - " 'market for petroleum coke': {'amount': 0.00391,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 122.38300000000001,\n", - " 'fossil CO2': 11.9323425},\n", - " 'market group for natural gas, high pressure': {'amount': 0.00017462,\n", - " 'biogenic CO2': 0.0,\n", - " 'energy': 6.28632,\n", - " 'fossil CO2': 0.3771792}})\n", - "2832.788501512605\n", - "1.0\n", - "new 2832.788501512605\n", - "new scaling factor 1.0943280793270356\n" + "Done!\n", + "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "Processing scenarios: 100%|█████████████| 1/1 [00:15<00:00, 15.19s/it]" + "\n" ] - }, + } + ], + "source": [ + "ndb.update()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d423f5a1-7bd5-4d0e-8e1f-38634dc80d1a", + "metadata": {}, + "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Done!\n", - "\n" + "Generate change report.\n", + "Report saved under /Users/romain/GitHub/premise/dev.\n" ] - }, + } + ], + "source": [ + "ndb.generate_change_report()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "3b0e71e7-2fbb-4247-abcf-60ebb1909d4a", + "metadata": {}, + "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "\n" + "gearbox, for lorry 201.6\n", + "transmission, for lorry 291.2\n", + "treatment of used lorry, 40 metric ton -1\n", + "market for power distribution unit, for electric passenger car 4\n", + "assembly operation, for lorry 29223.50287857302\n", + "market for converter, for electric passenger car 25\n", + "market for inverter, for electric passenger car 41.19999946653843\n", + "suspension, for lorry 3440\n", + "power electronics, for lorry 265\n", + "tires and wheels, for lorry 1422\n", + "market for battery management system, for Li-ion battery 125.73132358067981\n", + "glider lightweighting 268.7599962415552\n", + "cabin, for lorry 1153\n", + "other components, for electric lorry 1059\n", + "maintenance, lorry 40 metric ton 1\n", + "frame, blanks and saddle, for lorry 5539\n", + "market for electric motor, electric passenger car 600\n", + "market for used Li-ion battery -314.32832768712774\n", + "retarder, for lorry 67.2\n", + "market for battery cell, Li-ion, NMC622 188.597004106448\n", + "heavy duty truck, battery electric, NMC-622 battery, 40t gross weight, long haul 1\n" ] } ], - "execution_count": 4 + "source": [ + "from premise.utils import load_database\n", + "ndb.scenarios[0] = load_database(ndb.scenarios[0])\n", + "\n", + "for ds in ndb.scenarios[0][\"database\"]:\n", + " if ds[\"name\"] == \"heavy duty truck, battery electric, NMC-622 battery, 40t gross weight, long haul\":\n", + " for e in ds[\"exchanges\"]:\n", + " print(e[\"name\"], e[\"amount\"])" + ] }, { "cell_type": "code", + "execution_count": 6, "id": "dff4efb1-69ab-4bd5-8d52-4038df180a85", - "metadata": { - "ExecuteTime": { - "end_time": "2024-07-12T15:58:22.511387Z", - "start_time": "2024-07-12T15:44:21.450702Z" - } - }, - "source": "ndb.write_db_to_brightway([\"cement 2050 rcp19\", \"cement 2050 rcp26\"])", + "metadata": {}, "outputs": [ { "name": "stdout", @@ -323,35 +202,87 @@ "text": [ "Write new database(s) to Brightway.\n", "Running all checks...\n", - "Minor anomalies found: check the change report.\n", - "Database cement 2050 rcp19 already exists: it will be overwritten.\n", - "Vacuuming database \n", - "Warning: No valid output stream.\n", + "Minor anomalies found: check the change report.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Writing activities to SQLite3 database:\n", + "0% [##############################] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:41\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "Title: Writing activities to SQLite3 database:\n", - " Started: 07/12/2024 17:49:10\n", - " Finished: 07/12/2024 17:49:56\n", - " Total time elapsed: 00:00:45\n", - " CPU %: 94.30\n", - " Memory %: 22.17\n", - "Created database: cement 2050 rcp19\n", + " Started: 07/13/2024 18:54:03\n", + " Finished: 07/13/2024 18:54:45\n", + " Total time elapsed: 00:00:41\n", + " CPU %: 88.20\n", + " Memory %: 12.60\n", + "Created database: cement rcp26 2040\n", "Running all checks...\n", - "Minor anomalies found: check the change report.\n", - "Warning: No valid output stream.\n", + "Minor anomalies found: check the change report.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Writing activities to SQLite3 database:\n", + "0% [##############################] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:52\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Title: Writing activities to SQLite3 database:\n", + " Started: 07/13/2024 19:03:00\n", + " Finished: 07/13/2024 19:03:53\n", + " Total time elapsed: 00:00:52\n", + " CPU %: 89.80\n", + " Memory %: 8.77\n", + "Created database: cement rcp26 2060\n", + "Running all checks...\n", + "Minor anomalies found: check the change report.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Writing activities to SQLite3 database:\n", + "0% [##############################] 100% | ETA: 00:00:00\n", + "Total time elapsed: 00:00:53\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "Title: Writing activities to SQLite3 database:\n", - " Started: 07/12/2024 17:54:08\n", - " Finished: 07/12/2024 17:54:56\n", - " Total time elapsed: 00:00:47\n", - " CPU %: 94.60\n", - " Memory %: 22.50\n", - "Created database: cement 2050 rcp26\n", + " Started: 07/13/2024 19:12:37\n", + " Finished: 07/13/2024 19:13:30\n", + " Total time elapsed: 00:00:53\n", + " CPU %: 89.00\n", + " Memory %: 7.57\n", + "Created database: cement rcp19 2\n", "Generate scenario report.\n", - "Report saved under /Users/romain/Github/premise/dev/export/scenario_report.\n", + "Report saved under /Users/romain/GitHub/premise/dev/export/scenario_report.\n", "Generate change report.\n", - "Report saved under /Users/romain/Github/premise/dev.\n" + "Report saved under /Users/romain/GitHub/premise/dev.\n" ] } ], - "execution_count": 7 + "source": [ + "ndb.write_db_to_brightway([\"cement rcp26 2040\", \"cement rcp26 2060\", \"cement rcp19 2\"])" + ] }, { "cell_type": "code", @@ -10613,18 +10544,18 @@ "evalue": "Can't pickle local object 'main..'", "output_type": "error", "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[9], line 31\u001B[0m\n\u001B[1;32m 28\u001B[0m process\u001B[38;5;241m.\u001B[39mjoin()\n\u001B[1;32m 30\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;18m__name__\u001B[39m \u001B[38;5;241m==\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124m__main__\u001B[39m\u001B[38;5;124m\"\u001B[39m:\n\u001B[0;32m---> 31\u001B[0m \u001B[43mmain\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n", - "Cell \u001B[0;32mIn[9], line 25\u001B[0m, in \u001B[0;36mmain\u001B[0;34m()\u001B[0m\n\u001B[1;32m 23\u001B[0m process \u001B[38;5;241m=\u001B[39m Process(target\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mlambda\u001B[39;00m: configure_logging(current_process()\u001B[38;5;241m.\u001B[39mpid)\u001B[38;5;241m.\u001B[39minfo(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThis is an info message from process \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mcurrent_process()\u001B[38;5;241m.\u001B[39mpid\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m.\u001B[39m\u001B[38;5;124m\"\u001B[39m))\n\u001B[1;32m 24\u001B[0m processes\u001B[38;5;241m.\u001B[39mappend(process)\n\u001B[0;32m---> 25\u001B[0m \u001B[43mprocess\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mstart\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 27\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m process \u001B[38;5;129;01min\u001B[39;00m processes:\n\u001B[1;32m 28\u001B[0m process\u001B[38;5;241m.\u001B[39mjoin()\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/process.py:121\u001B[0m, in \u001B[0;36mBaseProcess.start\u001B[0;34m(self)\u001B[0m\n\u001B[1;32m 118\u001B[0m \u001B[38;5;28;01massert\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m _current_process\u001B[38;5;241m.\u001B[39m_config\u001B[38;5;241m.\u001B[39mget(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mdaemon\u001B[39m\u001B[38;5;124m'\u001B[39m), \\\n\u001B[1;32m 119\u001B[0m \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mdaemonic processes are not allowed to have children\u001B[39m\u001B[38;5;124m'\u001B[39m\n\u001B[1;32m 120\u001B[0m _cleanup()\n\u001B[0;32m--> 121\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_popen \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_Popen\u001B[49m\u001B[43m(\u001B[49m\u001B[38;5;28;43mself\u001B[39;49m\u001B[43m)\u001B[49m\n\u001B[1;32m 122\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_sentinel \u001B[38;5;241m=\u001B[39m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_popen\u001B[38;5;241m.\u001B[39msentinel\n\u001B[1;32m 123\u001B[0m \u001B[38;5;66;03m# Avoid a refcycle if the target function holds an indirect\u001B[39;00m\n\u001B[1;32m 124\u001B[0m \u001B[38;5;66;03m# reference to the process object (see bpo-30775)\u001B[39;00m\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/context.py:224\u001B[0m, in \u001B[0;36mProcess._Popen\u001B[0;34m(process_obj)\u001B[0m\n\u001B[1;32m 222\u001B[0m \u001B[38;5;129m@staticmethod\u001B[39m\n\u001B[1;32m 223\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21m_Popen\u001B[39m(process_obj):\n\u001B[0;32m--> 224\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43m_default_context\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_context\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mProcess\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_Popen\u001B[49m\u001B[43m(\u001B[49m\u001B[43mprocess_obj\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/context.py:288\u001B[0m, in \u001B[0;36mSpawnProcess._Popen\u001B[0;34m(process_obj)\u001B[0m\n\u001B[1;32m 285\u001B[0m \u001B[38;5;129m@staticmethod\u001B[39m\n\u001B[1;32m 286\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21m_Popen\u001B[39m(process_obj):\n\u001B[1;32m 287\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mpopen_spawn_posix\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m Popen\n\u001B[0;32m--> 288\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mPopen\u001B[49m\u001B[43m(\u001B[49m\u001B[43mprocess_obj\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/popen_spawn_posix.py:32\u001B[0m, in \u001B[0;36mPopen.__init__\u001B[0;34m(self, process_obj)\u001B[0m\n\u001B[1;32m 30\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21m__init__\u001B[39m(\u001B[38;5;28mself\u001B[39m, process_obj):\n\u001B[1;32m 31\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_fds \u001B[38;5;241m=\u001B[39m []\n\u001B[0;32m---> 32\u001B[0m \u001B[38;5;28;43msuper\u001B[39;49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[38;5;21;43m__init__\u001B[39;49m\u001B[43m(\u001B[49m\u001B[43mprocess_obj\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/popen_fork.py:19\u001B[0m, in \u001B[0;36mPopen.__init__\u001B[0;34m(self, process_obj)\u001B[0m\n\u001B[1;32m 17\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mreturncode \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[1;32m 18\u001B[0m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mfinalizer \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m\n\u001B[0;32m---> 19\u001B[0m \u001B[38;5;28;43mself\u001B[39;49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_launch\u001B[49m\u001B[43m(\u001B[49m\u001B[43mprocess_obj\u001B[49m\u001B[43m)\u001B[49m\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/popen_spawn_posix.py:47\u001B[0m, in \u001B[0;36mPopen._launch\u001B[0;34m(self, process_obj)\u001B[0m\n\u001B[1;32m 45\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 46\u001B[0m reduction\u001B[38;5;241m.\u001B[39mdump(prep_data, fp)\n\u001B[0;32m---> 47\u001B[0m \u001B[43mreduction\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdump\u001B[49m\u001B[43m(\u001B[49m\u001B[43mprocess_obj\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mfp\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 48\u001B[0m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[1;32m 49\u001B[0m set_spawning_popen(\u001B[38;5;28;01mNone\u001B[39;00m)\n", - "File \u001B[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/reduction.py:60\u001B[0m, in \u001B[0;36mdump\u001B[0;34m(obj, file, protocol)\u001B[0m\n\u001B[1;32m 58\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mdump\u001B[39m(obj, file, protocol\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mNone\u001B[39;00m):\n\u001B[1;32m 59\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m'''Replacement for pickle.dump() using ForkingPickler.'''\u001B[39;00m\n\u001B[0;32m---> 60\u001B[0m \u001B[43mForkingPickler\u001B[49m\u001B[43m(\u001B[49m\u001B[43mfile\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mprotocol\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdump\u001B[49m\u001B[43m(\u001B[49m\u001B[43mobj\u001B[49m\u001B[43m)\u001B[49m\n", - "\u001B[0;31mAttributeError\u001B[0m: Can't pickle local object 'main..'" + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 31\u001b[0m\n\u001b[1;32m 28\u001b[0m process\u001b[38;5;241m.\u001b[39mjoin()\n\u001b[1;32m 30\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m---> 31\u001b[0m \u001b[43mmain\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[9], line 25\u001b[0m, in \u001b[0;36mmain\u001b[0;34m()\u001b[0m\n\u001b[1;32m 23\u001b[0m process \u001b[38;5;241m=\u001b[39m Process(target\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mlambda\u001b[39;00m: configure_logging(current_process()\u001b[38;5;241m.\u001b[39mpid)\u001b[38;5;241m.\u001b[39minfo(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThis is an info message from process \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mcurrent_process()\u001b[38;5;241m.\u001b[39mpid\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m))\n\u001b[1;32m 24\u001b[0m processes\u001b[38;5;241m.\u001b[39mappend(process)\n\u001b[0;32m---> 25\u001b[0m \u001b[43mprocess\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstart\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 27\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m process \u001b[38;5;129;01min\u001b[39;00m processes:\n\u001b[1;32m 28\u001b[0m process\u001b[38;5;241m.\u001b[39mjoin()\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/process.py:121\u001b[0m, in \u001b[0;36mBaseProcess.start\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m _current_process\u001b[38;5;241m.\u001b[39m_config\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdaemon\u001b[39m\u001b[38;5;124m'\u001b[39m), \\\n\u001b[1;32m 119\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdaemonic processes are not allowed to have children\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 120\u001b[0m _cleanup()\n\u001b[0;32m--> 121\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_popen \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_Popen\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 122\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_sentinel \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_popen\u001b[38;5;241m.\u001b[39msentinel\n\u001b[1;32m 123\u001b[0m \u001b[38;5;66;03m# Avoid a refcycle if the target function holds an indirect\u001b[39;00m\n\u001b[1;32m 124\u001b[0m \u001b[38;5;66;03m# reference to the process object (see bpo-30775)\u001b[39;00m\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/context.py:224\u001b[0m, in \u001b[0;36mProcess._Popen\u001b[0;34m(process_obj)\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[38;5;129m@staticmethod\u001b[39m\n\u001b[1;32m 223\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_Popen\u001b[39m(process_obj):\n\u001b[0;32m--> 224\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_default_context\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_context\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mProcess\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_Popen\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprocess_obj\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/context.py:288\u001b[0m, in \u001b[0;36mSpawnProcess._Popen\u001b[0;34m(process_obj)\u001b[0m\n\u001b[1;32m 285\u001b[0m \u001b[38;5;129m@staticmethod\u001b[39m\n\u001b[1;32m 286\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_Popen\u001b[39m(process_obj):\n\u001b[1;32m 287\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpopen_spawn_posix\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Popen\n\u001b[0;32m--> 288\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mPopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprocess_obj\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/popen_spawn_posix.py:32\u001b[0m, in \u001b[0;36mPopen.__init__\u001b[0;34m(self, process_obj)\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, process_obj):\n\u001b[1;32m 31\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_fds \u001b[38;5;241m=\u001b[39m []\n\u001b[0;32m---> 32\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mprocess_obj\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/popen_fork.py:19\u001b[0m, in \u001b[0;36mPopen.__init__\u001b[0;34m(self, process_obj)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreturncode \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfinalizer \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m---> 19\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_launch\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprocess_obj\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/popen_spawn_posix.py:47\u001b[0m, in \u001b[0;36mPopen._launch\u001b[0;34m(self, process_obj)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 46\u001b[0m reduction\u001b[38;5;241m.\u001b[39mdump(prep_data, fp)\n\u001b[0;32m---> 47\u001b[0m \u001b[43mreduction\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdump\u001b[49m\u001b[43m(\u001b[49m\u001b[43mprocess_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 48\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 49\u001b[0m set_spawning_popen(\u001b[38;5;28;01mNone\u001b[39;00m)\n", + "File \u001b[0;32m~/anaconda3/envs/premise/lib/python3.10/multiprocessing/reduction.py:60\u001b[0m, in \u001b[0;36mdump\u001b[0;34m(obj, file, protocol)\u001b[0m\n\u001b[1;32m 58\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdump\u001b[39m(obj, file, protocol\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 59\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m'''Replacement for pickle.dump() using ForkingPickler.'''\u001b[39;00m\n\u001b[0;32m---> 60\u001b[0m \u001b[43mForkingPickler\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mprotocol\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdump\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mAttributeError\u001b[0m: Can't pickle local object 'main..'" ] } ], diff --git a/premise/cement.py b/premise/cement.py index 6d04d65d..0343a9b0 100644 --- a/premise/cement.py +++ b/premise/cement.py @@ -9,19 +9,17 @@ import copy import uuid -from collections import defaultdict from .logger import create_logger from .transformation import ( BaseTransformation, - Dict, IAMDataCollection, InventorySet, List, - calculate_input_energy, np, ws, ) +from .export import biosphere_flows_dictionary from .validation import CementValidation logger = create_logger("cement") @@ -121,128 +119,8 @@ def __init__( for v in list(value): self.fuel_map_reverse[v] = key - def fetch_current_energy_details(self, dataset): - # Fetches the current energy consumption per ton of clinker + self.biosphere_dict = biosphere_flows_dictionary(self.version) - d_fuels = defaultdict( - lambda: {"amount": 0, "energy": 0, "fossil CO2": 0, "biogenic CO2": 0} - ) - - for exc in ws.technosphere(dataset): - fuel_name = exc["name"] - - if fuel_name in self.cement_fuels_map["cement, dry feed rotary kiln"]: - fuel_data = self.fuels_specs[self.fuel_map_reverse[fuel_name]] - co2_emission = fuel_data["co2"] - biogenic_share = fuel_data["biogenic_share"] - - # Calculate the energy once for the given exc - input_energy = ( - calculate_input_energy( - fuel_name=exc["name"], - fuel_amount=exc["amount"], - fuel_unit=exc["unit"], - fuels_specs=self.fuels_specs, - fuel_map_reverse=self.fuel_map_reverse, - ) - * 1000 - ) - - # Update the dictionary in one go - d_fuels[fuel_name]["amount"] += exc["amount"] - d_fuels[fuel_name]["energy"] += input_energy - d_fuels[fuel_name]["fossil CO2"] += ( - co2_emission * input_energy * (1 - biogenic_share) - ) - d_fuels[fuel_name]["biogenic CO2"] += ( - co2_emission * input_energy * biogenic_share - ) - - return d_fuels - - def fetch_bio_ratio(self, dataset: dict) -> float: - """ - Return teh ratio between bio and non-bio CO2 emissions - in the dataset. - - :param dataset: clinker production dataset - :return: ratio between bio and non-bio CO2 emissions - """ - - bio_co2 = sum( - e["amount"] - for e in dataset["exchanges"] - if e["name"] == "Carbon dioxide, non-fossil" - ) - non_bio_co2 = sum( - e["amount"] - for e in dataset["exchanges"] - if e["name"] == "Carbon dioxide, fossil" - ) - - return bio_co2 / (bio_co2 + non_bio_co2) - - def rescale_fuel_inputs(self, dataset, scaling_factor, energy_details): - if scaling_factor != 1 and scaling_factor > 0: - for exc in dataset["exchanges"]: - if exc["name"] in self.cement_fuels_map["cement, dry feed rotary kiln"]: - exc["amount"] *= scaling_factor - - # update energy_details - energy_details[exc["name"]]["amount"] *= scaling_factor - energy_details[exc["name"]]["energy"] *= scaling_factor - energy_details[exc["name"]]["fossil CO2"] *= scaling_factor - energy_details[exc["name"]]["biogenic CO2"] *= scaling_factor - - new_energy_input = sum(d["energy"] for d in energy_details.values()) - - dataset["log parameters"].update( - { - "new energy input per ton clinker": new_energy_input, - } - ) - - return dataset - - def rescale_emissions(self, dataset: dict, energy_details: dict) -> dict: - for exc in ws.biosphere(dataset, ws.contains("name", "Carbon dioxide")): - if "non-fossil" in exc["name"].lower(): - dataset["log parameters"].update( - { - "initial biogenic CO2": exc["amount"], - } - ) - # calculate total biogenic CO2 from energy_details - total_biogenic_CO2 = sum( - [d["biogenic CO2"] for d in energy_details.values()] - ) - - exc["amount"] = total_biogenic_CO2 / 1000 - dataset["log parameters"].update( - { - "new biogenic CO2": total_biogenic_CO2 / 1000, - } - ) - else: - # calculate total fossil CO2 from energy_details - total_fossil_CO2 = sum( - [d["fossil CO2"] for d in energy_details.values()] - ) - - dataset["log parameters"].update( - { - "initial fossil CO2": exc["amount"], - } - ) - - # remove 543 kg for calcination - exc["amount"] = 0.543 + (total_fossil_CO2 / 1000) - dataset["log parameters"].update( - { - "new fossil CO2": exc["amount"], - } - ) - return dataset def build_CCS_datasets(self): ccs_datasets = { @@ -369,95 +247,15 @@ def build_clinker_production_datasets(self) -> list: ) for region, dataset in d_act_clinker.items(): - # calculate current thermal energy consumption per kg clinker - energy_details = self.fetch_current_energy_details(dataset) - - current_energy_input_per_ton_clinker = sum( - d["energy"] for d in energy_details.values() - ) - - if region == "WEU": - from pprint import pprint - - print(region, current_energy_input_per_ton_clinker) - pprint(energy_details) - - # fetch the amount of biogenic CO2 emissions - bio_CO2 = sum( - e["amount"] - for e in ws.biosphere( - dataset, ws.contains("name", "Carbon dioxide, non-fossil") - ) - ) - - # back-calculate the amount of waste fuel from - # the biogenic CO2 emissions - # biogenic CO2 / MJ for waste fuel - - waste_fuel_biogenic_co2_emission_factor = ( - self.fuels_specs["waste"]["co2"] - * self.fuels_specs["waste"]["biogenic_share"] - ) - - waste_fuel_fossil_co2_emission_factor = self.fuels_specs["waste"][ - "co2" - ] * (1 - self.fuels_specs["waste"]["biogenic_share"]) - - # energy input of waste fuel in MJ - energy_input_waste_fuel = ( - bio_CO2 / waste_fuel_biogenic_co2_emission_factor - ) - # amount waste fuel, in kg - amount_waste_fuel = ( - energy_input_waste_fuel / self.fuels_specs["waste"]["lhv"] - ) - - # add waste fuel to the energy details - energy_details["market for waste plastic, mixture"] = { - "amount": amount_waste_fuel, - "energy": energy_input_waste_fuel * 1000, - "fossil CO2": waste_fuel_fossil_co2_emission_factor - * energy_input_waste_fuel, - "biogenic CO2": bio_CO2, - } - - # add the waste fuel energy input - # to the total energy input - current_energy_input_per_ton_clinker += ( - energy_input_waste_fuel * 1000 - ) - if region == "WEU": - print(current_energy_input_per_ton_clinker) - - # add the waste fuel input to the dataset - if amount_waste_fuel != 0: - dataset["exchanges"].append( - { - "uncertainty type": 0, - "loc": 0, - "amount": amount_waste_fuel * -1, - "type": "technosphere", - "production volume": 0, - "name": "clinker production", - "unit": "kilogram", - "location": "RoW", - "product": "waste plastic, mixture", - } - ) - if "log parameters" not in dataset: - dataset["log parameters"] = {} - - dataset["log parameters"].update( - { - "initial energy input per ton clinker": current_energy_input_per_ton_clinker, - } - ) + # from Kellenberger at al. 2007, the total energy + # input per ton of clinker is 3.4 GJ/ton clinker + current_energy_input_per_ton_clinker = 3400 # calculate the scaling factor - # the correction factor applied to all fuel/electricity input is - # equal to the ratio fuel/output in the year in question - # divided by the ratio fuel/output in 2020 + # the correction factor applied to hard coal input + # we assume that any fuel use reduction would in priority + # affect hard coal use scaling_factor = 1 / self.find_iam_efficiency_change( data=self.iam_data.cement_efficiencies, @@ -465,20 +263,19 @@ def build_clinker_production_datasets(self) -> list: location=dataset["location"], ) - if region == "WEU": - print(scaling_factor) + new_energy_input_per_ton_clinker = 3400 + + if "log parameters" not in dataset: + dataset["log parameters"] = {} - new_energy_input_per_ton_clinker = 0 + dataset['log parameters']["initial energy input per ton clinker"] = current_energy_input_per_ton_clinker - if not np.isnan(scaling_factor) and scaling_factor > 0.0: + if not np.isnan(scaling_factor): # calculate new thermal energy # consumption per kg clinker new_energy_input_per_ton_clinker = ( current_energy_input_per_ton_clinker * scaling_factor ) - if region == "WEU": - print("new", new_energy_input_per_ton_clinker) - # put a floor value of 3100 kj/kg clinker if new_energy_input_per_ton_clinker < 3100: new_energy_input_per_ton_clinker = 3100 @@ -486,32 +283,56 @@ def build_clinker_production_datasets(self) -> list: elif new_energy_input_per_ton_clinker > 5000: new_energy_input_per_ton_clinker = 5000 + # but if efficient kiln, + # set the energy input to 3000 kJ/kg clinker + if variable.startswith( + "cement, dry feed rotary kiln, efficient" + ): + new_energy_input_per_ton_clinker = 3000 + + dataset['log parameters'][ + "new energy input per ton clinker"] = int(new_energy_input_per_ton_clinker) + scaling_factor = ( new_energy_input_per_ton_clinker / current_energy_input_per_ton_clinker ) - if region == "WEU": - print("new scaling factor", scaling_factor) + dataset['log parameters']["energy scaling factor"] = scaling_factor - # but if efficient kiln, set the energy input to 3100 kJ/kg clinker - if variable.startswith( - "cement, dry feed rotary kiln, efficient" + # rescale hard coal consumption and related emissions + coal_specs = self.fuels_specs["hard coal"] + old_coal_input, new_coal_input = 0, 0 + for exc in ws.technosphere( + dataset, + ws.contains("name", "hard coal"), ): - new_energy_input_per_ton_clinker = 3000 - scaling_factor = ( - new_energy_input_per_ton_clinker - / current_energy_input_per_ton_clinker + # in kJ + old_coal_input = float(exc["amount"] * coal_specs["lhv"]) + # in MJ + new_coal_input = old_coal_input - (( + current_energy_input_per_ton_clinker + - new_energy_input_per_ton_clinker + )/1000) + exc["amount"] = np.clip( + new_coal_input / coal_specs["lhv"], + 0, + None ) - # rescale fuel consumption and emissions - # rescale the fuel and electricity input - dataset = self.rescale_fuel_inputs( - dataset, scaling_factor, energy_details - ) + # rescale combustion-related fossil CO2 emissions + for exc in ws.biosphere( + dataset, + ws.contains("name", "Carbon dioxide"), + ): + if exc["name"] == "Carbon dioxide, fossil": + dataset["log parameters"]["initial fossil CO2"] = exc["amount"] + co2_reduction = (old_coal_input - new_coal_input) * coal_specs["co2"] + exc["amount"] -= co2_reduction + dataset["log parameters"]["new fossil CO2"] = exc["amount"] - # rescale combustion-related CO2 emissions - dataset = self.rescale_emissions(dataset, energy_details) + if exc["name"] == "Carbon dioxide, non-fossil": + dataset["log parameters"]["initial biogenic CO2"] = exc["amount"] # add 0.005 kg/kg clinker of ammonia use for NOx removal # according to Muller et al., 2024 @@ -599,7 +420,7 @@ def build_clinker_production_datasets(self) -> list: variable == "cement, dry feed rotary kiln, efficient, with on-site CCS" ): - # only 90% of process emissions (calcination) are captured + # only 95% of process emissions (calcination) are captured CCS_amount = ( 0.543 * ccs_datasets[variable]["capture share"] ) @@ -608,47 +429,96 @@ def build_clinker_production_datasets(self) -> list: CO2_amount * ccs_datasets[variable]["capture share"] ) - if variable in ccs_datasets: - ccs_exc = { - "uncertainty type": 0, - "loc": CCS_amount, - "amount": CCS_amount, - "type": "technosphere", - "production volume": 0, - "name": ccs_datasets[variable]["name"], - "unit": "kilogram", - "location": dataset["location"], - "product": ccs_datasets[variable][ - "reference product" - ], - } - dataset["exchanges"].append(ccs_exc) - - # Update CO2 exchanges - for exc in dataset["exchanges"]: + dataset["log parameters"][ + 'carbon capture rate' + ] = CCS_amount / CO2_amount + + ccs_exc = { + "uncertainty type": 0, + "loc": CCS_amount, + "amount": CCS_amount, + "type": "technosphere", + "production volume": 0, + "name": ccs_datasets[variable]["name"], + "unit": "kilogram", + "location": dataset["location"], + "product": ccs_datasets[variable][ + "reference product" + ], + } + dataset["exchanges"].append(ccs_exc) + + # Update CO2 exchanges + for exc in ws.biosphere( + dataset, + ws.contains("name", "Carbon dioxide, fossil"), + ): if ( - exc["name"].lower().startswith("carbon dioxide") - and exc["type"] == "biosphere" + variable + != "cement, dry feed rotary kiln, efficient, with on-site CCS" ): exc["amount"] *= ( CO2_amount - CCS_amount ) / CO2_amount - # make sure it's not negative - if exc["amount"] < 0: - exc["amount"] = 0 - - if "non-fossil" in exc["name"].lower(): - dataset["log parameters"].update( - { - "new biogenic CO2": exc["amount"], - } - ) - else: - dataset["log parameters"].update( - { - "new fossil CO2": exc["amount"], - } - ) + else: + exc["amount"] -= CCS_amount + + # make sure it's not negative + if exc["amount"] < 0: + exc["amount"] = 0 + + dataset["log parameters"]["new fossil CO2"] = exc["amount"] + + # Update biogenic CO2 exchanges + if ( + variable + != "cement, dry feed rotary kiln, efficient, with on-site CCS" + ): + for exc in ws.biosphere( + dataset, + ws.contains("name", "Carbon dioxide, non-fossil"), + ): + dataset["log parameters"]["initial biogenic CO2"] = exc["amount"] + exc["amount"] *= ( + CO2_amount - CCS_amount + ) / CO2_amount + + # make sure it's not negative + if exc["amount"] < 0: + exc["amount"] = 0 + + dataset["log parameters"]["new biogenic CO2"] = exc["amount"] + + biogenic_CO2_reduction = ( + dataset["log parameters"]["initial biogenic CO2"] + - dataset["log parameters"]["new biogenic CO2"] + ) + # add a flow of "Carbon dioxide, in air" to reflect + # the permanent storage of biogenic CO2 + dataset["exchanges"].append( + { + "uncertainty type": 0, + "loc": biogenic_CO2_reduction, + "amount": biogenic_CO2_reduction, + "type": "biosphere", + "name": "Carbon dioxide, in air", + "unit": "kilogram", + "categories": ("natural resource", "in air"), + "comment": "Permanent storage of biogenic CO2", + "input": ( + "biosphere3", + self.biosphere_dict[ + ( + "Carbon dioxide, in air", + "natural resource", + "in air", + "kilogram", + ) + ], + ), + } + ) + else: # Carbon capture rate: share of capture of total CO2 emitted carbon_capture_rate = self.get_carbon_capture_rate( @@ -835,6 +705,41 @@ def add_datasets_to_database(self) -> None: # add it to list of created datasets self.add_to_index(new_dataset) + # exclude the regionalization of these datasets + # because they are very rarely used in the database + excluded = [ + "factory", + "tile", + "sulphate", + "plaster", + "Portland Slag", + 'CP II-Z', + 'CP IV', + 'CP V RS', + 'Portland SR3', + 'CEM II/A-S', + 'CEM II/A-V', + 'CEM II/B-L', + 'CEM II/B-S', + 'type I (SM)', + 'type I-PM', + 'type IP/P', + 'type IS', + 'type S', + 'CEM III/C', + 'CEM V/A', + 'CEM V/B', + 'CEM II/A-L', + 'CEM III/B', + 'Pozzolana Portland', + 'ART', + 'type IP', + 'CEM IV/A', + 'CEM IV/B', + 'type ICo', + 'carbon' + ] + # cement markets markets = list( ws.get_many( @@ -842,7 +747,8 @@ def add_datasets_to_database(self) -> None: ws.contains("name", "market for cement"), ws.contains("reference product", "cement"), ws.doesnt_contain_any( - "name", ["factory", "tile", "sulphate", "plaster"] + "name", + excluded, ), ws.doesnt_contain_any("location", self.regions), ) @@ -879,7 +785,7 @@ def add_datasets_to_database(self) -> None: ws.contains("name", "cement production"), ws.contains("reference product", "cement"), ws.doesnt_contain_any( - "name", ["factory", "tile", "sulphate", "plaster", "carbon"] + "name", excluded ), ) ) @@ -919,6 +825,7 @@ def write_log(self, dataset, status="created"): f"{status}|{self.model}|{self.scenario}|{self.year}|" f"{dataset['name']}|{dataset['location']}|" f"{dataset.get('log parameters', {}).get('initial energy input per ton clinker', '')}|" + f"{dataset.get('log parameters', {}).get('energy scaling factor', '')}|" f"{dataset.get('log parameters', {}).get('new energy input per ton clinker', '')}|" f"{dataset.get('log parameters', {}).get('carbon capture rate', '')}|" f"{dataset.get('log parameters', {}).get('initial fossil CO2', '')}|" diff --git a/premise/data/additional_inventories/lci-carbon-capture.xlsx b/premise/data/additional_inventories/lci-carbon-capture.xlsx index d64ba88c..df758712 100644 Binary files a/premise/data/additional_inventories/lci-carbon-capture.xlsx and b/premise/data/additional_inventories/lci-carbon-capture.xlsx differ diff --git a/premise/data/utils/logging/reporting.yaml b/premise/data/utils/logging/reporting.yaml index d5901cf6..b35fd392 100644 --- a/premise/data/utils/logging/reporting.yaml +++ b/premise/data/utils/logging/reporting.yaml @@ -290,6 +290,10 @@ premise_cement: name: Initial energy input per ton clinker description: Energy input per ton clinker before adjustment (kj/kg) unit: kj/kg + energy scaling factor: + name: Energy scaling factor + description: Scaling factor the initial energy input is multiplied by + unit: 0-1 new energy input per ton clinker: name: New energy input per ton clinker after adjustment description: Energy input per ton clinker after (kj/kg)