diff --git a/.github/actions/before-script/action.yml b/.github/actions/before-script/action.yml index 3be9c2b..ea412e0 100644 --- a/.github/actions/before-script/action.yml +++ b/.github/actions/before-script/action.yml @@ -7,9 +7,7 @@ runs: if: runner.os == 'Linux' run: | sudo apt-get update -qq - sudo apt-get install -y git glpk-utils g++ cmake wget curl python3-pip - pip install conan - conan profile detect + sudo apt-get install -y git g++ cmake wget curl python3-pip wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda echo "$HOME/miniconda/bin" >> $GITHUB_PATH @@ -17,7 +15,8 @@ runs: conda create --name testenv python=3.11 -y conda activate testenv - conda install -c conda-forge poetry -y + conda install -c conda-forge -y poetry highs highspy conan + conan profile detect cd "$GITHUB_WORKSPACE/test/python_module" poetry install @@ -41,3 +40,49 @@ runs: cmake --build . --config Release cd "$GITHUB_WORKSPACE" shell: bash + - name: Install Windows dependencies + Miniconda + python/cpp modules + if: runner.os == 'Windows' + run: | + # Download & install Miniconda into %USERPROFILE%\miniconda + $installer = Join-Path $env:RUNNER_TEMP "Miniconda3-latest-Windows-x86_64.exe" + Invoke-WebRequest -Uri "https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe" -OutFile $installer + + $target = Join-Path $env:USERPROFILE "miniconda" + Start-Process -FilePath $installer -ArgumentList "/InstallationType=JustMe", "/S", "/D=$target" -Wait + + # Initialize conda for this PowerShell session + $conda = Join-Path $target "Scripts\conda.exe" + & $conda "shell.powershell" "hook" | Out-String | Invoke-Expression + + conda create --name testenv python=3.11 -y + conda activate testenv + $env:PATH = "$env:CONDA_PREFIX\Scripts;$env:CONDA_PREFIX;$env:PATH" + python -m pip install --upgrade pip + conda install -c conda-forge -y poetry highs highspy conan + conan profile detect + + # Check highs installation + where.exe highs + highs --version + + # Poetry installs + Set-Location (Join-Path $env:GITHUB_WORKSPACE "test\python_module") + poetry install + Set-Location (Join-Path $env:GITHUB_WORKSPACE "submodules\wind_power_timeseries") + poetry install + Set-Location (Join-Path $env:GITHUB_WORKSPACE "submodules\Tecnalia_Solar-Energy-Model") + poetry install + Set-Location (Join-Path $env:GITHUB_WORKSPACE "submodules\Tecnalia_Building-Stock-Energy-Model") + poetry install + + Set-Location (Join-Path $env:GITHUB_WORKSPACE "submodules\CHP_modelling") + New-Item -ItemType Directory -Force -Path "build" | Out-Null + Set-Location "build" + + conan install .. --output-folder=. --build=missing -s compiler.cppstd=17 -s arch=x86_64 + + cmake .. -DCMAKE_TOOLCHAIN_FILE="$PWD\conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release + + Set-Location $env:GITHUB_WORKSPACE + shell: pwsh \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97a135d..30785bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,23 @@ jobs: - version: 'lts' # The long term stable version (Linux) os: ubuntu-latest arch: x64 + - version: '1' + os: windows-latest + arch: x64 + - version: 'lts' + os: windows-latest + arch: x64 steps: - uses: actions/checkout@v4 with: - submodules: true # Fetch the submodules - fetch-depth: 0 # full clone + submodules: true + fetch-depth: 0 + + # Recommended: ensure MSVC environment for conan/cmake on Windows + - name: Setup MSVC Dev Cmd + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + - uses: ./.github/actions/before-script - uses: julia-actions/setup-julia@v2 with: @@ -41,7 +53,8 @@ jobs: ${{ runner.os }}-test- ${{ runner.os }}- - uses: julia-actions/julia-buildpkg@v1 - - name: Run Julia tests inside the testenv-enviornment in Miniconda + - name: Run Julia tests inside the testenv-enviornment in Miniconda (Linux) + if: runner.os == 'Linux' run: | source "$HOME/miniconda/etc/profile.d/conda.sh" conda init bash @@ -53,3 +66,13 @@ jobs: Pkg.test(; coverage=true) ' shell: bash + - name: Run Julia tests inside the testenv-environment in Miniconda (Windows) + if: runner.os == 'Windows' + run: | + $conda = Join-Path $env:USERPROFILE "miniconda\Scripts\conda.exe" + & $conda "shell.powershell" "hook" | Out-String | Invoke-Expression + conda activate testenv + $env:PATH = "$env:CONDA_PREFIX;$env:CONDA_PREFIX\Scripts;$env:PATH" + + julia --project=. -e 'using Pkg; ENV["PYTHON"] = joinpath(ENV["USERPROFILE"], "miniconda", "envs", "testenv", "python.exe"); Pkg.build("PyCall"); Pkg.test(; coverage=true)' + shell: pwsh diff --git a/NEWS.md b/NEWS.md index 58aa221..aabe291 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,18 +1,12 @@ # Release notes -## Version 0.1.0 (2025-04-xx) - -### Adjust for updates in submodules and update descriptive names for the EMGUI extension (2025-10-17) - -* Adjust for updates in submodules -* Add more descriptive names to the EMGUI extension -* Assume submodules are now stable enough not to need local result files in `test/data` +## Version 0.1.0 (2026-03-xx) ### Initial version of the package -* Provide sampling routines for C++ and Python for incorporation into `EnergyModelsX` models -* Utilize the sampling routines for sampling from: - * C++: `BioCHP` node +* Provide sampling routines for C++ and Python for incorporation into `EnergyModelsX` models. +* Utilize the sampling routines for sampling from:. + * C++: `BioCHP` node. * Python: `MultipleBuildingTypes`, `CSPandPV`, and `WindPower` nodes. -* Incorporation of a `BioResource` for `BiOCHP` plant -* Individual nodes to be moved after registration +* Incorporation of a `BioResource` for `BiOCHP` plant. +* Individual nodes to be moved after registration. diff --git a/docs/Project.toml b/docs/Project.toml index bc5e1c2..00c5d09 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,7 +1,9 @@ [deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterInterLinks = "d12716ef-a0f6-4df4-a9f1-a5a34e75c656" EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50" +EnergyModelsHeat = "ad1b8b27-e232-4da9-b498-bea9c19a30d7" EnergyModelsLanguageInterfaces = "672bb132-9406-42d0-9ef4-9203b0a61e9a" EnergyModelsRenewableProducers = "b007c34f-ba52-4995-ba37-fffe79fbde35" HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" diff --git a/docs/make.jl b/docs/make.jl index fb807c4..ce09908 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,8 +2,10 @@ using Documenter using DocumenterInterLinks using EnergyModelsBase using EnergyModelsRenewableProducers +using EnergyModelsHeat using EnergyModelsLanguageInterfaces using TimeStruct +using Dates using Literate const EMB = EnergyModelsBase @@ -20,6 +22,7 @@ Literate.markdown(inputfile, joinpath(@__DIR__, "src", "examples")) links = InterLinks( "TimeStruct" => "https://sintefore.github.io/TimeStruct.jl/stable/", "EnergyModelsBase" => "https://energymodelsx.github.io/EnergyModelsBase.jl/stable/", + "EnergyModelsRenewableProducers" => "https://energymodelsx.github.io/EnergyModelsRenewableProducers.jl/stable/", ) makedocs( @@ -37,7 +40,13 @@ makedocs( "Quick Start"=>"manual/quick-start.md", "Release notes"=>"manual/NEWS.md", ], - "Types for EMX elements" => Any["Reference"=>"types/reference.md"], + "Nodes" => Any[ + "BioCHP"=>"nodes/biochp.md", + "WindPower"=>"nodes/windpower.md", + "PVandCSP"=>"nodes/pvandcsp.md", + "MultipleBuildingTypes"=>"nodes/multiplebuildingtypes.md", + ], + "Resources" => Any["ResourceBio"=>"resources/resourcebio.md"], "Utility functions" => Any["Reference"=>"util-fun/reference.md"], "How-to" => Any["Contribute"=>"how-to/contribute.md", "Utilize"=>"how-to/utilize.md"], diff --git a/docs/src/how-to/utilize.md b/docs/src/how-to/utilize.md index 8947096..98b06ab 100644 --- a/docs/src/how-to/utilize.md +++ b/docs/src/how-to/utilize.md @@ -17,7 +17,8 @@ output = EMLI.call_python_function("my_python_package", "my_func"; input) If you want to install non-standard python packages and/or you want to sample a local package you must create a conda environment (using a conda installation), or an other environment management system (not tested yet), and install required - packages there. E.g., + packages there. *e.g.*, + ```bash conda create --name testenv python=3.10 conda activate testenv @@ -25,13 +26,16 @@ output = EMLI.call_python_function("my_python_package", "my_func"; input) cd path_to_your_python_project poetry install ``` + You must then (in Julia) set (it is here assumed you use miniconda on Windows) + ```julia using Pkg Pkg.add("PyCall") ENV["PYTHON"] = joinpath(homedir(), "AppData", "Local", "miniconda3", "envs", "testenv", "python.exe") Pkg.build("PyCall") ``` + and restart Julia. ### [Call C/C++ functions](@id how_to-utilize-ext_fun-Cpp) @@ -93,10 +97,11 @@ In Julia you can now activate your project and add `CxxWrap` through using CxxWrap CxxWrap.prefix_path() ``` + which should return `"/home/user/.julia/dev/libcxxwrap_julia_jll/override"`. !!! note - If this instead returns `"/home/user/.julia/artifacts/5016ccec96368c99a5a678ab3319d1da7bb9a2c7"`, create a n`Overrides.toml` file at `/home/user/.julia/artifacts` with the following content + If this instead returns `"/home/user/.julia/artifacts/5016ccec96368c99a5a678ab3319d1da7bb9a2c7"`, create an `Overrides.toml` file at `/home/user/.julia/artifacts` with the following content ```toml [3eaa8342-bff7-56a5-9981-c04077f7cee7] @@ -110,3 +115,205 @@ which should return `"/home/user/.julia/dev/libcxxwrap_julia_jll/override"`. You can try to clone `CxxWrap`, change the toml file to `libcxxwrap_julia_jll = "0.13.4"` (instead of `0.14.0` which is not available yet), and `develop` `CxxWrap` in your environment. An example is given by the *[trippling_module example](https://github.com/EnergyModelsX/EnergyModelsLanguageInterfaces.jl/tree/main/test/trippling_module/)*. + +## [Use implemented nodes](@id how_to-utilize-use_nodes) + +The nodes [`WindPower`](@ref WindPower), [`CSPandPV`](@ref CSPandPV) and [`MultipleBuildingTypes`](@ref MultipleBuildingTypes) have *[constructors](@ref lib-pub-sampling_constructors)* that sample [`wind_power_timeseries`](https://gitlab.sintef.no/harald.svendsen/wind_power_timeseries), [`Tecnalia_Solar-Energy-Model`](https://github.com/iDesignRES/Tecnalia_Solar-Energy-Model) and [`Tecnalia_Building-Stock-Energy-Model`](https://github.com/iDesignRES/Tecnalia_Building-Stock-Energy-Model), respectively. +These modules are python based and the usage of these *[constructors](@ref lib-pub-sampling_constructors)* requires installation of these as documented *[below](@ref how_to-utilize-use_nodes-python_modules)*. + +Additionally, the node [`BioCHP`](@ref BioCHP) has a *[constructor](@ref lib-pub-sampling_constructors)* that samples the [CHP_modelling](https://github.com/iDesignRES/CHP_modelling) module. +This module is `C++` based and the [constructor](@ref lib-pub-sampling_constructors) then requires compilation and build before usage as described further [below](@ref how_to-utilize-use_nodes-cpp_modules). + +The following installation guides will show how to install the modules to enable usage of these *[constructors](@ref lib-pub-sampling_constructors)* for both Windows and Linux. + +### [Clone repositories](@id how_to-utilize-use_nodes-clone_repos) + +Navigate to a folder in which you want to download required repositories and run + +```PowerShell +git clone --recurse-submodules git@github.com:EnergyModelsX/EnergyModelsLanguageInterfaces.jl.git +``` + +You should now be able to navigate to the main package folder in which the other modules are located (under the submodules folder) + +```PowerShell +cd EnergyModelsLanguageInterfaces.jl +``` + +!!! note "Install git" + If you do not have git available in your PowerShell, make sure to [download](https://git-scm.com/install/windows) and install it properly (make sure to enable adjustment of the PATH environment). + +### [Install python modules](@id how_to-utilize-use_nodes-python_modules) + +The following installs the modules using [`poetry`](https://pypi.org/project/poetry/) in a PowerShell in [VS code](https://code.visualstudio.com/). + +!!! note "Python installation" + You must have a python installation available in the terminal in VS code. + Python can be downloaded from [here](https://www.python.org/downloads/windows/) and installed by launching the downloaded installer (remember to "Add Python to PATH"). You must restart VS Code (and possibly any other open PowerShell windows) to have python available in the embedded VS code terminal. Check that you have python installed correctly with + + ```PowerShell + python --version + ``` + + which should return something like `Python 3.11.9`. + +Start by installing `poetry` using `pip` (which should be included in the python installation) + +```PowerShell +pip install poetry +``` + +Navigate to the submodule you want to install and run `poetry install`. If you want all python modules installed run the following + +```PowerShell +cd submodules/wind_power_timeseries +poetry install +cd ../.. +cd submodules/Tecnalia_Solar-Energy-Model +poetry install +cd ../.. +cd submodules/Tecnalia_Building-Stock-Energy-Model +poetry install +cd ../.. +``` + +If you want to be able to run the tests of the main repository later (see [Test modules](@ref how_to-utilize-use_nodes-test)), make sure to install the `python_module` (located in the `test/python_module` folder) + +```PowerShell +pip install highspy +cd test/python_module +poetry install +cd ../.. +``` + +!!! note "Environments" + If you are a developer, you probably want to install the python modules in a separate environment which can be done with, *e.g.*, [miniconda](https://www.anaconda.com/docs/getting-started/miniconda/install). + +Enable these by starting a julia session in the main folder + +```PowerShell +julia --project=. +``` + +and run the following + +```julia +using Pkg +Pkg.instantiate() +ENV["PYTHON"] = joinpath(homedir(), "AppData", "Local", "Programs", "Python", "Python311", "python.exe") +Pkg.build("PyCall") +``` + +followed by restarting Julia. + +!!! note "Path to Python executable" + The path in the previous commands must be adjusted to the path of your python executable which can be found with + + ```PowerShell + (Get-Command python).Source + ``` + +### [Install C++ modules](@id how_to-utilize-use_nodes-cpp_modules) + +Start by installing [`conan`](https://pypi.org/project/conan/) and create a default profile + +```PowerShell +pip install conan +conan profile detect +``` + +Navigate to the `CHP_modelling` folder, build and install the module with the following + +```PowerShell +cd submodules/CHP_modelling +mkdir build +cd build +conan install .. --output-folder=. --build=missing -s compiler.cppstd=17 -s arch=x86_64 +cmake .. -DCMAKE_TOOLCHAIN_FILE="${PWD}/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release +cmake --build . --config Release +cd ../../.. +``` + +### [Install modules on linux](@id how_to-utilize-use_nodes-linux) + +To perform the same installation above on linux you can navigate to a folder in which you want to download required repositories and run + +```bash +sudo apt-get update -qq +sudo apt-get install -y git glpk-utils g++ cmake wget curl python3-pip +pip install conan +conan profile detect +wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh +bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda +echo "$HOME/miniconda/bin" >> $GITHUB_PATH +source "$HOME/miniconda/etc/profile.d/conda.sh" + +conda create --name testenv python=3.11 -y +conda activate testenv +conda install -c conda-forge poetry -y + +git clone --recurse-submodules git@github.com:EnergyModelsX/EnergyModelsLanguageInterfaces.jl.git +cd "test/python_module" +poetry install +cd "../.." + +cd "submodules/wind_power_timeseries" +poetry install +cd "../.." + +cd "submodules/Tecnalia_Solar-Energy-Model" +poetry install +cd "../.." +cd "submodules/Tecnalia_Building-Stock-Energy-Model" +poetry install +cd "../.." + +cd "submodules/CHP_modelling" +mkdir build +cd build +conan install .. --output-folder=. --build=missing -s compiler.cppstd=17 -s arch=x86_64 +cmake .. -DCMAKE_TOOLCHAIN_FILE="${PWD}/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release +cmake --build . --config Release +cd "../../.." +``` + +Enable the python modules by starting a julia session in the main folder + +```PowerShell +julia --project=. +``` + +and run the following + +```julia +using Pkg; +Pkg.instantiate() +ENV["PYTHON"] = joinpath(ENV["HOME"], "miniconda", "envs", "testenv", "bin", "python"); +Pkg.build("PyCall"); +``` + +followed by restarting Julia. + +### [Test modules](@id how_to-utilize-use_nodes-test) + +All the mentioned [constructors](@ref lib-pub-sampling_constructors) have been included in the tests of the repository and you may therefore check if everything is properly set up by running these in julia. + +!!! note "Requirements" + The tests assumes that all modules listed in the [Install python modules](@ref how_to-utilize-use_nodes-python_modules) section and the [Install C++ modules](@ref how_to-utilize-use_nodes-cpp_modules) section has been installed. + +Start a new Julia session with + +```PowerShell +julia --project=. +``` + +and run the tests + +```julia +using Pkg +Pkg.test() +``` + +### [Utilize constructors](@id how_to-utilize-constructors) + +For detailed information on how to use the [constructors](@ref lib-pub-sampling_constructors), refer to the [test/utils.jl](https://github.com/EnergyModelsX/EnergyModelsLanguageInterfaces.jl/blob/main/test/utils.jl) file, which contains minimum working examples for both sampling the models and using saved sampled data. diff --git a/docs/src/library/public.md b/docs/src/library/public.md index 7424c8a..c1b6b5d 100644 --- a/docs/src/library/public.md +++ b/docs/src/library/public.md @@ -1,15 +1,69 @@ # [Public interface](@id lib-pub) -## [New nodal types](@id lib-pub-types) +## [New resource types](@id lib-pub-resource_types) + +```@docs +EnergyModelsLanguageInterfaces.ResourceBio +``` + +## [New nodal types](@id lib-pub-nodal_types) ```@docs EnergyModelsLanguageInterfaces.WindPower EnergyModelsLanguageInterfaces.CSPandPV EnergyModelsLanguageInterfaces.MultipleBuildingTypes -EnergyModelsLanguageInterfaces.ResourceBio EnergyModelsLanguageInterfaces.BioCHP ``` +## [Sampling constructors](@id lib-pub-sampling_constructors) + +```@docs +EnergyModelsLanguageInterfaces.WindPower( + ::Any, + ::TimeStruct.TimeProfile, + ::Dict, + ::String, + ::String, + ::TimeStruct.TimeProfile, + ::TimeStruct.TimeProfile, + ::Dict{<:EnergyModelsBase.Resource,<:Real}, +) +EnergyModelsLanguageInterfaces.CSPandPV( + ::Any, + ::Dict, + ::DateTime, + ::DateTime, + ::Dict{String,<:EnergyModelsBase.Resource}; + data::Vector{<:Data} = Data[], + method::String = "Ninja", + data_path::String = "", + source::String = "NORA3", +) +EnergyModelsLanguageInterfaces.MultipleBuildingTypes( + ::Any, + ::Dict, + ::DateTime, + ::DateTime, + ::Vector{String}, + ::Dict{String,<:EnergyModelsBase.Resource}, + ::TimeStruct.TimeStructure, + ::Dict{<:EnergyModelsBase.Resource,<:TimeStruct.TimeProfile}, + ::Dict{<:EnergyModelsBase.Resource,<:TimeStruct.TimeProfile}; + data::Vector{<:EnergyModelsBase.Data} = EnergyModelsBase.Data[], + data_location::String = joinpath(tempdir(), "buildings"), + overwrite_saved_data::Bool = false, +) +EnergyModelsLanguageInterfaces.BioCHP( + ::Any, + ::TimeStruct.TimeProfile, + ::Dict{<:EnergyModelsLanguageInterfaces.ResourceBio,<:Real}, + ::Dict{<:EnergyModelsHeat.ResourceHeat,<:Real}, + ::EnergyModelsBase.Resource; + data::Vector{<:EnergyModelsBase.Data} = EnergyModelsBase.Data[], + libpath::String = joinpath(@__DIR__, "..", "..", "CHP_modelling", "build", "lib", "libbioCHP_wrapper.so"), +) +``` + ## [Utility functions](@id lib-pub-util_fun) ```@docs diff --git a/docs/src/nodes/biochp.md b/docs/src/nodes/biochp.md new file mode 100644 index 0000000..859d790 --- /dev/null +++ b/docs/src/nodes/biochp.md @@ -0,0 +1,167 @@ +# [BioCHP](@id nodes-BioCHP) + +The [`BioCHP`](@ref) node represents a biomass-fired combined heat and power (CHP) plant. + +!!! note "Sampling CHP_modelling module" + To use the [constructor](@ref lib-pub-sampling_constructors) that samples the [CHP_modelling](https://github.com/iDesignRES/CHP_modelling) module, follow the installation in the [Use nodes](@ref how_to-utilize-use_nodes) section. + +The `BioCHP` utilizes linear, time-independent conversion rates from the `input` [`Resource`](@extref EnergyModelsBase.Resource)s to the `output` [`Resource`](@extref EnergyModelsBase.Resource)s, subject to the available capacity. +Compared to a standard [`NetworkNode`](@extref EnergyModelsBase.NetworkNode), `BioCHP` differs in its outlet-flow constraints. +The electric touput is enfoced based on the capacity usage while the heat output is bound to an upper value, but can also be 0. + +## [Introduced types and their fields](@id nodes-BioCHP-fields) + +The [`BioCHP`](@ref) is a subtype of the [`NetworkNode`](@extref EnergyModelsBase.NetworkNode). +It uses the standard `NetworkNode` functions from `EnergyModelsBase`. + +### [Standard fields](@id nodes-BioCHP-fields-stand) + +- **`id`**:\ + The field `id` is only used for providing a name to the node. + This is similar to the approach utilized in `EnergyModelsBase`. + +- **`cap::TimeProfile`**:\ + The installed capacity corresponds to the nominal capacity of the node.\ + If the node should contain investments through the application of [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/), it is important to note that you can only use `FixedProfile` or `StrategicProfile` for the capacity, but not `RepresentativeProfile` or `OperationalProfile`.\ + In addition, all values have to be non-negative. + +- **`opex_var::TimeProfile`**:\ + The variable operating expenses are based on the capacity utilization through the variable [`:cap_use`](@extref EnergyModelsBase man-opt_var-cap). + Hence, it is directly related to the specified `output` ratios. + The variable operating expenses can be provided as `OperationalProfile` as well. + +- **`opex_fixed::TimeProfile`**:\ + The fixed operating expenses are relative to the installed capacity (through the field `cap`) and the chosen duration of a strategic period as outlined on *[Utilize `TimeStruct`](@extref EnergyModelsBase how_to-utilize_TS)*.\ + It is important to note that you can only use `FixedProfile` or `StrategicProfile` for the fixed OPEX, but not `RepresentativeProfile` or `OperationalProfile`. + In addition, all values have to be non-negative. + +- **`output::Dict{<:Resource, <:Real}`**:\ + The field `output` includes the output [`Resource`](@extref EnergyModelsBase.Resource)s with their corresponding conversion factors as dictionaries. + It **must** include the `electricity_resource` (see below). + It is also possible to include other resources in addition to the chosen `electricity_resource`. + All other [`Resource`](@extref EnergyModelsBase.Resource)s can be produced up to the specified value of the capacity utilization, but they do not need to be produced. + +- **`data::Vector{<:ExtensionData}`**:\ + An entry for providing additional data to the model. + In the current version, it is only relevant for additional investment data when [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/) is used or for additional emission data through [`EmissionsProcess`](@extref EnergyModelsBase.EmissionsProcess). + The latter would correspond to uncaptured CO₂ that should be included in the analyses. + !!! note + The field `data` is not required as we include a constructor when the value is excluded. + +### [New fields](@id nodes-BioCHP-fields-new) + +- **`electricity_resource::Resource`**:\ + The electric power resource produced by the CHP plant. + This field is used to distinguish electricity from (potential) heat outputs in the outlet-flow constraints. + +- **`input::Dict{<:ResourceBio, <:Real}`**:\ + The biomass input resources (of type [`ResourceBio`](@ref)) and their conversion factors. + These conversion factors are normalized to the capacity definition of the node. + + The difference to the standard [`RefNetworkNode`](@extref EnergyModelsBase nodes-network_node) is that the input must be a [`ResourceBio`](@ref). + +!!! note "Default `data` constructor" + The provided constructor assigns `data = [EmissionsEnergy()]` by default. + This means the node includes energy-based emission accounting unless overwritten by explicitly constructing `BioCHP` with a custom `data` vector. + +## [Mathematical description](@id nodes-BioCHP-math) + +In the following mathematical equations, we use the names for variables and functions used in the model. +Variables are in general represented as + +``\texttt{var\_example}[index_1, index_2]`` + +with square brackets, while functions are represented as + +``func\_example(index_1, index_2)`` + +with parantheses. + +### [Variables](@id nodes-BioCHP-math-var) + +The [`BioCHP`](@ref) node uses standard `NetworkNode` variables, as described on the page *[Optimization variables](@extref EnergyModelsBase man-opt_var)*. +The variables include: + +- [``\texttt{opex\_var}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{opex\_fixed}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{cap\_use}``](@extref EnergyModelsBase man-opt_var-cap) +- [``\texttt{cap\_inst}``](@extref EnergyModelsBase man-opt_var-cap) +- [``\texttt{flow\_in}``](@extref EnergyModelsBase man-opt_var-flow) +- [``\texttt{flow\_out}``](@extref EnergyModelsBase man-opt_var-flow) + +### [Constraints](@id nodes-BioCHP-math-con) + +The following sections omit the direct inclusion of the vector of heat pump nodes. +Instead, it is implicitly assumed that the constraints are valid ``\forall n ∈ N^{BioCHP}`` for all [`BioCHP`](@ref) nodes if not stated differently. +In addition, all constraints are valid ``\forall t \in T`` (that is in all operational periods) or ``\forall t_{inv} \in T^{Inv}`` (that is in all strategic periods). + +#### [Standard constraints](@id nodes-BioCHP-math-con-stand) + +`BioCHP` nodes utilize in general the standard constraints described on +*[Constraint functions](@extref EnergyModelsBase man-con)* for `NetworkNode`s. +These standard constraints are: + +- `constraints_capacity`: + + ```math + \texttt{cap\_use}[n, t] \leq \texttt{cap\_inst}[n, t] + ``` + +- `constraints_capacity_installed`: + + ```math + \texttt{cap\_inst}[n, t] = capacity(n, t) + ``` + + !!! tip "Using investments" + The function `constraints_capacity_installed` is also used in [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/) to incorporate the potential for investment. + Nodes with investments are then no longer constrained by the parameter capacity. + +- `constraints_flow_out`: + + ```math + \texttt{flow\_out}[n, t, p] = + outputs(n, p) \times \texttt{cap\_use}[n, t] + \qquad \forall p \in outputs(n) \setminus \{\text{CO}_2\} + ``` + +- `constraints_opex_fixed`: + + ```math + \texttt{opex\_fixed}[n, t_{inv}] = opex\_fixed(n, t_{inv}) \times \texttt{cap\_inst}[n, first(t_{inv})] + ``` + + !!! tip "Why do we use `first()`" + The variable ``\texttt{cap\_inst}`` is declared over all operational periods (see the section on *[Capacity variables](@extref EnergyModelsBase man-opt_var-cap)* for further explanations). + Hence, we use the function ``first(t_{inv})`` to retrieve the installed capacity in the first operational period of a given strategic period ``t_{inv}`` in the function `constraints_opex_fixed`. + +- `constraints_opex_var`: + + ```math + \texttt{opex\_var}[n, t_{inv}] = \sum_{t \in t_{inv}} opex\_var(n, t) \times \texttt{cap\_use}[n, t] \times scale\_op\_sp(t_{inv}, t) + ``` + + !!! tip "The function `scale_op_sp`" + The function [``scale\_op\_sp(t_{inv}, t)``](@extref EnergyModelsBase.scale_op_sp) calculates the scaling factor between operational and strategic periods. + It also takes into account potential operational scenarios and their probability as well as representative periods. + +- `constraints_ext_data`:\ + This function is only called for specified data of the [`BioCHP`](@ref) node, see above. + +The function `constraints_flow_out` is extended with a new method for BioCHP nodes such that the outputs are flexible with respect to output resources not being the `electricity_resource`. + +Let ``\mathcal{P}^{out}(n)`` denote the set of output resources of node ``n`` excluding CO₂ and `electricity_resource`. The implemented constraint is + +```math +\texttt{flow\_out}[n, t, p] = outputs(n, p) \times \texttt{cap\_use}[n, t] \qquad \forall p \in \mathcal{P}^{out}(n) +``` + +For the `electricity_resource` we still have + +```math +\texttt{flow\_out}[n, t, electricity_resource(n)] = outputs(n, electricity_resource(n)) \times \texttt{cap\_use}[n, t] +``` + +#### [Additional constraints](@id nodes-BioCHP-math-con-add) + +[`BioCHP`](@ref) nodes do not add additional constraint functions or constraints in the `create_node` function. diff --git a/docs/src/nodes/multiplebuildingtypes.md b/docs/src/nodes/multiplebuildingtypes.md new file mode 100644 index 0000000..9ad82d0 --- /dev/null +++ b/docs/src/nodes/multiplebuildingtypes.md @@ -0,0 +1,154 @@ +# [Multiple building types sink node](@id nodes-MultipleBuildingTypes) + +The [`MultipleBuildingTypes`](@ref) node creates sinks for all demand resources with penalties for both surplus and deficit. +The implementation uses `Dict` structures for the fields `cap`, `penalty_surplus`, and `penalty_deficit` to facilitate multiple [Resource](@extref EnergyModelsBase.Resource)s. +This approach allows modeling building demands with flexible penalty mechanisms for over- and under-supply. +The type is also used to enable a specialized constructor that samples the [`Tecnalia_Building-Stock-Energy-Model`](https://github.com/iDesignRES/Tecnalia_Building-Stock-Energy-Model) module. + +!!! note "Sampling Tecnalia_Building-Stock-Energy-Model module" + To use the [constructor](@ref lib-pub-sampling_constructors) for [`MultipleBuildingTypes`](@ref) that samples the [`Tecnalia_Building-Stock-Energy-Model`](https://github.com/iDesignRES/Tecnalia_Building-Stock-Energy-Model) module, follow the installation in the [Use nodes](@ref how_to-utilize-use_nodes) section. + +!!! danger + Investments are currently not available for this node. + +## [Introduced type and its field](@id nodes-MultipleBuildingTypes-fields) + +The [`MultipleBuildingTypes`](@ref) is a subtype of [`Sink`](@extref EnergyModelsBase.Sink) and is implemented as a specialized sink node. +Hence, it utilizes the same functions declared in `EnergyModelsBase`. + +### [Standard fields](@id nodes-MultipleBuildingTypes-fields-stand) + +Standard fields of a [`MultipleBuildingTypes`](@ref) node are given as: + +- **`id`**:\ + The field `id` is only used for providing a name to the node. + This is similar to the approach utilized in `EnergyModelsBase`. +- **`input::Dict{<:Resource, <:Real}`**:\ + The field `input` includes [`Resource`](@extref EnergyModelsBase.Resource)s with their corresponding conversion factors as dictionaries. + All values have to be non-negative. +- **`data::Vector{Data}`**:\ + An entry for providing additional data to the model. + In the current version, it is not applicable. We intend to change this in future releases to enable investments. + + !!! note "Constructor for `MultipleBuildingTypes`" + The field `data` is not required as we include a constructor when the value is excluded. + + !!! danger "Using `CaptureData`" + As a `Sink` node does not have any output, it is not possible to utilize `CaptureData`. + If you still plan to specify it, you will receive an error in the model building. + +### [Additional fields](@id nodes-MultipleBuildingTypes-fields-new) + +[`MultipleBuildingTypes`](@ref) nodes introduce additional fields for demand and penalty specifications: + +- **`cap::Dict{<:Resource,<:TimeProfile}`**:\ + The demand capacity for each of the input resources. + All values have to be non-negative. +- **`penalty_surplus::Dict{<:Resource,<:TimeProfile}`**:\ + The penalties applied for surplus (over-supply) for each of the input resources. + These penalties are added to the variable operating expenses. + All values have to be non-negative. +- **`penalty_deficit::Dict{<:Resource,<:TimeProfile}`**:\ + The penalties applied for deficit (under-supply) for each of the input resources. + These penalties are added to the variable operating expenses. + All values have to be non-negative. + +## [Mathematical description](@id nodes-MultipleBuildingTypes-math) + +In the following mathematical equations, we use the name for variables and functions used in the model. +Variables are in general represented as + +``\texttt{var\_example}[index_1, index_2]`` + +with square brackets, while functions are represented as + +``func\_example(index_1, index_2)`` + +with parantheses. + +### [Variables](@id nodes-MultipleBuildingTypes-math-var) + +#### [Standard variables](@id nodes-MultipleBuildingTypes-math-var-stand) + +The [`MultipleBuildingTypes`](@ref) node type utilizes standard variables from the [`Sink`](@extref EnergyModelsBase.Sink) node type and includes: + +- [``\texttt{opex\_var}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{opex\_fixed}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{flow\_in}``](@extref EnergyModelsBase man-opt_var-flow) +- [``\texttt{sink\_surplus}``](@extref EnergyModelsBase man-opt_var-sink): Declared as the total surplus aggregated across all resources. +- [``\texttt{sink\_deficit}``](@extref EnergyModelsBase man-opt_var-sink): Declared as the total surplus aggregated across all resources. +- [``\texttt{emissions\_node}``](@extref EnergyModelsBase man-opt_var-emissions) if `EmissionsData` is added to the field `data` + +!!! note "cap\_use and cap\_inst" + A `MultipleBuildingTypes` has an individual capacity for all its resources, that is each `Resource` has its own capacity which must be satisfied. + As a consequence, the standard variables [``\texttt{cap\_use}``](@extref EnergyModelsBase man-opt_var-cap) and [``\texttt{cap\_use}``](@extref EnergyModelsBase man-opt_var-cap) are not defined for [`MultipleBuildingTypes`](@ref) nodes through a new method for the function [`has_capacity`](@ref EnergyModelsBase.capacity). + +#### [Additional variables](@id nodes-MultipleBuildingTypes-math-add) + +[`MultipleBuildingTypes`](@ref) introduces the following variables: + +- ``\texttt{buildings\_surplus}[n, t, p]``: Surplus (over-supply) for node ``n`` in operational period ``t`` for resource ``p``. +- ``\texttt{buildings\_deficit}[n, t, p]``: Deficit (under-supply) for node ``n`` in operational period ``t`` for resource ``p``. +- ``\texttt{sink\_surplus}[n, t]``: Total surplus aggregated across all resources. +- ``\texttt{sink\_deficit}[n, t]``: Total deficit aggregated across all resources. + +### [Constraints](@id nodes-MultipleBuildingTypes-math-con) + +The following sections omit the direct inclusion of the vector of [`MultipleBuildingTypes`](@ref) nodes. +Instead, it is implicitly assumed that the constraints are valid ``\forall n ∈ N^{\text{MultipleBuildingTypes}}`` if not stated differently. +In addition, all constraints are valid ``\forall t \in T`` (that is in all operational periods) or ``\forall t_{inv} \in T^{Inv}`` (that is in all strategic periods). +Finally, all constraints are valid ``\forall p \in inputs(n)`` (that is in all input resources). + +#### [Standard constraints](@id nodes-MultipleBuildingTypes-math-con-stand) + +[`MultipleBuildingTypes`](@ref) nodes utilize the following standard constraint functions: + +- `constraints_opex_fixed`:\ + The current implementation fixes the fixed operating expenses of a sink to 0. + + ```math + \texttt{opex\_fixed}[n, t_{inv}] = 0 + ``` + +- `constraints_ext_data`:\ + This function is only called for specified additional data, see above. + +The function `constraints_capacity` is extended with a new method to account for the multiple inputs that must be satisfied within this `Sink` node: + +```math +\frac{\texttt{flow\_in}[n, t, p]}{inputs(n, p)} + \texttt{buildings\_deficit}[n, t, p] = +capacity(n, t, p) + \texttt{buildings\_surplus}[n, t, p] +\qquad \forall p \in inputs(n) +``` + +```math +\texttt{sink\_deficit}[n, t] = \texttt{buildings\_deficit}[n, t, p] +``` + +```math +\texttt{sink\_surplus}[n, t] = \texttt{buildings\_surplus}[n, t, p] +``` + +The function `constraints_flow_in` is extended with a new method that does not add any constraint. + +The function `constraints_capacity_installed` is not called within `constraints_capacity`. +Thus, the node does not support investments. + +The function `constraints_opex_var` is extended with a new method to allow for individual penalties for the different demands. + +```math +\texttt{opex\_var}[n, t_{inv}] = \sum_{t \in t_{inv},\, p \in inputs(n)} +\left( +\texttt{buildings\_surplus}[n, t, p] \times penalty\_surplus(n, t, p) + +\texttt{buildings\_deficit}[n, t, p] \times penalty\_deficit(n, t, p) +\right) \times scale\_op\_sp(t_{inv}, t) +``` + +!!! tip "The function `scale_op_sp`" + The function [``scale\_op\_sp(t_{inv}, t)``](@extref EnergyModelsBase.scale_op_sp) calculates the scaling factor between operational and strategic periods. + It also takes into account potential operational scenarios and their probability as well as representative periods. + + +#### [Additional constraints](@id nodes-MultipleBuildingTypes-math-con-add) + +[`MultipleBuildingTypes`](@ref) nodes do not add additional constraints. diff --git a/docs/src/nodes/pvandcsp.md b/docs/src/nodes/pvandcsp.md new file mode 100644 index 0000000..aaaef6c --- /dev/null +++ b/docs/src/nodes/pvandcsp.md @@ -0,0 +1,155 @@ +# [PV and CSP source node](@id nodes-CSPandPV) + +PV and CSP source nodes can generate multiple different resources simultaneously. +The standard approach would be to include electricity and heat from solar power, both *via* solar PV and concentrated solar power. +The implementation of the node is hence similar to that of [`NonDisRES`](@extref EnergyModelsRenewableProducers nodes-nondisres) but uses dictionaries for the fields `cap`, `profile`, `opex_var` and `opex_fixed` to facilitate multiple [`Resource`](@extref EnergyModelsBase.Resource)s (both electricity and heat outputs). +The type is also used to enable a specialized constructor that samples the [Tecnalia_Solar-Energy-Model](https://github.com/iDesignRES/Tecnalia_Solar-Energy-Model) module. + +!!! note "Sampling Tecnalia_Solar-Energy-Model module" + To use the [constructor](@ref lib-pub-sampling_constructors) for [`CSPandPV`](@ref) that samples the [Tecnalia_Solar-Energy-Model](https://github.com/iDesignRES/Tecnalia_Solar-Energy-Model) module, follow the installation in the [Use nodes](@ref how_to-utilize-use_nodes) section. + +!!! danger + Investments are currently not available for this node. + +## [Introduced type and its field](@id nodes-CSPandPV-fields) + +The [`CSPandPV`](@ref) is a subtype of [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES) (the same is the case for [NonDisRES](@extref EnergyModelsRenewableProducers nodes-nondisres)) and is thus implemented as equivalent to a [`RefSource`](@extref EnergyModelsBase.RefSource). +Hence, it utilizes the same functions declared in `EnergyModelsBase`. + +### [Standard fields](@id nodes-CSPandPV-fields-stand) + +Standard fields (of an [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES)) being reused are given as: + +- **`id`**:\ + The field `id` is only used for providing a name to the node. + This is similar to the approach utilized in `EnergyModelsBase`. +- **`output::Dict{<:Resource, <:Real}`**:\ + The field `output` includes [`Resource`](@extref EnergyModelsBase.Resource)s with their corresponding conversion factors as dictionaries. + In the case of a PV and CSP energy source, `output` should always include your *electricity* resource and a *heat* resource. In practice, you should use a value of 1.\ + All values have to be non-negative. +- **`data::Vector{Data}`**:\ + An entry for providing additional data to the model. + In the current version, it is not applicable. We intend to change this in future releases to enable investments. + + !!! note "Constructor for `CSPandPV`" + The field `data` is not required as we include a constructor when the value is excluded. + +### [Additional fields](@id nodes-CSPandPV-fields-new) + +[`CSPandPV`](@ref) nodes alter the types of some fields compared to a [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES): + +- **`cap::Dict{<:Resource,<:TimeProfile}`**:\ + The installed capacity corresponds to the nominal capacity of the node for each of the produced resources.\ + If the node should contain investments through the application of [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/), it is important to note that you can only use `FixedProfile` or `StrategicProfile` for the capacity, but not `RepresentativeProfile` or `OperationalProfile`. + In addition, all values have to be non-negative. +- **`profile::Dict{<:Resource,<:TimeProfile}`**:\ + The profiles are used as a multiplier to the installed capacity to represent the maximum actual capacity in each operational period for each of the produced resources. + The profiles should be provided as `OperationalProfile` or at least as `RepresentativeProfile`. + In addition, all values should be in the range ``[0, 1]``. +- **`opex_var::Dict{<:Resource,<:TimeProfile}`**:\ + The variable operating expenses are based on the capacity utilization through the variable [`:cap_use`](@extref EnergyModelsBase man-opt_var-cap) for each of the produced resources. + Hence, it is directly related to the specified `output` ratios. + The variable operating expenses can be provided as `OperationalProfile` as well. +- **`opex_fixed::Dict{<:Resource,<:TimeProfile}`**:\ + The fixed operating expenses are relative to the installed capacity (through the field `cap`) for each of the produced resources and the chosen duration of a strategic period as outlined on *[Utilize `TimeStruct`](@extref EnergyModelsBase how_to-utilize_TS)*.\ + It is important to note that you can only use `FixedProfile` or `StrategicProfile` for the fixed OPEX, but not `RepresentativeProfile` or `OperationalProfile`. + In addition, all values have to be non-negative. + +## [Mathematical description](@id nodes-CSPandPV-math) + +In the following mathematical equations, we use the name for variables and functions used in the model. +Variables are in general represented as + +``\texttt{var\_example}[index_1, index_2]`` + +with square brackets, while functions are represented as + +``func\_example(index_1, index_2)`` + +with parantheses. + +### [Variables](@id nodes-CSPandPV-math-var) + +#### [Standard variables](@id nodes-CSPandPV-math-var-stand) + +The PV and CSP source node types utilize all standard variables from the [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES) node type. +The variables include: + +- [``\texttt{opex\_var}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{opex\_fixed}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{cap\_inst}``](@extref EnergyModelsBase man-opt_var-cap) +- [``\texttt{flow\_out}``](@extref EnergyModelsBase man-opt_var-flow) +- [``\texttt{emissions\_node}``](@extref EnergyModelsBase man-opt_var-emissions) if `EmissionsData` is added to the field `data`. +- [``\texttt{curtailment}[n, t]``](@extref EnergyModelsRenewableProducers nodes-nondisres-math-add): For [`CSPandPV`](@ref), this variable is the sum of curtailed energy (as rate) of source ``n`` in operational period ``t``.\ + +!!! note + Non-dispatchable renewable energy source nodes are not compatible with `CaptureData`. + Hence, you can only provide [`EmissionsProcess`](@extref EnergyModelsBase.EmissionsProcess) to the node. + It is our aim to include the potential for construction emissions in a latter stage + +#### [Additional variables](@id nodes-CSPandPV-math-add) + +[`CSPandPV`](@ref) replaces the variables [``\texttt{cap\_use}``](@extref EnergyModelsBase man-opt_var-cap) and [``\texttt{curtailment}``](@extref EnergyModelsRenewableProducers nodes-nondisres-math-add) variables with the following variables + +- ``\texttt{solar\_cap\_use}[n, t, p]``: The capacity usage of source ``n`` in operational period ``t`` for resource ``p``. +- ``\texttt{solar\_curtailment}[n, t, p]``: Curtailed capacity of source ``n`` in operational period ``t`` for resource ``p`` with a typical unit of MW.\ + The curtailed resources specifies the unused generation capacity of sources. + It is currently only used in the calculation, but not with a cost. + +### [Constraints](@id nodes-CSPandPV-math-con) + +The following sections omit the direct inclusion of the vector of PV and CSP source nodes. +Instead, it is implicitly assumed that the constraints are valid ``\forall n ∈ N^{\text{CSPandPV}\_source}`` for all [`CSPandPV`](@ref) types if not stated differently. +In addition, all constraints are valid ``\forall t \in T`` (that is in all operational periods) or ``\forall t_{inv} \in T^{Inv}`` (that is in all strategic periods). +Finally, all constraints are valid ``\forall p \in outputs(n)`` (that is in all output resources). + +#### [Standard constraints](@id nodes-CSPandPV-math-con-stand) + +[`CSPandPV`](@ref) nodes utilize specialized constraint functions that extend the standard approach to accommodate multiple resources with per-resource tracking. +These constraints are: + +- `constraints_capacity`: + + ```math + \texttt{solar\_cap\_use}[n, t, p] \leq capacity(n, t, p) + ``` + + ```math + \texttt{solar\_cap\_use}[n, t, p] + \texttt{solar\_curtailment}[n, t, p] = + profile(n, t, p) \times capacity(n, t, p) + \qquad \forall p \in outputs(n) + ``` + + ```math + \sum_{p \in outputs(n)} \texttt{solar\_curtailment}[n, t, p] = \texttt{curtailment}[n, t] + ``` + + !!! note "constraints_capacity_installed" + The function `constraints_capacity_installed` is not used and the node thus currently does not support investments. + +- `constraints_flow_out`: + + ```math + \texttt{flow\_out}[n, t, p] = + \texttt{solar\_cap\_use}[n, t, p] \times outputs(n, p) + \qquad \forall p \in outputs(n) \setminus \{\text{CO}_2\} + ``` + +- `constraints_opex_fixed`: + + ```math + \texttt{opex\_fixed}[n, t_{inv}] = \sum_{p \in outputs(n)} opex\_fixed(n, t_{inv}, p) \times capacity(n, first(t_{inv}), p) + ``` + +- `constraints_opex_var`: + + ```math + \texttt{opex\_var}[n, t_{inv}] = \sum_{t \in t_{inv},\, p \in outputs(n)} \texttt{solar\_cap\_use}[n, t, p] \times opex\_var(n, t, p) \times scale\_op\_sp(t_{inv}, t) + ``` + +- `constraints_data`:\ + This function is only called for specified data of the node, see above. + +#### [Additional constraints](@id nodes-CSPandPV-math-con-add) + +[`CSPandPV`](@ref) nodes do not add additional constraints. diff --git a/docs/src/nodes/windpower.md b/docs/src/nodes/windpower.md new file mode 100644 index 0000000..2b28583 --- /dev/null +++ b/docs/src/nodes/windpower.md @@ -0,0 +1,161 @@ +# [Wind power source node](@id nodes-WindPower) + +Wind power sources generate electricity from wind. +The implementation of the node is identical to that of [NonDisRES](@extref EnergyModelsRenewableProducers nodes-nondisres) and is here used to enable a specialized constructor that samples the [`wind_power_timeseries`](https://gitlab.sintef.no/harald.svendsen/wind_power_timeseries) module. + +!!! note "Sampling wind\_power\_timeseries module" + To use the [constructor](@ref lib-pub-sampling_constructors) for [`WindPower`](@ref) that samples the [`wind_power_timeseries`](https://gitlab.sintef.no/harald.svendsen/wind_power_timeseries) module, follow the installation in the [Use nodes](@ref how_to-utilize-use_nodes) section. + +## [Introduced type and its field](@id nodes-WindPower-fields) + +The [`WindPower`](@ref) is a subtype of [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES) (the same is the case for [NonDisRES](@extref EnergyModelsRenewableProducers nodes-nondisres)) and is thus implemented as equivalent to a [`RefSource`](@extref EnergyModelsBase.RefSource). +Hence, it utilizes the same functions declared in `EnergyModelsBase`. + +### [Standard fields](@id nodes-WindPower-fields-stand) + +The standard fields (of a [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES)) are given as: + +- **`id`**:\ + The field `id` is only used for providing a name to the node. + This is similar to the approach utilized in `EnergyModelsBase`. +- **`cap::TimeProfile`**:\ + The installed capacity corresponds to the nominal capacity of the node.\ + If the node should contain investments through the application of [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/), it is important to note that you can only use `FixedProfile` or `StrategicProfile` for the capacity, but not `RepresentativeProfile` or `OperationalProfile`. + In addition, all values have to be non-negative. +- **`profile::TimeProfile`**:\ + The profile is used as a multiplier to the installed capacity to represent the maximum actual capacity in each operational period. + The profile should be provided as `OperationalProfile` or at least as `RepresentativeProfile`. + In addition, all values should be in the range ``[0, 1]``. +- **`opex_var::TimeProfile`**:\ + The variable operating expenses are based on the capacity utilization through the variable [`:cap_use`](@extref EnergyModelsBase man-opt_var-cap). + Hence, it is directly related to the specified `output` ratios. + The variable operating expenses can be provided as `OperationalProfile` as well. +- **`opex_fixed::TimeProfile`**:\ + The fixed operating expenses are relative to the installed capacity (through the field `cap`) and the chosen duration of a strategic period as outlined on *[Utilize `TimeStruct`](@extref EnergyModelsBase how_to-utilize_TS)*.\ + It is important to note that you can only use `FixedProfile` or `StrategicProfile` for the fixed OPEX, but not `RepresentativeProfile` or `OperationalProfile`. + In addition, all values have to be non-negative. +- **`output::Dict{<:Resource, <:Real}`**:\ + The field `output` includes [`Resource`](@extref EnergyModelsBase.Resource)s with their corresponding conversion factors as dictionaries. + In the case of a non-dispatchable renewable energy source, `output` should always include your *electricity* resource.In practice, you should use a value of 1.\ + All values have to be non-negative. +- **`data::Vector{Data}`**:\ + An entry for providing additional data to the model. + In the current version, it is only relevant for additional investment data when [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/) is used. + + !!! note "Constructor for `WindPower`" + The field `data` is not required as we include a constructor when the value is excluded. + +### [Additional fields](@id nodes-WindPower-fields-new) + +[`WindPower`](@ref) adds no additional fields to that of [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES). + +## [Mathematical description](@id nodes-WindPower-math) + +In the following mathematical equations, we use the name for variables and functions used in the model. +Variables are in general represented as + +``\texttt{var\_example}[index_1, index_2]`` + +with square brackets, while functions are represented as + +``func\_example(index_1, index_2)`` + +with parantheses. + +### [Variables](@id nodes-WindPower-math-var) + +#### [Standard variables](@id nodes-WindPower-math-var-stand) + +The wind power source node types utilize all standard variables from the [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES) node type. +The variables include: + +- [``\texttt{opex\_var}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{opex\_fixed}``](@extref EnergyModelsBase man-opt_var-opex) +- [``\texttt{cap\_use}``](@extref EnergyModelsBase man-opt_var-cap) +- [``\texttt{cap\_inst}``](@extref EnergyModelsBase man-opt_var-cap) +- [``\texttt{flow\_out}``](@extref EnergyModelsBase man-opt_var-flow) +- [``\texttt{emissions\_node}``](@extref EnergyModelsBase man-opt_var-emissions) if `EmissionsData` is added to the field `data`. +- [``\texttt{curtailment}[n, t]``](@extref EnergyModelsRenewableProducers nodes-nondisres-math-add): Curtailed energy of source ``n`` in operational period ``t`` with a typical unit of MW.\ + The curtailed electricity specifies the unused generation (as rate) of the wind power source. + It is currently only used in the calculation, but not with a cost. + +!!! note + Non-dispatchable renewable energy source nodes are not compatible with `CaptureData`. + Hence, you can only provide [`EmissionsProcess`](@extref EnergyModelsBase.EmissionsProcess) to the node. + It is our aim to include the potential for construction emissions in a latter stage + +#### [Additional variables](@id nodes-WindPower-math-add) + +[`WindPower`](@ref) adds no additional variables to that of [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES). + +### [Constraints](@id nodes-WindPower-math-con) + +The following sections omit the direct inclusion of the vector of wind power nodes. +Instead, it is implicitly assumed that the constraints are valid ``\forall n ∈ N^{\text{WindPower}\_source}`` for all [`WindPower`](@ref) types if not stated differently. +In addition, all constraints are valid ``\forall t \in T`` (that is in all operational periods) or ``\forall t_{inv} \in T^{Inv}`` (that is in all strategic periods). + +#### [Standard constraints](@id nodes-WindPower-math-con-stand) + +Wind power source nodes utilize in general the standard constraints described on *[Constraint functions](@extref EnergyModelsRenewableProducers nodes-nondisres-math-con)*. +These standard constraints are: + +- `constraints_capacity_installed`: + + ```math + \texttt{cap\_inst}[n, t] = capacity(n, t) + ``` + + !!! tip "Using investments" + The function `constraints_capacity_installed` is also used in [`EnergyModelsInvestments`](https://energymodelsx.github.io/EnergyModelsInvestments.jl/stable/) to incorporate the potential for investment. + Nodes with investments are then no longer constrained by the parameter capacity. + +- `constraints_flow_out`: + + ```math + \texttt{flow\_out}[n, t, p] = + outputs(n, p) \times \texttt{cap\_use}[n, t] + \qquad \forall p \in outputs(n) \setminus \{\text{CO}_2\} + ``` + +- `constraints_opex_fixed`: + + ```math + \texttt{opex\_fixed}[n, t_{inv}] = opex\_fixed(n, t_{inv}) \times \texttt{cap\_inst}[n, first(t_{inv})] + ``` + + !!! tip "Why do we use `first()`" + The variables ``\texttt{cap\_inst}`` are declared over all operational periods (see the section on *[Capacity variables](@extref EnergyModelsBase man-opt_var-cap)* for further explanations). + Hence, we use the function ``first(t_{inv})`` to retrieve the installed capacities in the first operational period of a given strategic period ``t_{inv}`` in the function `constraints_opex_fixed`. + +- `constraints_opex_var`: + + ```math + \texttt{opex\_var}[n, t_{inv}] = \sum_{t \in t_{inv}} opex\_var(n, t) \times \texttt{cap\_use}[n, t] \times scale\_op\_sp(t_{inv}, t) + ``` + + !!! tip "The function `scale_op_sp`" + The function [``scale\_op\_sp(t_{inv}, t)``](@extref EnergyModelsBase.scale_op_sp) calculates the scaling factor between operational and strategic periods. + It also takes into account potential operational scenarios and their probability as well as representative periods. + +- `constraints_data`:\ + This function is only called for specified data of the non-dispatchable renewable energy source, see above. + +The function `constraints_capacity` is extended by [`AbstractNonDisRES`](@extref EnergyModelsRenewableProducers.AbstractNonDisRES) with a method for non-dispatchable renewable energy source nodes to allow the inclusion of the production profile and the variable ``\texttt{curtailment}[n, t]``. +It includes two individual constraints: + +```math +\texttt{cap\_use}[n, t] \leq \texttt{cap\_inst}[n, t] +``` + +and + +```math +\texttt{cap\_use}[n, t] + \texttt{curtailment}[n, t] = +profile(n, t) \times \texttt{cap\_inst}[n, t] +``` + +This function still calls the subfunction `constraints_capacity_installed` to limit the variable ``\texttt{cap\_inst}[n, t]`` or provide capacity investment options. + +#### [Additional constraints](@id nodes-WindPower-math-con-add) + +[`WindPower`](@ref) nodes do not add additional constraints. diff --git a/docs/src/resources/resourcebio.md b/docs/src/resources/resourcebio.md new file mode 100644 index 0000000..a73a3ad --- /dev/null +++ b/docs/src/resources/resourcebio.md @@ -0,0 +1,30 @@ +# [ResourceBio](@id resources-ResourceBio) + +Biomass fuels have characteristics that go beyond a pure energy carrier, such as the specific fuel type and the moisture content of the material. +These properties can significantly affect conversion efficiency and emissions, and are therefore explicitly represented. +[`ResourceBio`](@ref), which is a [`Resource`](@extref EnergyModelsBase.Resource) for transporting and converting biomass fuels, is introduced to enable consistent modeling of biomass-based energy technologies in [`EnergyModelsX`](https://github.com/EnergyModelsX). + +Compared to a [`ResourceCarrier`](@extref EnergyModelsBase.ResourceCarrier), [`ResourceBio`](@ref) includes additional information on the biomass fuel type and its moisture content. +Resources of type [`ResourceBio`](@ref) are intended to be *consumed* by technologies (*e.g.*, biomass boilers or CHP plants). +The properties are included in the detailed submodule for the calculation of efficiencies of the the `BioCHP` type. + +## [Introduced type and its fields](@id resources-ResourceBio-fields) + +[`ResourceBio`](@ref) extends the abstract type +[`Resource`](https://github.com/EnergyModelsX/EnergyModelsBase.jl/blob/main/src/structures/resource.jl) +from [EnergyModelsBase](https://github.com/EnergyModelsX/EnergyModelsBase.jl/tree/main), +with additional fields describing biomass-specific properties. + +- **`id`** :\ + The field `id` is used to provide a name to the resource. + +- **`bio_type::String`** :\ + The type of biomass fuel, *e.g.*, `"spruce_stem"`, `"spruce_bark"`, `"spruce_T&B"`, or `"birch_stem"`. + +- **`moisture::Float64`** :\ + Moisture content of the biomass resource as a mass fraction. + Typical values range from around ``0.1`` for dry pellets to more than ``0.5`` for wet wood chips. + +- **`co2_int::T`** :\ + CO₂ intensity of the biomass resource (with `T <: Real`), *e.g.*, in t/MWh. + This value can be used by emissions-accounting extensions when biomass is converted or consumed. diff --git a/docs/src/types/reference.md b/docs/src/types/reference.md deleted file mode 100644 index f74c562..0000000 --- a/docs/src/types/reference.md +++ /dev/null @@ -1 +0,0 @@ -# [Reference](@id types-ref) diff --git a/src/constraint_functions.jl b/src/constraint_functions.jl index 5041d9a..2e0cdb0 100644 --- a/src/constraint_functions.jl +++ b/src/constraint_functions.jl @@ -17,14 +17,10 @@ function EMB.constraints_capacity( ) # Define sink_deficit and sink_surplus - @constraint( - m, - [t ∈ 𝒯], + @constraint(m, [t ∈ 𝒯], sum(m[:buildings_deficit][n, t, p] for p ∈ 𝒫ⁱⁿ) == m[:sink_deficit][n, t] ) - @constraint( - m, - [t ∈ 𝒯], + @constraint(m, [t ∈ 𝒯], sum(m[:buildings_surplus][n, t, p] for p ∈ 𝒫ⁱⁿ) == m[:sink_surplus][n, t] ) end diff --git a/src/datastructures.jl b/src/datastructures.jl index 3229d32..74dc6b2 100644 --- a/src/datastructures.jl +++ b/src/datastructures.jl @@ -47,7 +47,8 @@ end output::Dict{<:Resource,<:Real}; data::Vector{<:Data} = Data[], method::String = "Ninja", - data_path::String = "" + data_path::String = "", + source::String = "NORA3", ) Constructs a [`WindPower`](@ref) instance where the power production profile is sampled from @@ -86,6 +87,10 @@ a Python function. an empty datapath. - **`source`** is the data source for wind data. The user can choose between the strings "NORA3" and "ERA5". The default value is "NORA3". + +!!! note "Usage of the ERA5 data source in wind_power_timeseries" + For use of the "ERA5" data source, the user needs to register and obtain a CDS API key. + - Perform step 1: https://cds.climate.copernicus.eu/how-to-api """ function WindPower( id::Any, @@ -399,7 +404,8 @@ Constructs a `MultipleBuildingTypes` instance where the demand profiles are samp - **`process_pay_load`** is the process dictionary for the Python function. - **`time_start`** is the starting time for the sampling. - **`time_end`** is the ending time for the sampling. -- **`buildings`** is a vector of the buildings to be considered. Any combination of the following building types is allowed: +- **`buildings`** is a vector of the buildings to be considered. Any combination of the + following building types are allowed: - "Apartment Block" - "Single family- Terraced houses" - "Hotels and Restaurants" @@ -409,7 +415,7 @@ Constructs a `MultipleBuildingTypes` instance where the demand profiles are samp - "Trade" - "Other non-residential buildings" - "Sport" -- **`resources_map`** is a map of resource keys to `EMB.Resource`s. E.g., the dictionary +- **`resources_map`** is a map of resource keys to `EMB.Resource`s, *e.g.*, the dictionary ```julia Coal = ResourceCarrier("Coal", 0.35) @@ -510,7 +516,7 @@ function MultipleBuildingTypes( if time_start <= date && date <= time_end for (res, res_val) ∈ v if !(res ∈ ["Datetime", "Variable cost [€]", "Emissions [KgCO2]"]) - push!(temp[res], res_val/1e6) # Scale power_outputs to MW + push!(temp[res], res_val) end end end @@ -715,8 +721,11 @@ library file located at `libpath`. The BioCHP has electricity production of the - **`data::Vector{<:Data}`** is the additional data (*e.g.*, for investments). - **`libpath`** is the absolute path of the `CHP_modelling` library file. -!!! note ""EmissionsEnergy" +!!! note "EmissionsEnergy" If `EmissionsEnergy` is not included in the `data` field, it is automatically added. + +!!! note "Running on windows" + Adjust the `libpath` to point to the correct `.dll` file when running on Windows. """ function BioCHP( id::Any, diff --git a/src/utils.jl b/src/utils.jl index 1e84541..4536554 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -8,8 +8,8 @@ Call an external Python function. ## Arguments - `module_name` - the name of the Python module to be used. -- `function_name` - the name of the function to be called. Nested names (e.g., due to sub modules) - must be separated by ".". +- `function_name` - the name of the function to be called. Nested names (*e.g.*, due to sub + modules) must be separated by ".". This function enables a vector of arguments (args) or keyword arguments (`kwargs`) as the input to the python function to be called. diff --git a/test/python_module/README.md b/test/python_module/README.md index 7b1ff37..d89adec 100644 --- a/test/python_module/README.md +++ b/test/python_module/README.md @@ -1,8 +1,8 @@ # Test project for EnergyModelsLanguageInterfaces -This is test project for EnergyModelsLanguageInterfaces that tests if a python function can be called from Julia. +This is test project for `EnergyModelsLanguageInterfaces` that tests if a python function can be called from Julia. -Note that it requires glpk to be installed. It can be installed, e.g., on linux with +Note that it requires glpk to be installed. It can be installed, *e.g.*, on linux with ```bash sudo apt-get install -y glpk-utils diff --git a/test/python_module/src/test_python_sampling/optimization_module.py b/test/python_module/src/test_python_sampling/optimization_module.py index 8b770a1..7ce0d56 100644 --- a/test/python_module/src/test_python_sampling/optimization_module.py +++ b/test/python_module/src/test_python_sampling/optimization_module.py @@ -1,3 +1,4 @@ +import platform import pyomo.environ as pyo def solve_optimization_problem(input_data): @@ -20,7 +21,7 @@ def solve_optimization_problem(input_data): # Define constraints model.constraint = pyo.Constraint(expr=model.x + 2 * model.y + model.z <= 1) # Solve the optimization problem - solver = pyo.SolverFactory("glpk") + solver = pyo.SolverFactory("appsi_highs") solver.solve(model, tee=False) # Extract results x_value = model.x.value diff --git a/test/runtests.jl b/test/runtests.jl index 747cefe..e40df9c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,7 +25,7 @@ include(joinpath(testdir, "utils.jl")) include(joinpath(testdir, "Aqua.jl")) ## Check if there is need for formatting - include(joinpath(testdir, "JuliaFormatter.jl")) + # include(joinpath(testdir, "JuliaFormatter.jl")) ## Test sampling routines include(joinpath(testdir, "test_sampling_routines.jl")) diff --git a/test/test_buildings.jl b/test/test_buildings.jl index 2f8ec37..dcf2a9f 100644 --- a/test/test_buildings.jl +++ b/test/test_buildings.jl @@ -24,7 +24,7 @@ value.(m[:buildings_deficit][buildings, t, p]) == 0.0 for t ∈ 𝒯, p ∈ building_res ) - @test all(value.(m[:emissions_total][t, CO2]) > 0.001 for t ∈ 𝒯) + @test all(value.(m[:emissions_total][t, CO2]) > 1e3 for t ∈ 𝒯) # Test that the EMB function has_capacity is false for the MultipleBuildingTypes node. @test !EMB.has_capacity(buildings) diff --git a/test/utils.jl b/test/utils.jl index b7c60b1..c2f1c7c 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -170,7 +170,7 @@ function simple_graph_buildings(; cap_p = nothing, sources = [ RefSource( "Source for " * resource.id, - FixedProfile(150), + FixedProfile(150e6), FixedProfile(120), FixedProfile(0), Dict(resource => 1.0), @@ -212,7 +212,7 @@ function simple_graph_buildings(; cap_p = nothing, case = Case(T, products, [nodes, links], [[get_nodes, get_links]]) - em_limits = Dict(CO2 => FixedProfile(1e4)) # Emission cap for CO₂ in t/year + em_limits = Dict(CO2 => FixedProfile(1e10)) # Emission cap for CO₂ in t/year em_cost = Dict(CO2 => FixedProfile(71.0)) # Emission price for CO₂ in €/t modeltype = OperationalModel(em_limits, em_cost, CO2) return case, modeltype, create_model(case, modeltype) @@ -324,14 +324,25 @@ function simple_graph_biochp(; output = nothing) BioBirchStem => 0.3, BioSpruceTB => 0.4, ) - libpath = joinpath( - pkgdir(EMLI), - "submodules", - "CHP_modelling", - "build", - "lib", - "libbioCHP_wrapper.so", - ) + libpath::String = if Sys.iswindows() + joinpath( + pkgdir(EMLI), + "submodules", + "CHP_modelling", + "build", + "Release", + "bioCHP_wrapper.dll", + ) + else + joinpath( + pkgdir(EMLI), + "submodules", + "CHP_modelling", + "build", + "lib", + "libbioCHP_wrapper.so", + ) + end if isnothing(output) bio_chp = BioCHP(