Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simultaneous charge and discharge #214

Closed
Ethan-Russell opened this issue Sep 11, 2023 · 5 comments · Fixed by #220
Closed

Simultaneous charge and discharge #214

Ethan-Russell opened this issue Sep 11, 2023 · 5 comments · Fixed by #220
Labels
bug Something isn't working

Comments

@Ethan-Russell
Copy link
Member

I am still seeing simultaneous charging and discharging with the full size grid. Need to get creative with a way to limit this with LP hopefully without changing the objective.

@sallyrobson
Copy link
Member

I am still seeing simultaneous charging and discharging with the full size grid. Need to get creative with a way to limit this with LP hopefully without changing the objective.

I'm thinking this might be a good question for Ray if we want to reach out, I don't think we've used up our time for him.

@Ethan-Russell Ethan-Russell added the bug Something isn't working label Sep 14, 2023
@Ethan-Russell
Copy link
Member Author

Ethan-Russell commented Sep 14, 2023

A brain dump.

If we say that storage is on the generator side:

power discharged should be added to pgen_bus
power charged should be subtracted from pgen_bus

That is theoretically what is happening in line 456-457 of Storage.jl

Ok. Let's find an example of when things are messed up.

julia> out_path = raw"L:\Project-Gurobi\Workspace3\E4ST_Output\baseline_testing\bar_hom\230906_140425427"

julia> data = read_processed_results(out_path)

julia> pdischarge = get_raw_result(data, :pdischarge_stor);

julia> pcharge = get_raw_result(data, :pcharge_stor);

julia> p = pdischarge .* pcharge;

julia> count(>(1), p)
1254 # This means there are 1254 times for which there is simultaneous charging & discharging

julia> findmax(p)
(1.1764705813597874e6, CartesianIndex(209, 1, 43))

julia> day = get_table_col(data, :hours, :e4st_day)[43]
12

julia> hr_idxs = get_hour_idxs(data, :e4st_day=>day)
2-element Vector{Int64}:
 43
 44

julia> pcharge[209, 1, hr_idxs]
2-element Vector{Float64}:
 1176.4705851141678
    9.690407329034632e-7

julia> pdischarge[209, 1, hr_idxs]
2-element Vector{Float64}:
 999.9999968087767
   1.3619506173416022e-6

julia> 999.9999968087767 / 1176.4705851141678
0.849999999542474 # Round trip storage efficiency of 85%

# Yup, we are charging a lot here.  First thing to check is LMP's
julia> get_table_col(data, :storage, :lmp_e)[209][ 1, hr_idxs]
2-element Vector{Float64}:
 45.72981667241463
 19.777874344097867

# Just to make sure we're getting the correct LMP here:
julia> get_table_col(data, :bus, :lmp_elserv_preloss)[get_table_col(data, :storage, :bus_idx)[209]][1, hr_idxs]
2-element Vector{Float64}:
 45.72981667241463
 19.777874344097867

# Rats.  Positive LMP means that increasing the power served would increase the objective, so I don't know why we're seeing this.

# Maybe let's take a look at the `pgen_bus` value
julia> pgen_bus[get_table_col(data, :storage, :bus_idx)[209], 1, hr_idxs]
2-element Vector{Float64}:
 1999.2075872742473
    8.155446399178975e-7

julia> gen_idxs = get_table_row_idxs(data, :gen, :bus_idx=>stor_bus_idx)
5-element Vector{Int64}:
  8227
 10024
 18493
 20760
 20761

julia> pgen_gen[gen_idxs, 1, hr_idxs]
5×2 Matrix{Float64}:
    4.18739e-8  3.86825e-7
 2175.68        0.0
    8.4827e-8   4.22689e-9
    1.18931e-8  1.34263e-8
    1.56859e-8  1.81568e-8

# Ah, all the generation is from a single generator, 10024.  Let's check out its cost of operation:
julia> obj_coef = get_raw_result(data, :obj_coef);
julia> obj_pgen_gen = obj_coef[:pgen_gen];
julia> obj_pgen_gen_unweighted = unweight_hourly(data, obj_pgen_gen)
julia> obj_pgen_gen_unweighted[10024, 1, hr_idxs]
julia> obj_pgen_gen_unweighted[10024, 1, hr_idxs]
2-element Vector{Float64}:
 -22.38
 -22.38

# That looks like an IRA ptc value.
julia> gen.pcap[10024]
1-element view(::Matrix{Float64}, 1, :) with eltype Float64:
 2999.9999999493302

julia> gen.af[10024][hr_idxs]
2-element Vector{Float64}:
 0.725226058959961
 0.0

julia> gen.pcap[10024] .*  0.725226058959961
1-element Vector{Float64}:
 2175.6781768431356

# Ah.  So there is a generator with maxed out capacity and generation with negative gen cost

# Let's check and see why we wouldn't be discharging after - could be that e0 is maxed out
julia> e0 = get_raw_result(data, :e0_stor)
julia> e0[stor_idx,1]
3999.9999976746653

# Yup.  Seems like our yearly e0 is hurting us a bit.

julia> sum(weight_hourly(data, pdischarge) .* (pdischarge .* pcharge .> 1))
1.950544207651207e7

julia> sum(weight_hourly(data, pdischarge))
7.695451029947007e7

So in conclusion, our initial stored energy in the battery, e0, is sometimes getting maxed out to the point where it cannot generate more

@Ethan-Russell
Copy link
Member Author

Ok so we need the following:

  • e0 should become a daily variable because our hours representation isn't that sequential.
  • (optional) constrain pcharge + pdischarge to be <= max of (pcharge_max and pdischarge_max)

@Ethan-Russell
Copy link
Member Author

After making those two changes, we are still seeing simultaneous charging and discharging, albeit less frequently.

Let's take a look at another case

julia> data = read_processed_results("L:/Project-Gurobi/Workspace3/E4ST_Output/baseline_testing/bar_hom\\230914_162839691");

julia> pcharge = get_raw_result(data, :pcharge_stor);

julia> pdischarge = get_raw_result(data, :pdischarge_stor);

julia> p = pdischarge .* pcharge;

julia> count(>(10), p)
790

julia> findmax(p)
(345261.40185765864, CartesianIndex(218, 1, 36))

julia> stor_idx = 218
218

julia> hr_idxs^C

julia> day = get_table_col(data, :hours, :e4st_day)[36]
8

julia> hr_idxs = get_hour_idxs(data, :e4st_day=>day)
2-element Vector{Int64}:
 35
 36

julia> get_table(data, :hours, hr_idxs)
2×14 SubDataFrame
 Row │ hour_id  src_file                           description                        hours    year   month  day    hour   season   e4st_day  hrs_per_day  hr_idx  e4st_day_hour_duration  ozone_season 
     │ String3  String                             String                             Float64  Int64  Int64  Int64  Int64  String7  Int64     Int64        Int64   Int64                   Int64        
─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ h35      L:\\Project-Gurobi\\Workspace3\\  us-can: load weighted average of  76.9171   2008      8      6     17  summer          8            2      35                       4             1
   2 │ h36      L:\\Project-Gurobi\\Workspace3\\  us-can: load weighted average of  76.9171   2008      8      6     13  summer          8            2      36                       4             1

julia> pcharge[stor_idx, 1, hr_idxs]
2-element Vector{Float64}:
 656.072149189513
 615.7875374234399

julia> pdischarge[stor_idx, 1, hr_idxs]
2-element Vector{Float64}:
 520.398061636471
 560.6826719850344

julia> pdischarge[stor_idx,:, hr_idxs] .+ pcharge[stor_idx, :, hr_idxs]
1×2 Matrix{Float64}:
 1176.47  1176.47

# Ok, so our new constraint is not being violated here, and we're seeing some slight differences in the amount of charge/discharge in each hour

julia> sum(pdischarge[stor_idx, 1, hr_idxs]) / sum(pcharge[stor_idx, 1, hr_idxs])
0.8500000000003894 # No violation of our efficiency constraint

julia> pcharge[stor_idx,1, hr_idxs] .- pdischarge[stor_idx, 1, hr_idxs]
2-element Vector{Float64}:
 135.674087553042
  55.104865438405454

# A bit more load added to the grid in the first hour than in the 2nd hour.  Let's dig into the LMP.
julia> get_table_col(data, :bus, :lmp_elserv_preloss)[get_table_col(data, :storage, :bus_idx)[stor_idx]][1, hr_idxs]
2-element Vector{Float64}:
 0.08949863770090605
 0.08949863774134087

julia> stor_bus_idx = get_table_col(data, :storage, :bus_idx)[stor_idx]
218

julia> pgen_bus = get_raw_result(data, :pgen_bus); pgen_gen = get_raw_result(data, :pgen_gen);

julia> pgen_bus[stor_bus_idx, 1, hr_idxs]
2-element Vector{Float64}:
 -135.67405792881732
  -55.10483585548184

julia> stor_bus_idx = get_table_col(data, :storage, :bus_idx)[stor_idx]
218

julia> pgen_bus = get_raw_result(data, :pgen_bus); pgen_gen = get_raw_result(data, :pgen_gen);

julia> pgen_bus[stor_bus_idx, 1, hr_idxs]
2-element Vector{Float64}:
 -135.67405792881732
  -55.10483585548184

julia> gen_idxs = get_table_row_idxs(data, :gen, :bus_idx=>stor_bus_idx);

julia> pgen_gen[gen_idxs, 1, hr_idxs]
23×2 Matrix{Float64}:
 2.3553e-8   2.39795e-8
 2.91271e-5  2.90053e-5
 2.37537e-8  2.4189e-8
 0.0         0.0
 3.55872e-8  3.59832e-8
 1.77827e-8  1.80557e-8
 2.41219e-8  2.45704e-8
 3.66558e-8  3.76026e-8
 0.0         0.0
 2.09853e-8  2.13719e-8
 1.77358e-8  1.80055e-8
 2.05869e-8  2.09049e-8
 3.51248e-8  3.5477e-8
 1.98514e-8  1.99209e-8
 2.07689e-8  2.11016e-8
 2.16953e-8  2.20374e-8
 3.59679e-8  3.62593e-8
 4.77814e-8  4.93427e-8
 3.39119e-8  1.02346e-7
 3.81812e-8  3.85238e-8
 0.0         4.70806e-9
 1.15352e-8  1.16289e-8
 1.15328e-8  1.1627e-8

# Huh, no generation at this bus.  But very small LMP.

# Just to keep track of how much discharging is happening while there is charging happening too, I will come up with a metric:
julia> sum(weight_hourly(data, pdischarge) .* (pdischarge .* pcharge .> 1))
1.0157812171984944e7

julia> sum(weight_hourly(data, pdischarge))
7.739219724255703e7

# That's about 10 million TWh out of 77 million TWh of total battery discharge.

@Ethan-Russell
Copy link
Member Author

I think I'd like to add a small VOM to our storage to see if that significantly reduces the problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants