Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Release notes

## Version 0.1.0 (2026-04-25)
## Version 0.1.0 (2026-05-28)

### Added WindFarmParameters

* Introduces `WindFarmParameters` more in line with `PVParameters` and also made the corresponding nodes more consistent.

### Add Building node

Expand All @@ -16,7 +20,7 @@
### Initial version of the package

* Provide sampling routines for C++ and Python for incorporation into `EnergyModelsX` models.
* Utilize the sampling routines for sampling from:.
* Utilize the sampling routines for sampling from:
* C++: `BioCHP` node.
* Python: `MultipleBuildingTypes`, `CSPandPV`, and `WindPower` nodes.
* Incorporation of a `BioResource` for `BiOCHP` plant.
Expand Down
8 changes: 5 additions & 3 deletions docs/src/library/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ EMLI.ResourceBio

```@docs
EMLI.PVParameters
EMLI.WindFarmParameters
```

## [New nodal types](@id lib-pub-nodal_types)
Expand All @@ -29,12 +30,12 @@ EMLI.BioCHP
EMLI.WindPower(
::Any,
::TimeStruct.TimeProfile,
::Dict,
::String,
::String,
::TimeStruct.TimeProfile,
::TimeStruct.TimeProfile,
::Dict{<:EnergyModelsBase.Resource,<:Real},
::DateTime,
::DateTime,
::WindFarmParameters,
)
EMLI.PV(
::Any,
Expand Down Expand Up @@ -108,4 +109,5 @@ EMLI.BioCHP(
```@docs
EMLI.call_python_function
EMLI.fetch_element
EMLI.to_dict
```
4 changes: 2 additions & 2 deletions src/EnergyModelsLanguageInterfaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ include("constraint_functions.jl")
include("utils.jl")

export call_python_function, fetch_element
export WindPower, CSPandPV, MultipleBuildingTypes
export PVParameters, WindFarmParameters
export PV, WindPower, CSPandPV, MultipleBuildingTypes
export ResourceBio, BioCHP
export PV, PVParameters
export Building

end # module EnergyModelsLanguageInterfaces
234 changes: 172 additions & 62 deletions src/datastructures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ abstract type AbstractParameters end
A structure to hold parameters for photovoltaic (PV) power generation.

# Fields
- **`lat::Real`**: Latitude of the location in decimal degrees (e.g., 52.0 for 52°N).
- **`lon::Real`**: Longitude of the location in decimal degrees (e.g., 13.0 for 13°E).
- **`loss::Real=14.0`**: Total system losses in percentage (e.g., 14.0 for 14% losses).
- **`pvtechchoice::String="crystSi"`**: Type of PV technology. Options include:
- **`lat::Real`** is the latitude of the location in decimal degrees (e.g., 52.0 for 52°N).
- **`lon::Real`** is the longitude of the location in decimal degrees (e.g., 13.0 for 13°E).
Comment thread
JulStraus marked this conversation as resolved.
Outdated
- **`loss::Real=14.0`** is the total system losses in percentage (e.g., 14.0 for 14% losses).
- **`pvtechchoice::String="crystSi"`** is the type of PV technology. Options include:
- `"crystSi"`: Crystalline silicon (default).
- `"CIS"`: Copper indium selenide.
- `"CdTe"`: Cadmium telluride.
- **`mountingplace::String="free"`**: Mounting type of the PV system. Options include:
- **`mountingplace::String="free"`** is the mounting type of the PV system. Options include:
- `"free"`: Free-standing system (default).
- `"building"`: Building-integrated system.
- **`optimalangles::Bool=true`**: Whether to use optimal tilt and azimuth angles for the PV system.
- **`usehorizon::Bool=true`**: Whether to include the effect of the horizon in the calculations.
- **`optimalangles::Bool=true`** is a flag for whether to use optimal tilt and azimuth angles for the PV system.
- **`usehorizon::Bool=true`** is a flag for whether to include the effect of the horizon in the calculations.
"""
struct PVParameters <: AbstractParameters
lat::Real
Expand All @@ -41,6 +41,16 @@ struct PVParameters <: AbstractParameters
usehorizon::Bool,
)
errors = String[]
if !isfinite(lat)
push!(errors, "lat must be finite.")
elseif lat < -90 || lat > 90
push!(errors, "lat must be in [-90, 90].")
end
if !isfinite(lon)
push!(errors, "lon must be finite.")
elseif lon < -180 || lon > 180
push!(errors, "lon must be in [-180, 180].")
end
Comment thread
JulStraus marked this conversation as resolved.
Outdated
if loss < 0
push!(errors, "Loss must be non-negative.")
end
Expand Down Expand Up @@ -79,6 +89,128 @@ function PVParameters(
optimalangles, usehorizon)
end

"""
WindFarmParameters
WindFarmParameters(
id::String,
lat::Real,
lon::Real,
turbine_height::Real;
orientation = missing,
shape = missing,
method::String = "Ninja",
source::String = "NORA3",
)

A structure to hold wind farm parameters and metadata for wind power time series generation.

# Fields
- **`id`**: Identifier for the wind farm.
- **`lat`**: Latitude of the wind farm.
- **`lon`**: Longitude of the wind farm.
- **`turbine_height`**: Height of the wind turbines in meters.
- **`orientation`**: Orientation of the wind farm (default: `missing`).
- **`shape`**: Shape of the wind farm (default: `missing`).
- **`method`** is the chosen method for data retrieval. The user can choose between the
strings "Ninja", "Tradewind_offshore", "Tradewind_upland", and "Tradewind_lowland".
The default value is "Ninja".
- **`source`** is the data source for wind data. The user can choose between the strings
"NORA3" and "ERA5". The default value is "NORA3".
"""
Comment thread
JulStraus marked this conversation as resolved.
struct WindFarmParameters <: AbstractParameters
id::String
lat::Real
lon::Real
turbine_height::Real
orientation::Any
shape::Any
method::String
source::String
function WindFarmParameters(
id::String,
lat::Real,
lon::Real,
turbine_height::Real,
orientation::Any,
shape::Any,
method::String,
source::String,
)
errors = String[]
if !isfinite(lat) || lat < -90 || lat > 90
push!(errors, "lat must be finite and in [-90, 90].")
end
if !isfinite(lon) || lon < -180 || lon > 180
push!(errors, "lon must be finite and in [-180, 180].")
end
if !isfinite(turbine_height) || turbine_height <= 0
push!(errors, "turbine_height must be finite and positive.")
end
Comment thread
JulStraus marked this conversation as resolved.
Outdated

methods = ("Ninja", "Tradewind_offshore", "Tradewind_upland", "Tradewind_lowland")
if !(method in methods)
push!(errors, "method must be one of $(methods).")
end

sources = ("NORA3", "ERA5")
if !(source in sources)
push!(errors, "source must be one of $(sources).")
end

if !isempty(errors)
throw(ArgumentError(join(errors, " ")))
end

return new(
id,
lat,
lon,
turbine_height,
orientation,
shape,
method,
source,
)
end
end
function WindFarmParameters(
id::String,
lat::Real,
lon::Real,
turbine_height::Real;
orientation = missing,
shape = missing,
method::String = "Ninja",
source::String = "NORA3",
)
return WindFarmParameters(
id,
lat,
lon,
turbine_height,
orientation,
shape,
method,
source,
)
end

"""
to_dict(params::WindFarmParameters)

Convert a `WindFarmParameters` instance to a dictionary for use in Python calls.
"""
function to_dict(params::WindFarmParameters)
return Dict(
"id" => params.id,
"lat" => params.lat,
"lon" => params.lon,
"turbine_height" => params.turbine_height,
"orientation" => params.orientation,
"shape" => params.shape,
)
end

"""
WindPower <: AbstractNonDisRES

Expand Down Expand Up @@ -120,54 +252,33 @@ end
WindPower(
id::Any,
cap::TimeProfile,
windfarm::Dict,
time_start::String,
time_end::String,
opex_var::TimeProfile,
opex_fixed::TimeProfile,
output::Dict{<:Resource,<:Real};
output::Dict{<:Resource,<:Real},
time_start::DateTime,
time_end::DateTime,
wind_params::WindFarmParameters;
data::Vector{<:ExtensionData} = ExtensionData[],
method::String = "Ninja",
data_path::String = "",
source::String = "NORA3",
)

Constructs a [`WindPower`](@ref) instance where the power production profile is sampled from
a Python function.

# Arguments
- **`id`** is the name or identifier of the node.
- **`cap`** is the installed capacity.
- **`windfarm`** is a dictionary containing the wind farm parameters. An example dictionary
is given by:

```julia
windfarm = Dict(
"id" => "windfarm_1", # The identifier of the windfarm
"lat" => 56.8233, # The latitude coordinates of the windfarm
"lon" => 4.3467, # The longitude of the wind farm
"orientation" => missing, # The orientation
"shape" => missing,
"turbine_height" => 150, # The turbine height
)
```
- **`time_start`** is the starting time (as a string) for the wind power time series sampling.
The format is "YYYY-MM-DD".
- **`time_end`** is the end time (as a string) for the wind power time series sampling.
The format is "YYYY-MM-DD".
- **`opex_var`** is the variable operating expense per energy unit produced.
- **`opex_fixed`** is the fixed operating expense.
- **`output`** are the generated `Resource`s, normally Power, with conversion value `Real`.
- **`cap::TimeProfile`** is the installed capacity.
- **`opex_var::TimeProfile`** is the variable operating expense per energy unit produced.
- **`opex_fixed::TimeProfile`** is the fixed operating expense.
- **`output::Dict{<:Resource,<:Real}`** are the generated `Resource`s, normally Power, with conversion value `Real`.
- **`time_start::DateTime`** is the starting time for the wind power time series sampling.
- **`time_end::DateTime`** is the end time for the wind power time series sampling.
- **`wind_params::WindFarmParameters`** are the parameters for the wind farm. See [`WindFarmParameters`](@ref) for details.

# Keyword arguments
- **`data`** is the additional data (*e.g.*, for investments). The default value is no `data`.
- **`method`** is the chosen method for data retrieval. The user can choose between the
strings "Ninja", "Tradewind_offshore", "Tradewind_upland", and "Tradewind_lowland".
The default value is "Ninja".
- **`data_path`** is an optional file path for already downloaded data. The default value is
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.
Expand All @@ -176,26 +287,24 @@ a Python function.
function WindPower(
id::Any,
cap::TimeProfile,
windfarm::Dict,
time_start::String,
time_end::String,
opex_var::TimeProfile,
opex_fixed::TimeProfile,
output::Dict{<:Resource,<:Real};
output::Dict{<:Resource,<:Real},
time_start::DateTime,
time_end::DateTime,
wind_params::WindFarmParameters;
data::Vector{<:ExtensionData} = ExtensionData[],
method::String = "Ninja",
data_path::String = "",
source::String = "NORA3",
)
power = call_python_function(
"wind_power_timeseries",
"sample.wind_power";
windfarm = windfarm,
time_start = time_start,
time_end = time_end,
method = method,
windfarm = to_dict(wind_params),
time_start = Dates.format(time_start, "yyyy-mm-dd"),
time_end = Dates.format(time_end, "yyyy-mm-dd"),
method = wind_params.method,
data_path = data_path,
source = source,
source = wind_params.source,
)
profile = OperationalProfile(power)

Expand Down Expand Up @@ -256,22 +365,23 @@ end
)

Constructs a [`PV`](@ref) instance where the power production profile is sampled from
the PVGIS API.
the PVGIS API tool from the EU Science Hub (available at https://re.jrc.ec.europa.eu/pvg_tools).

# Arguments
- **`id`**: The name or identifier of the node.
- **`cap`**: The installed capacity.
- **`opex_var`**: The variable operating expense per energy unit produced.
- **`opex_fixed`**: The fixed operating expense.
- **`output`**: The generated `Resource`s, normally Power, with conversion value `Real`.
- **`time_start::DateTime`**: The start of the time range for which the PV output data is requested.
- **`time_end::DateTime`**: The end of the time range for which the PV output data is requested.
- **`params::PVParameters`**: Parameters for the PV system. See [`PVParameters`](@ref) for details.
- **`id`** is the name/identifier of the node.
- **`cap::TimeProfile`** is the installed capacity.
- **`opex_var::TimeProfile`** is the variable operating expense per energy unit produced.
- **`opex_fixed::TimeProfile`** is the fixed operating expense.
- **`output::Dict{<:Resource,<:Real}`** are the generated `Resource`s, normally Power.
- **`time_start::DateTime`** is the start of the time range for which the PV output data is requested.
- **`time_end::DateTime`** is the end of the time range for which the PV output data is requested.
- **`params::PVParameters`** are the parameters for the PV system. See [`PVParameters`](@ref) for details.

# Keyword arguments
- **`data`**: Additional data (e.g., for investments). Default is no `data`.
- **`data_path`**: Directory where the cached CSV file will be stored. Default is `"pvgis_cache"`.
- **`filename_hint`**: Optional string to include in the cache file name for identification. Default is `""`.
- **`data::Vector{<:ExtensionData}`** is the additional data (e.g., for investments).
The field `data` is conditional through usage of a constructor.
- **`data_path::String`** is the directory where the cached CSV file will be stored. Default is `"pvgis_cache"`.
- **`filename_hint::String`** is an optional string to include in the cache file name for identification. Default is `""`.
"""
function PV(
id::Any,
Expand Down
7 changes: 6 additions & 1 deletion test/test_PV.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
@testset "PVParameters" begin
# Test the constructor and field access
@testset "Constructor" begin
params1 = PVParameters(
52.0,
Expand All @@ -23,6 +22,12 @@
end

@testset "Invalid parameters" begin
# Test that invalid lat/lon throws an error
@test_throws ArgumentError PVParameters(-91.0, 5.0)
@test_throws ArgumentError PVParameters(91.0, 5.0)
@test_throws ArgumentError PVParameters(52.0, -181.0)
@test_throws ArgumentError PVParameters(52.0, 181.0)

# Test that invalid loss throws an error
@test_throws ArgumentError PVParameters(52.0, 5.0; loss = -1.0)

Expand Down
Loading
Loading