# Defining constraint for renewable generation with availability factors

**URL:** <https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147>\
**Category:** Optimization (Mathematical)\
**Tags:** optimization\
**Created:** [March 20, 2022, 12:30am UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147 "2022-03-20T00:30:55Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![mbah](https://avatars.discourse-cdn.com/v4/letter/m/bcef8e/32.png) [@mbah](https://discourse.julialang.org/u/mbah)\
**Post date:** [March 20, 2022, 12:30am UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/1 "2022-03-20T00:30:55Z")

</div>

Hello,

I am trying to code up a straightforward constraint for renewable generation (wind, solar etc…) without much success. As an example, the general formulation for a wind generation constraint is as follows:

 ![example_formulation](https://global.discourse-cdn.com/julialang/original/3X/c/d/cde7c0da7a13fe1586b077d03dd47d4ee85e4712.png)

My availability factor data file is structured by node (i.e. zone) over time

![availability_factor](https://global.discourse-cdn.com/julialang/original/3X/3/f/3f11f5e06d69765dba5f400b71b071f2936375c0.png)

The wind installed capacity paramter is accessed by calling the g\_max coloumn in this file

![wind plant](https://global.discourse-cdn.com/julialang/original/3X/b/d/bd8071a58a35472774c0a0d3526aa62c84692d92.png)

A snippet of the code i’ve tried so far that is generating a key error

```julia
@constraint(dispatch_model, 
  WNDGeneration[t in T, n in N, w in WND], 
  WIND[t,n,w] <= data[:wind_cf][n][t] * data[:plants_aggregation][:g_max][w][n]  
)

```

```julia
ERROR: LoadError: KeyError: key (1, "ZONE_A", "ZONE_A_WIND") not found
Stacktrace:

```

Any ideas/solutions to this problem would be greatly appreciated.

Thanks

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 20, 2022, 7:17pm UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/2 "2022-03-20T19:17:19Z")

</div>

Please provide a reproducible example.

Do you have the full stacktrace of the error? My guess is that your `WIND` variable isn’t defined over the same indices as the `WNDGeneration` constraint.

---

<div class="post-metadata">

**Author:** ![mbah](https://avatars.discourse-cdn.com/v4/letter/m/bcef8e/32.png) [@mbah](https://discourse.julialang.org/u/mbah)\
**Post date:** [March 20, 2022, 11:21pm UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/3 "2022-03-20T23:21:21Z")

</div>

Thanks @odow. I will work on a reproducible example. In the mean time, this is the full stacktrace error message:

```julia
ERROR: KeyError: key (1, "ZONE_A", "ZONE_A_WIND") not found
Stacktrace:
  [1] to_index(A::JuMP.Containers.DenseAxisArray{VariableRef, 2, Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}}, Tuple{JuMP.Containers._AxisLookup{Dict{Int64, Int64}}, JuMP.Containers._AxisLookup{Dict{String, Int64}}}}, idx::Tuple{Int64, String, String})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\DenseAxisArray.jl:257
  [2] getindex(::JuMP.Containers.DenseAxisArray{VariableRef, 2, Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}}, Tuple{JuMP.Containers._AxisLookup{Dict{Int64, Int64}}, JuMP.Containers._AxisLookup{Dict{String, Int64}}}}, ::Int64, ::String, ::String)
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\DenseAxisArray.jl:266
  [3] macro expansion
    @ C:\Users\mbahe\.julia\packages\MutableArithmetics\8xkW3\src\rewrite.jl:279 [inlined]
  [4] macro expansion
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\macros.jl:676 [inlined]
  [5] (::var"#44#45")(t::Int64, n::String, w::String)
    @ Main C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\macro.jl:194
  [6] #38
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:105 [inlined]
  [7] iterate
    @ .\generator.jl:47 [inlined]
  [8] collect(itr::Base.Generator{JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}}, JuMP.Containers.var"#38#39"{var"#44#45"}})
    @ Base .\array.jl:678
  [9] map(f::Function, A::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}})
    @ Base .\abstractarray.jl:2323
 [10] container(f::Function, indices::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}}, #unused#::Type{JuMP.Containers.DenseAxisArray})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:105
 [11] container(f::Function, indices::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:66
 [12] macro expansion
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\macros.jl:142 [inlined]
 [13] top-level scope

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 21, 2022, 12:08am UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/4 "2022-03-21T00:08:40Z")

</div>

How did you define `WIND`? It looks like it has two dimensions, but you’re indexing over three.

---

<div class="post-metadata">

**Author:** ![jd-foster](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jd-foster/32/35824_2.png) [@jd-foster](https://discourse.julialang.org/u/jd-foster)\
**Post date:** [March 21, 2022, 12:42am UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/5 "2022-03-21T00:42:39Z")

</div>

> [@mbah](#):
>
> ```julia
> @constraint(dispatch_model, 
> WNDGeneration[t in T, n in N, w in WND], 
> WIND[t,n,w] <=
> 
> ```

As @odow said, the definition of the variable `WIND` is the issue, not your constraint declaration (shown above).  
MWE:

```julia
using JuMP
model = JuMP.Model()
T = [1, 2, 3]
N = ["ZONE_A", "ZONE_B", "ZONE_C"]
WND = ["ZONE_A_WIND", "ZONE_B_WIND"]
@variable(model, WIND[N, WND]) ## THIS needs to be:
# @variable(model, WIND[T, N, WND])
@constraint(model, WNDGeneration[t in T, n in N, w in WND], WIND[t,n,w] <= 1.0)

```

---

<div class="post-metadata">

**Author:** ![mbah](https://avatars.discourse-cdn.com/v4/letter/m/bcef8e/32.png) [@mbah](https://discourse.julialang.org/u/mbah)\
**Post date:** [March 22, 2022, 3:52pm UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/6 "2022-03-22T15:52:35Z")

</div>

Thank you @odow and @jd-foster for the previous answers. I am trying to code up a relatively simple dispatch model. Since this is a network model, ideally the results i expect is dispatch of conventionals and output of renewables (wind, solar, etc) across the zones and over time. As you pointed out, my issue seems to be with indexing, but i’m not sure where exactly.

```julia
using JuMP, Clp, DataFrames, CSV 
using Base: Symbol

#Define sets
P= plants.index
N = nodes.index
N = nodes.index
T = demand_2018.index
L= lines.index

#Specifying all plant types

C = filter(r -> any(occursin.(["conventional"], r.plant_type)), readDataDFs[:plants]).index
ROR = filter(r -> any(occursin.(["ROR"], r.plant_type)), readDataDFs[:plants]).index #Run-of-river
SOL = filter(r -> any(occursin.(["solar"], r.plant_type)), readDataDFs[:plants]).index #solar
WND = filter(r -> any(occursin.(["wind"], r.plant_type)), readDataDFs[:plants]).index #wind

dispatch_model = Model(Clp.Optimizer)

#Output variables

@variables(dispatch_model, begin
  GEN[T, C] >=0 #Conventional plant generation
  FLOW[T, L] #Line power flow 
  RUNOFRIVER[T,ROR] >=0 #Run-of-River
  SOLAR[T,SOL] >=0
  WIND[T,WND] >=0
  VoltageAngle[T, N] #Voltage angle
end) 

#Objective function
@objective(dispatch_model, 
  Min, 
  sum(sum(GEN[t, c]*data[:plants][:cost][c] for c in C) for t in T))

#conventional plant capacity constraint
@constraint(dispatch_model, 
CapConstraint[t in T, c in C], 
GEN[t,c] <= data[:plants][:g_max][c] 
) 

#I have issues with renewable generation constraints

#Run-of-river generation constraint
@constraint(dispatch_model, 
  RORGeneration_new[t in T, n in N, r in ROR], 
  RUNOFRIVER[t,n,r] <= data[:ror_cf][t] * data[:plants][:g_max][r][n]   
)

#Wind generation constraint
@constraint(dispatch_model, 
  WNDGeneration[t in T, n in N, w in WND], 
  WIND[t,n,w] <= data[:wind_cf][n][t] * data[:plants][:g_max][w][n]  
)

#Solar generation constraint
@constraint(dispatch_model, 
  SOLARGeneration[t in T, n in N, s in SOL], 
  SOLAR[t,n,s] <= data[:solar_cf][n][t] * data[:plants][:g_max][s][n]  
)

...

```

Initial error messsage for Wind

```julia
ERROR: KeyError: key (1, "ZONE_A", "ZONE_A_WIND") not found
Stacktrace:
  [1] to_index(A::JuMP.Containers.DenseAxisArray{VariableRef, 2, Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}}, Tuple{JuMP.Containers._AxisLookup{Dict{Int64, Int64}}, JuMP.Containers._AxisLookup{Dict{String, Int64}}}}, idx::Tuple{Int64, String, String})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\DenseAxisArray.jl:257
  [2] getindex(::JuMP.Containers.DenseAxisArray{VariableRef, 2, Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}}, Tuple{JuMP.Containers._AxisLookup{Dict{Int64, Int64}}, JuMP.Containers._AxisLookup{Dict{String, Int64}}}}, ::Int64, ::String, ::String)
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\DenseAxisArray.jl:266
  [3] macro expansion
    @ C:\Users\mbahe\.julia\packages\MutableArithmetics\8xkW3\src\rewrite.jl:279 [inlined]
  [4] macro expansion
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\macros.jl:676 [inlined]
  [5] (::var"#44#45")(t::Int64, n::String, w::String)
    @ Main C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\macro.jl:194
  [6] #38
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:105 [inlined]
  [7] iterate
    @ .\generator.jl:47 [inlined]
  [8] collect(itr::Base.Generator{JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}}, JuMP.Containers.var"#38#39"{var"#44#45"}})
    @ Base .\array.jl:678
  [9] map(f::Function, A::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}})
    @ Base .\abstractarray.jl:2323
 [10] container(f::Function, indices::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}}, #unused#::Type{JuMP.Containers.DenseAxisArray})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:105
 [11] container(f::Function, indices::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:66
 [12] macro expansion
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\macros.jl:142 [inlined]
 [13] top-level scope

```

I am aware that the variables are not defined the same as the constraints. I attempted to make them similar,

```julia
@variables(dispatch_model, begin
  GEN[T, C] >=0 #Conventional plant generation
  FLOW[T, L] #Line power flow 
  RUNOFRIVER[T,N,ROR] >=0 #Run-of-River
  SOLAR[T,N,SOL] >=0
  WIND[T,N,WND] >=0
  VoltageAngle[T, N] #Voltage angle
end) 

#Wind generation constraint
@constraint(dispatch_model, 
  WNDGeneration[t in T, n in N, w in WND], 
  WIND[t,n,w] <= data[:wind_cf][n][t] * data[:plants][:g_max][w][n]  
)

```

but I get the following error message (example of wind).

```julia
ERROR: KeyError: key "ZONE_A" not found
Stacktrace:
  [1] getindex(h::Dict{Symbol, Dict{Int64, V} where V}, key::String)
    @ Base .\dict.jl:482
  [2] macro expansion
    @ C:\Users\mbahe\.julia\packages\MutableArithmetics\8xkW3\src\rewrite.jl:279 [inlined]
  [3] macro expansion
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\macros.jl:676 [inlined]
  [4] (::var"#42#43")(t::Int64, n::String, w::String)
    @ Main C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\macro.jl:194
  [5] #38
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:105 [inlined]
  [6] iterate
    @ .\generator.jl:47 [inlined]
  [7] collect(itr::Base.Generator{JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}}, JuMP.Containers.var"#38#39"{var"#42#43"}})
    @ Base .\array.jl:678
  [8] map(f::Function, A::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}})
    @ Base .\abstractarray.jl:2323
  [9] container(f::Function, indices::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}}, #unused#::Type{JuMP.Containers.DenseAxisArray})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:105
 [10] container(f::Function, indices::JuMP.Containers.VectorizedProductIterator{Tuple{Vector{Int64}, WeakRefStrings.StringVector{String}, WeakRefStrings.StringVector{String}}})
    @ JuMP.Containers C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\Containers\container.jl:66
 [11] macro expansion
    @ C:\Users\mbahe\.julia\packages\JuMP\klrjG\src\macros.jl:142 [inlined]

```

I would appreciate any help on this issue.

Thanks

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [March 22, 2022, 7:51pm UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/7 "2022-03-22T19:51:13Z")

</div>

The new error is saying that your data is wrong:

Try calling this for different `t`, `n`, and `w`:

`data[:wind_cf][n][t] * data[:plants][:g_max][w][n]`

If I had to guess the problem, it’s that your `data[:wind_df]` expects a `Symbol` as the key, but you passed a `String`. You might want `data[:wind_cf][Symbol(n)][t]`, or you could change how you create the data.

---

<div class="post-metadata">

**Author:** ![mbah](https://avatars.discourse-cdn.com/v4/letter/m/bcef8e/32.png) [@mbah](https://discourse.julialang.org/u/mbah)\
**Post date:** [March 22, 2022, 8:36pm UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/8 "2022-03-22T20:36:16Z")

</div>

Thanks @odow for this insight. I suspect the problem may be when I try to convert my input dataframes into dictionaries as follows;

```julia
for inputDataItem in inputDataItems 
  readDataDFs[Symbol(inputDataItem)] = DataFrame(CSV.File(data_dir * inputDataItem * ".csv"), copycols=true) 
  data[Symbol(inputDataItem)] = df_to_dict_with_id(readDataDFs[Symbol(inputDataItem)]) 
  set[Symbol(inputDataItem)] = readDataDFs[Symbol(inputDataItem)].index
end

#Here index is set to the first coloumn of all csv files

```

Any way around this?

---

<div class="post-metadata">

**Author:** ![jd-foster](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jd-foster/32/35824_2.png) [@jd-foster](https://discourse.julialang.org/u/jd-foster)\
**Post date:** [March 22, 2022, 11:33pm UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/9 "2022-03-22T23:33:57Z")

</div>

> [@mbah](#):
>
> `df_to_dict_with_id`

We would have to know what this function looks like.

---

<div class="post-metadata">

**Author:** ![mbah](https://avatars.discourse-cdn.com/v4/letter/m/bcef8e/32.png) [@mbah](https://discourse.julialang.org/u/mbah)\
**Post date:** [March 23, 2022, 9:31am UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/10 "2022-03-23T09:31:20Z")

</div>

Sure @jd-foster , I have added the complete data reading code below

```julia
function df_to_dict_with_id(df::DataFrame)

    Dict(col[1] => Dict(zip(df[!, 1], col[2])) for col in pairs(eachcol(df)))

  end

data = Dict{Symbol,Any}()  
set = Dict{Symbol,Any}()
readDataDFs = Dict()
inputDataItems = ["plants", "demand_2018", "nodes","lines","solar_cf","wind_cf","ror_cf"] # read csv file names for input data 

for inputDataItem in inputDataItems #loop starts~ for each data file inputDataItems
  readDataDFs[Symbol(inputDataItem)] = DataFrame(CSV.File(data_dir * inputDataItem * ".csv"), copycols=true) #read in all the CSV files into the dictionary readDataDFs
  data[Symbol(inputDataItem)] = df_to_dict_with_id(readDataDFs[Symbol(inputDataItem)]) #use the dataframe to dictionary function to convert all dataframes into dictionaries and insert the dic files to data. Data is now main working file
  set[Symbol(inputDataItem)] = readDataDFs[Symbol(inputDataItem)].index #set is set to the first coloumn (index) of all the csv file 
end

```

If i can add a bit of explanation to my previous response to odow. The files ‘plants,demand\_2018,nodes and lines’ all have a coloumn called index which is mostly a combination of strings and numbers identifying each element. A few example below

Lines  
 ![lines](https://global.discourse-cdn.com/julialang/original/3X/f/b/fbf1d68e14c0d69a8bb716681abb770d9bd13b8f.png)

Plants  
 ![plants](https://global.discourse-cdn.com/julialang/original/3X/3/c/3c3a1a55bfb2e91e5d49e5e97d025309c7e447e3.png)

Nodes  
 ![nodes](https://global.discourse-cdn.com/julialang/original/3X/3/1/3111fd4f8b471472707911cb87c99e671bc95abc.png)

However, when it comes to the capacity factor csv files, the zones are ordered in seperate columns and the index is the time

Example: wind  
 ![wind_cf](https://global.discourse-cdn.com/julialang/original/3X/5/0/50925f391fc2108c82ecd84fdb5025fa5243d2e4.png)

Could this be the cause of the problem?

Thanks a lot!

---

<div class="post-metadata">

**Author:** ![jd-foster](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jd-foster/32/35824_2.png) [@jd-foster](https://discourse.julialang.org/u/jd-foster)\
**Post date:** [March 31, 2022, 4:16am UTC](https://discourse.julialang.org/t/defining-constraint-for-renewable-generation-with-availability-factors/78147/11 "2022-03-31T04:16:11Z")

</div>

Did you figure this one out? I’m pretty sure this is the solution to the last error you gave:

> [@odow](#):
>
> If I had to guess the problem, it’s that your `data[:wind_df]` expects a `Symbol` as the key, but you passed a `String` . You might want `data[:wind_cf][Symbol(n)][t]` , or you could change how you create the data.

The reason that it is expecting a `Symbol` as key in the second set of brackets, e.g

```julia
data[:wind_cf][:MHKVL_E][2]

```

is that you are using the `pairs` function in `df_to_dict_with_id` on DataFrame columns for your `wind_cf` table, hence the column headers are interpreted as `Symbol`s for your dictionary keys. You can do what @odow recommended, but if you really want to have `String`s for your keys, then change `df_to_dict_with_id` to

```julia
function df_to_dict_with_id(df::DataFrame)

    Dict( String(col[1]) => Dict(zip(df[!, 1], col[2])) for col in pairs(eachcol(df)))

end

```

BTW, rather than

> [@mbah](#):
>
> `DataFrame(CSV.File(data_dir * inputDataItem * ".csv"), copycols=true)`

you might find that this is easier to read:

```julia
CSV.read(joinpath(data_dir,inputDataItem * ".csv"), DataFrame)

```

noting `copycols=true` is the default here anyway.
