{ "cells": [ { "attachments": {}, "cell_type": "markdown", "id": "c3b31311-c4da-43d1-b074-6b0432da7ea3", "metadata": {}, "source": [ "# Optimizing the design and operation of a DAC system under varying ambient conditions\n", "This case study is based on a model developed here:\n", "[Wiegner, Jan F., Alexa Grimm, Lukas Weimann, and Matteo Gazzani. \"Optimal design and operation of solid sorbent direct air capture processes at varying ambient conditions.\" Industrial & Engineering Chemistry Research 61, no. 34 (2022): 12649-12667.\n", "](https://pubs.acs.org/doi/10.1021/acs.iecr.2c00681)\n", "\n", "We want to optimize the design and operation of a DAC with a target net negative emissions of 1000 t in the Netherlands. We therefore load climate data for a location in the Netherlands. We store the CO2 in a not further defined CO2 sink." ] }, { "cell_type": "markdown", "id": "c76a7dea-3a71-4286-b28b-1259124a8df0", "metadata": {}, "source": [ "## Create templates\n", "We set the input data path and in this directory we can add input data templates for the model configuration and the topology with the function create_optimization_templates.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "35fa511b-d815-43d0-bf9f-21ca339151cf", "metadata": {}, "outputs": [], "source": [ "import adopt_net0 as adopt\n", "import json\n", "from pathlib import Path\n", "\n", "input_data_path = Path(\"./caseStudies/dac\")\n", "adopt.create_optimization_templates(input_data_path)" ] }, { "cell_type": "markdown", "id": "f0d8ee0c-234f-4222-a2b0-700540c1292a", "metadata": {}, "source": [ "## Adapt Topology\n", "We need to adapt the topology as well as the model configuration file to our case study. This can be done either in the file itself (Topology.json) or, as we do it here) via some lines of code.\n", "For the topology, we need to change the following:\n", "- Change nodes: nl\n", "- Change carriers: electricity, heat and CO2captured\n", "- Change investment periods: period1\n", "- The options regarding the time frame we can leave at the default (one year with hourly operation)" ] }, { "cell_type": "code", "execution_count": null, "id": "5ad41f8f-43cc-4edd-a663-e66c8969339a", "metadata": {}, "outputs": [], "source": [ "# Load json template\n", "with open(input_data_path / \"Topology.json\", \"r\") as json_file:\n", " topology = json.load(json_file)\n", "# Nodes\n", "topology[\"nodes\"] = [\"nl\"]\n", "# Carriers:\n", "topology[\"carriers\"] = [\"electricity\", \"heat\", \"CO2captured\"]\n", "# Investment periods:\n", "topology[\"investment_periods\"] = [\"period1\"]\n", "# Save json template\n", "with open(input_data_path / \"Topology.json\", \"w\") as json_file:\n", " json.dump(topology, json_file, indent=4)" ] }, { "cell_type": "markdown", "id": "ad6ca662-97ac-4899-a03c-7a9fa591d28c", "metadata": {}, "source": [ "## Adapt Model Configurations\n", "Now, we need to adapt the model configurations respectively. As the DAC model is rather complex, we also cluster the full resolution into 50 typical days (method 1).\n", "- Change objective to 'costs_emissionlimit' (this minimizes annualized costs at an emission limit)\n", "- Change emission limit to -1000 to account for the emission target\n", "- Change the number of typical days to 30 and select time aggregation method 1 " ] }, { "cell_type": "code", "execution_count": null, "id": "f459d0aa-b2b6-40a9-b63c-1f55bfbdd07f", "metadata": {}, "outputs": [], "source": [ "# Load json template\n", "with open(input_data_path / \"ConfigModel.json\", \"r\") as json_file:\n", " configuration = json.load(json_file)\n", "# Change objective\n", "configuration[\"optimization\"][\"objective\"][\"value\"] = \"costs_emissionlimit\"\n", "# Set emission limit:\n", "configuration[\"optimization\"][\"emission_limit\"][\"value\"] = -1000\n", "# Set time aggregation settings:\n", "configuration[\"optimization\"][\"typicaldays\"][\"N\"][\"value\"] = 30\n", "configuration[\"optimization\"][\"typicaldays\"][\"method\"][\"value\"] = 1\n", "# Set MILP gap\n", "configuration[\"solveroptions\"][\"mipgap\"][\"value\"] = 0.02\n", "# Save json template\n", "with open(input_data_path / \"ConfigModel.json\", \"w\") as json_file:\n", " json.dump(configuration, json_file, indent=4)" ] }, { "cell_type": "markdown", "id": "eeba77ce-2e8f-451e-9f58-408121561959", "metadata": {}, "source": [ "## Define input data\n", "We first create all required input data files based on the topology file and then add the DAC technology as a new technology to the respective node.\n", "Additionally we:\n", "- copy over technology data to the input data folder\n", "- define climate data for a dutch location (the location is used by default if nothing in NodeLocation.csv is specified)" ] }, { "cell_type": "code", "execution_count": null, "id": "090672de-cd62-46ed-af4b-86c70e69f659", "metadata": {}, "outputs": [], "source": [ "adopt.create_input_data_folder_template(input_data_path)\n", "\n", "# Add DAC as a new technology\n", "with open(input_data_path / \"period1\" / \"node_data\" / \"nl\" / \"Technologies.json\", \"r\") as json_file:\n", " technologies = json.load(json_file)\n", "technologies[\"new\"] = [\"DAC_Adsorption\"]\n", "technologies[\"existing\"] = {\"PermanentStorage_CO2_simple\": 10000}\n", "\n", "with open(input_data_path / \"period1\" / \"node_data\" / \"nl\" / \"Technologies.json\", \"w\") as json_file:\n", " json.dump(technologies, json_file, indent=4)\n", "\n", "# Copy over technology files\n", "adopt.copy_technology_data(input_data_path)\n", "\n", "# Define climate data\n", "adopt.load_climate_data_from_api(input_data_path)\n" ] }, { "cell_type": "markdown", "id": "af3c3914-594d-4064-81d7-10534bafb223", "metadata": {}, "source": [ "## Run model - infeasibility\n", "Now, we have defined all required data to run the model. It will be infeasible though..." ] }, { "cell_type": "code", "execution_count": null, "id": "193d2a5c-6121-454a-bf1e-f06796add068", "metadata": {}, "outputs": [], "source": [ "m = adopt.ModelHub()\n", "m.read_data(input_data_path)\n", "m.quick_solve()" ] }, { "cell_type": "markdown", "id": "1b881e00-47b9-4df8-8e4c-48e905d52375", "metadata": {}, "source": [ "## Allowing for heat and electricity import\n", "The model is infeasible, because we did not define where heat or electricity should come from. Here, we allow for electricity and heat import at a certain price at no additional emissions. As such we define:\n", "- An abitrary import limit on heat and electricity (1GW)\n", "- An import price on electricity of 60 EUR/MWh\n", "- An import price on heat of 20 EUR/MWh" ] }, { "cell_type": "code", "execution_count": null, "id": "6070c1ec-7419-4c92-9418-6027db67f0dd", "metadata": {}, "outputs": [], "source": [ "adopt.fill_carrier_data(input_data_path, value_or_data=1000, columns=['Import limit'], carriers=['electricity', 'heat'], nodes=['nl'])\n", "adopt.fill_carrier_data(input_data_path, value_or_data=60, columns=['Import price'], carriers=['electricity'], nodes=['nl'])\n", "adopt.fill_carrier_data(input_data_path, value_or_data=20, columns=['Import price'], carriers=['heat'], nodes=['nl'])" ] }, { "cell_type": "markdown", "id": "221513d9-c0b7-4f41-b4ff-d1ec2661c25f", "metadata": {}, "source": [ "## Run model again\n", "Now, the model should be feasible" ] }, { "cell_type": "code", "execution_count": null, "id": "1fd87d92-c79c-4b3d-a93f-5dca21e00043", "metadata": {}, "outputs": [], "source": [ "m = adopt.ModelHub()\n", "m.read_data(input_data_path)\n", "m.quick_solve()" ] }, { "attachments": {}, "cell_type": "markdown", "id": "76619a94-fdfb-4e97-81bf-4baa251b18b4", "metadata": {}, "source": [ "## Visualization\n", "The results can be inspected using the provided [visualization platform](https://resultvisualization.streamlit.app/) for some basic plots. The figures below are screenshots from the visualization platform.\n", "\n", "### Costs\n", "The objective is 295506, which are the total annual costs (from the log or the Summary.xlsx). As we captured 1000 t of CO2, the specific capturing costs are around 300 EUR.\n", "\n", "### Electricity and Heat requirements\n", "
\n", "\n", "
\n", "\n", "### DAC operation\n", "
\n", "\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }