Optimization issue
AlannLiu opened this issue · comments
We are using GenX to model capacity expansion for wave energy. We are sweeping different costs of wave energy and seeing the results using GenX. The y axis corresponds to availability profiles and the x axis denotes costs.
After analyzing the ISONE Singlezone in the Real System Example, the cases found above and to the right of the red square do not seem to converge. We expect them to have the same or very similar capacities, but they differ by a significant amount. The only changing inputs are price and capacity of wave energy, and these cases all have a wave capacity of 0. We have an idea to try and alter the initial conditions but we are not sure if this is viable. Also, does the model have a certain degree of randomness to it that may be behind these issues?
I am working with Alan on this issue. To expand on his description, we are confused why the system cost is not equal for all cases in the red box. We expect the system costs to be equal because the wave capacity is zero for all those cases, and the only variables changing in the different cases are related to wave energy cost and wave availability profile. So we think the linear optimization might not be converging, which is why we would like to try changing the initial condition to help it converge. We cannot find a way to alter the initial condition in GenX.
Hi Alann and Rebecca,
Thanks for reaching out about this. I think I understand your issue - what you're trying to do and the outcome that you're expecting. Here are some questions to start to narrow it down:
- What version of GenX are you running?
- What solver are you using?
- What operating system?
- What version of julia?
- How are you implementing the wave energy plant? Is it the same as a standard VRE resource (e.g. did you start by copying one of the wind or solar resources)?
- Is the case very similar to the RealSystemExample/ISONE_Singlezone, the only difference being this wave resource?
- What policies are active? i.e. (Reserves, CapacityReserveMargin, MinCapReq/MaxCapReq, CO2Cap)?
- In general there's nothing random per se; solves should be deterministic. However, small changes to coefficients (even coefficients that end up being irrelevant, such as some ancillary cost of a very-expensive wave generator which ends up with 0 capacity) may lead to different individual outcomes e.g. for operational time series and perhaps for capacities. BUT the objective should still be the same within the 'optimality tolerance' which by default in that example is 1e-5. It looks like your objective value ranges over 10% or something.
I think in general if the solution is infeasible it'll say so in the log and I think you might not even get a full output.
As per "initial conditions" I think you're referring to a "warm start" in optimization parlance. This would be setting the initial solution in the solver to whatever the previous optimal solution was.
(I presume you're not referring to 'initial conditions' like in the operational time series in the simulation.)
This is possible with GenX, with some work, but it would require some careful rearranging of the run script (the stuff in run_genx_case!
) and probably parts deeper than that. This being said, you really shouldn't need to warm-start the solver if you're just plotting the objective.
- Would it be possible to share any logs?
- Would it be possible to share the case?
We are using Gurobi as the solver in GenX v.0.3.6.
I am using Julia version 1.9.1.
My operating system is Windows 10.
The active policies are EnergyShareRequirement, CapacityReserveMargin, CO2Cap, StorageLosses, MinCapReq, ParameterScale, WriteShadowPrices, UCommit (active with linear clustering) and TimeDomainReduction.
We implement the wave energy plant by adding a row to generators_data.csv and generators_variability.csv with resource type other_renewables .
We use the case runner to create cases where the wave energy has different availability profiles and costs. Our implementation can be found here: https://github.com/symbiotic-engineering/GenX.
The case is identical except for the addition of the wave resource.
Thanks for the help. Yes, by initial conditions we were referring to warm start, not anything with the timeseries, it is good to know that this should not be necessary. We would be happy to share logs but we're not sure how to obtain them.
Ah, neat. I'm impressed that you've figured out a way to use the GenX case runner to replace whole time series!
I'm taking a look now.
One thing I've noticed here is that I see you're running TimeDomainReduction. This could very well be the reason for the scatter you're seeing. While I'm not 100% certain, TDR may actually have a random component due to part of the time-clustering algorithm.
Additionally, I don't know TDR well enough to know how it would behave with the wave generator time series.
As you said in your report that each run takes a minute or so, I might recommend full-year runs (with TDR off). It might take 10x longer but should give much more consistent results.
You should be able to see the logs for your cases where you run caserunner.jl (I assume you start it in some sort of terminal?).
Here's what I'm referring to:
something like
112 5.70548812e+03 5.70544849e+03 3.36e-08 1.53e-11 5.35e-08 43s
113 5.70548812e+03 5.70544849e+03 5.02e-08 1.53e-11 5.35e-08 43s
114 5.70548812e+03 5.70544849e+03 5.02e-08 1.52e-11 5.35e-08 44s
115 5.70548812e+03 5.70544851e+03 5.02e-08 1.53e-11 5.35e-08 44s
116 5.70548812e+03 5.70544851e+03 5.02e-08 1.52e-11 5.35e-08 45s
117 5.70548811e+03 5.70544853e+03 5.73e-08 1.52e-11 5.34e-08 45s
118 5.70548811e+03 5.70544854e+03 5.73e-08 1.52e-11 5.34e-08 46s
119 5.70548811e+03 5.70544854e+03 5.73e-08 1.53e-11 5.34e-08 46s
Barrier performed 119 iterations in 46.15 seconds
Sub-optimal termination - objective 5.70548815e+03
Crossover log...
76925 DPushes remaining with DInf 0.0000000e+00 46s
0 DPushes remaining with DInf 0.0000000e+00 49s
28728 PPushes remaining with PInf 6.6979336e-02 49s
16759 PPushes remaining with PInf 4.5097204e-01 50s
0 PPushes remaining with PInf 0.0000000e+00 55s
Push phase complete: Pinf 0.0000000e+00, Dinf 2.1770566e+05 55s
Iteration Objective Primal Inf. Dual Inf. Time
84850 5.7055649e+03 0.000000e+00 2.177057e+05 55s
85213 5.7055334e+03 0.000000e+00 7.554712e+02 56s
86665 5.7055333e+03 0.000000e+00 1.188795e+02 61s
88480 5.7054922e+03 0.000000e+00 7.128028e-01 66s
Extra simplex iterations after uncrush: 10
88971 5.7054815e+03 0.000000e+00 0.000000e+00 69s
... typically it looks like it goes on to Optimal Objective
. That's what you want.
... I see that the Period_Map files are significantly different between different cases. This would be why the total objective is so different: you're using totally different representative weeks.
It miiiiight be possible to arrange the TDR settings to be more deterministic.
If you really want to avoid full-year runs there could be another way around it (basically, of arranging the time series and __SPECIAL_KEY_indices__
quasi-manually) but that would be annoying.
TLDR; set TimeDomainReduction to 0 in genx_settings.yml and try again. I think you'll get consistent results then.
Thanks for the detailed response! We'll give this a try.
Is this still an issue? Were you able to solve it?
The issue was resolved by turning off TDR.