Tutorial 6: Solver Settings

Interactive Notebook of the tutorial

Though solving the model relies only on optimize, there are a number of ways to change the way in which the model is optimized. This tutorial goes over solver parameters and how they affect the model solution.

Table of Contents

using YAML
using GenX
using JuMP
using HiGHS
using DataFrames
using Plots
using Plotly
case = joinpath("example_systems/1_three_zones") 

genx_settings = GenX.get_settings_path(case, "genx_settings.yml");
writeoutput_settings = GenX.get_settings_path(case, "output_settings.yml")
setup = GenX.configure_settings(genx_settings,writeoutput_settings) 
settings_path = GenX.get_settings_path(case)
    Configuring Settings

    "example_systems/1_three_zones/settings"
### Create TDR_Results
if "TDR_results" in cd(readdir,case)
    rm(joinpath(case,"TDR_results"), recursive=true) 
end

TDRpath = joinpath(case, setup["TimeDomainReductionFolder"])
system_path = joinpath(case, setup["SystemFolder"])

if setup["TimeDomainReduction"] == 1
    GenX.prevent_doubled_timedomainreduction(system_path)
    if !GenX.time_domain_reduced_files_exist(TDRpath)
        println("Clustering Time Series Data (Grouped)...")
        GenX.cluster_inputs(case, settings_path, setup)
    else
        println("Time Series Data Already Clustered.")
    end
end

inputs = GenX.load_inputs(setup, case)

The HiGHS Solver

In the example files, the solver is HiGHS. HiGHS is freely available for all to use. Other solvers, such as Gurobi, are available for free for academics. For the purpose of this tutorial, we will be focusing on HiGHS.

To set the solver preferences, go into the settings folder of your case and select the YAML file of the solver you're using.

settings_folder = cd(readdir,joinpath(case,"Settings")) # Print Settings folder
    7-element Vector{String}:
    ".DS_Store"
    "clp_settings.yml"
    "cplex_settings.yml"
    "genx_settings.yml"
    "gurobi_settings.yml"
    "highs_settings.yml"
    "time_domain_reduction_settings.yml"
highs_settings = YAML.load(open(joinpath(case,"Settings/highs_settings.yml")))
    Dict{Any, Any} with 6 entries:
      "Method"        => "choose"
      "Feasib_Tol"    => 0.1
      "run_crossover" => "on"
      "TimeLimit"     => 1.0e23
      "Optimal_Tol"   => 0.1
      "Pre_Solve"     => "choose"

The function configure_highs in src/configure_solver contains a list of default settings for the HiGHS solver

png

There are about 80, so we'll only focus on a few for now. In most cases, you can leave the other settings on default.

The default settings are combined with the settings you specify in highs_settings.yml in configure_highs, which is called from configure_solver in run_genx_case_simple right before the model is generated.

Feasibility Tolerance

The parameters Feasib_Tol and Optimal_Tol represent the feasibility of the primal and dual functions respectively. Without going into too much detail, a dual function is an analagous formulation of the original ("primal") function whose objective value acts as a lower bound to the primal function. The objective value of the primal function is then the upper bound of the dual function. HiGHS will solve the dual and primal at each time step, then terminate when the solutions of the two are within a certain tolerance range. For more information on how this works specifically in HiGHS, see the HiGHS documentaion.

If we decrease the tolerance parameters, the objective value becomes closer to the "true" optimal value.

# Change tolerance, generate and solve model`
tols = [1e-7,1e-4,1e-2,1e-1]
OV = zeros(1,4)

for i in range(1,length(tols))
    println(" ")
    println("----------------------------------------------------")
    println("Iteration ",i)
    println("Tolerance = ",tols[i])
    println("----------------------------------------------------")
    highs_settings["Feasib_Tol"] = tols[i]
    highs_settings["Optimal_Tol"] = tols[i]
    YAML.write_file(joinpath(case,"settings/highs_settings.yml"), highs_settings)
    OPTIMIZER1 = GenX.configure_solver(settings_path,HiGHS.Optimizer)
    EP = GenX.generate_model(setup,inputs,OPTIMIZER1)
    GenX.solve_model(EP,setup)
    OV[i] = objective_value(EP)
end

Using the smallest tolerance as our base, we can see the error as the tolerance increases:

DataFrame([tols[2:end] abs.(OV[2:end] .- OV[1])],["Tolerance", "Error"])
3×2 DataFrame
    Row	Tolerance	Error
        Float64	    Float64
    ───────────────────────────
    1	0.0001	    0.0
    2	0.01	    1.81899e-12
    3	0.1	        3.45608e-11
using Plots
using Plotly
# Plot the error as a function of the tolerance
plotlyjs()
Plots.scatter(tols[2:end], abs.(OV[2:end] .- OV[1]),legend=:topleft,
                ylabel="Error", xlabel="Tolerance",size=(920,400),label=:"Error",title="Tolerance of Solver vs Error")
ygrid!(:on, :dashdot, 0.1)

svg

PreSolve

In optimization, presolve is a stage at the beginning of the solver in which the problem is simplified to remove redunant constraints and otherwise simplify the problem before the optimization itself begins. The default for presolve in GenX is "choose", allowing the solver to use presolve only if it will reduce computation time.

Let's try setting presolve to off and on, then compare computation times.

# First, set tolerances back to original
highs_settings["Feasib_Tol"] = 1e-5
highs_settings["Optimal_Tol"] = 1e-5
YAML.write_file(joinpath(case,"Settings/highs_settings.yml"), highs_settings)    
highs_settings["Pre_Solve"] = "off"
YAML.write_file(joinpath(case,"Settings/highs_settings.yml"), highs_settings)
OPTIMIZER2 = GenX.configure_solver(setup["Solver"], settings_path);
EP2 = GenX.generate_model(setup,inputs,OPTIMIZER2)
    Discharge Module
    Non-served Energy Module
    Investment Discharge Module
    Unit Commitment Module
    Emissions Module (for CO2 Policy modularization
    Dispatchable Resources Module
    Storage Resources Module
    Storage Investment Module
    Storage Core Resources Module
    Storage Resources with Symmetric Charge/Discharge Capacity Module
    Thermal (Unit Commitment) Resources Module
    C02 Policies Module
    Energy Share Requirement Policies Module
    Capacity Reserve Margin Policies Module
    Minimum Capacity Requirement Module
    Maximum Capacity Requirement Module

    A JuMP Model
    Minimization problem with:
    Variables: 18492
    Objective function type: AffExpr
    `AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 5544 constraints
    `AffExpr`-in-`MathOptInterface.GreaterThan{Float64}`: 7398 constraints
    `AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 27730 constraints
    `VariableRef`-in-`MathOptInterface.EqualTo{Float64}`: 2 constraints
    `VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 18490 constraints
    Model mode: AUTOMATIC
    CachingOptimizer state: EMPTY_OPTIMIZER
    Solver name: HiGHS
    Names registered in the model: cCO2Emissions_systemwide, cCapacityResMargin, cESRShare, cMaxCap, cMaxCapEnergy, cMaxCapEnergyDuration, cMaxNSE, cMaxRetCommit, cMaxRetEnergy, cMaxRetNoCommit, cMinCap, cMinCapEnergy, cMinCapEnergyDuration, cNSEPerSeg, cPowerBalance, cSoCBalInterior, cSoCBalStart, cZoneMaxCapReq, cZoneMinCapReq, eCFix, eCFixEnergy, eCNSE, eCStart, eCVar_in, eCVar_out, eCapResMarBalance, eCapResMarBalanceStor, eCapResMarBalanceThermal, eCapResMarBalanceVRE, eELOSS, eELOSSByZone, eESR, eESRDischarge, eEmissionsByPlant, eEmissionsByZone, eExistingCap, eExistingCapEnergy, eGenerationByThermAll, eGenerationByVRE, eGenerationByZone, eMaxCapRes, eMaxCapResInvest, eMinCapRes, eMinCapResInvest, eObj, ePowerBalance, ePowerBalanceDisp, ePowerBalanceNse, ePowerBalanceStor, ePowerBalanceThermCommit, eTotalCFix, eTotalCFixEnergy, eTotalCNSE, eTotalCNSET, eTotalCNSETS, eTotalCStart, eTotalCStartT, eTotalCVarIn, eTotalCVarInT, eTotalCVarOut, eTotalCVarOutT, eTotalCap, eTotalCapEnergy, vCAP, vCAPENERGY, vCHARGE, vCO2Cap_slack, vCOMMIT, vNSE, vP, vRETCAP, vRETCAPENERGY, vS, vSHUT, vSTART, vZERO
solution2 = @elapsed GenX.solve_model(EP2,setup)
    Running HiGHS 1.6.0: Copyright (c) 2023 HiGHS under MIT licence terms
    Solving LP without presolve or with basis
    Using EKK dual simplex solver - serial
      Iteration        Objective     Infeasibilities num(sum)
              0    -4.3842305368e+02 Ph1: 19318(12391.6); Du: 5(438.423) 0s
          18662     9.5083526285e+03 Pr: 3142(5018.36); Du: 0(0.000872182) 5s
          23359     9.8583752055e+03 Pr: 0(0); Du: 0(1.27565e-13) 6s
    Model   status      : Optimal
    Simplex   iterations: 23359
    Objective value     :  9.8583752055e+03
    HiGHS run time      :          6.77
    LP solved for primal

    6.84933975
highs_settings["Pre_Solve"] = "on"
YAML.write_file(joinpath(case,"Settings/highs_settings.yml"), highs_settings)
OPTIMIZER3 = GenX.configure_solver(setup["Solver"], settings_path);
EP3 = GenX.generate_model(setup,inputs,OPTIMIZER3)
    Discharge Module
    Non-served Energy Module
    Investment Discharge Module
    Unit Commitment Module
    Emissions Module (for CO2 Policy modularization
    Dispatchable Resources Module
    Storage Resources Module
    Storage Investment Module
    Storage Core Resources Module
    Storage Resources with Symmetric Charge/Discharge Capacity Module
    Thermal (Unit Commitment) Resources Module
    C02 Policies Module
    Energy Share Requirement Policies Module
    Capacity Reserve Margin Policies Module
    Minimum Capacity Requirement Module
    Maximum Capacity Requirement Module

    A JuMP Model
    Minimization problem with:
    Variables: 18492
    Objective function type: AffExpr
    `AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 5544 constraints
    `AffExpr`-in-`MathOptInterface.GreaterThan{Float64}`: 7398 constraints
    `AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 27730 constraints
    `VariableRef`-in-`MathOptInterface.EqualTo{Float64}`: 2 constraints
    `VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 18490 constraints
    Model mode: AUTOMATIC
    CachingOptimizer state: EMPTY_OPTIMIZER
    Solver name: HiGHS
    Names registered in the model: cCO2Emissions_systemwide, cCapacityResMargin, cESRShare, cMaxCap, cMaxCapEnergy, cMaxCapEnergyDuration, cMaxNSE, cMaxRetCommit, cMaxRetEnergy, cMaxRetNoCommit, cMinCap, cMinCapEnergy, cMinCapEnergyDuration, cNSEPerSeg, cPowerBalance, cSoCBalInterior, cSoCBalStart, cZoneMaxCapReq, cZoneMinCapReq, eCFix, eCFixEnergy, eCNSE, eCStart, eCVar_in, eCVar_out, eCapResMarBalance, eCapResMarBalanceStor, eCapResMarBalanceThermal, eCapResMarBalanceVRE, eELOSS, eELOSSByZone, eESR, eESRDischarge, eEmissionsByPlant, eEmissionsByZone, eExistingCap, eExistingCapEnergy, eGenerationByThermAll, eGenerationByVRE, eGenerationByZone, eMaxCapRes, eMaxCapResInvest, eMinCapRes, eMinCapResInvest, eObj, ePowerBalance, ePowerBalanceDisp, ePowerBalanceNse, ePowerBalanceStor, ePowerBalanceThermCommit, eTotalCFix, eTotalCFixEnergy, eTotalCNSE, eTotalCNSET, eTotalCNSETS, eTotalCStart, eTotalCStartT, eTotalCVarIn, eTotalCVarInT, eTotalCVarOut, eTotalCVarOutT, eTotalCap, eTotalCapEnergy, vCAP, vCAPENERGY, vCHARGE, vCO2Cap_slack, vCOMMIT, vNSE, vP, vRETCAP, vRETCAPENERGY, vS, vSHUT, vSTART, vZERO
solution3 = @elapsed GenX.solve_model(EP3,setup)
    Running HiGHS 1.6.0: Copyright (c) 2023 HiGHS under MIT licence terms
    Presolving model
    35947 rows, 17464 cols, 136177 nonzeros
    34470 rows, 15991 cols, 136110 nonzeros
    Presolve : Reductions: rows 34470(-6202); columns 15991(-2501); elements 136110(-29848)
    Solving the presolved LP
    Using EKK dual simplex solver - serial
      Iteration        Objective     Infeasibilities num(sum)
              0    -2.9011432493e+00 Ph1: 118(557.293); Du: 15(2.90114) 0s
          16445     9.8583752055e+03 Pr: 0(0); Du: 0(1.25316e-13) 4s
    Solving the original LP from the solution after postsolve
    Model   status      : Optimal
    Simplex   iterations: 16445
    Objective value     :  9.8583752055e+03
    HiGHS run time      :          4.55
    LP solved for primal

    4.655439792

As we can see, the runtime with PreSolve is shorter, and would be even shorter for a larger system. However, it could introduce numerical inaccuracies. If you find the model is struggling to converge, try turn PreSolve off.

# Write PreSolve back to choose
highs_settings["Pre_Solve"] = "choose"
YAML.write_file(joinpath(case,"Settings/highs_settings.yml"), highs_settings)

Crossover

Crossover is a method in which, at each step of the optimization algorithm, the solution is pushed to the boundary of the solution space. This allows for a potentially more accurate solution, but can be computationally intensive. Let's try turning crossover on and off and see what solutions we get:

highs_settings["run_crossover"] = "off"
YAML.write_file(joinpath(case,"Settings/highs_settings.yml"), highs_settings)
OPTIMIZER4 = GenX.configure_solver(setup["Solver"], settings_path);
EP4 = GenX.generate_model(setup,inputs,OPTIMIZER4)
    Discharge Module
    Non-served Energy Module
    Investment Discharge Module
    Unit Commitment Module
    Emissions Module (for CO2 Policy modularization
    Transmission Module
    Dispatchable Resources Module
    Storage Resources Module
    Storage Investment Module
    Storage Core Resources Module
    Storage Resources with Symmetric Charge/Discharge Capacity Module
    Thermal (Unit Commitment) Resources Module
    C02 Policies Module
    Minimum Capacity Requirement Module

    A JuMP Model
    Minimization problem with:
    Variables: 83192
    Objective function type: AffExpr
    `AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 24024 constraints
    `AffExpr`-in-`MathOptInterface.GreaterThan{Float64}`: 20334 constraints
    `AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 103509 constraints
    `VariableRef`-in-`MathOptInterface.EqualTo{Float64}`: 4 constraints
    `VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 79492 constraints
    Model mode: AUTOMATIC
    CachingOptimizer state: EMPTY_OPTIMIZER
    Solver name: HiGHS
    Names registered in the model: cCO2Emissions_systemwide, cMaxCap, cMaxCapEnergy, cMaxCapEnergyDuration, cMaxFlow_in, cMaxFlow_out, cMaxLineReinforcement, cMaxNSE, cMaxRetCommit, cMaxRetEnergy, cMaxRetNoCommit, cMinCap, cMinCapEnergy, cMinCapEnergyDuration, cNSEPerSeg, cPowerBalance, cSoCBalInterior, cSoCBalStart, cTAuxLimit, cTAuxSum, cTLoss, cZoneMinCapReq, eAvail_Trans_Cap, eCFix, eCFixEnergy, eCNSE, eCStart, eCVar_in, eCVar_out, eELOSS, eELOSSByZone, eEmissionsByPlant, eEmissionsByZone, eExistingCap, eExistingCapEnergy, eGenerationByThermAll, eGenerationByVRE, eGenerationByZone, eLosses_By_Zone, eMinCapRes, eMinCapResInvest, eNet_Export_Flows, eObj, ePowerBalance, ePowerBalanceDisp, ePowerBalanceLossesByZone, ePowerBalanceNetExportFlows, ePowerBalanceNse, ePowerBalanceStor, ePowerBalanceThermCommit, eTotalCFix, eTotalCFixEnergy, eTotalCNSE, eTotalCNSET, eTotalCNSETS, eTotalCNetworkExp, eTotalCStart, eTotalCStartT, eTotalCVarIn, eTotalCVarInT, eTotalCVarOut, eTotalCVarOutT, eTotalCap, eTotalCapEnergy, eTransMax, vCAP, vCAPENERGY, vCHARGE, vCO2Cap_slack, vCOMMIT, vFLOW, vNEW_TRANS_CAP, vNSE, vP, vRETCAP, vRETCAPENERGY, vS, vSHUT, vSTART, vTAUX_NEG, vTAUX_POS, vTLOSS, vZERO
solution4 = @elapsed GenX.solve_model(EP4,setup)
    Running HiGHS 1.6.0: Copyright (c) 2023 HiGHS under MIT licence terms
    Presolving model
    123675 rows, 81174 cols, 478190 nonzeros
    116575 rows, 74076 cols, 478470 nonzeros
    Presolve : Reductions: rows 116575(-31292); columns 74076(-9116); elements 478470(-75676)
    Solving the presolved LP
    Using EKK dual simplex solver - serial
      Iteration        Objective     Infeasibilities num(sum)
              0     1.9243583242e+03 Pr: 5545(9397.81); Du: 0(3.92542e-09) 0s
          13530     2.9999821788e+03 Pr: 30131(9.49605e+06); Du: 0(0.0013131) 5s
          17547     3.5484267823e+03 Pr: 19414(952045); Du: 0(0.00175439) 10s
          20723     4.7298157079e+03 Pr: 29321(991206); Du: 0(0.00183106) 15s
          23580     5.7123748112e+03 Pr: 32150(7.12339e+06); Du: 0(0.00148821) 21s
          26170     6.1864339355e+03 Pr: 23825(7.35638e+06); Du: 0(0.00149712) 26s
          29149     6.6441899572e+03 Pr: 27775(3.51868e+06); Du: 0(0.00148729) 31s
          31348     6.8846964690e+03 Pr: 35484(2.8051e+06); Du: 0(0.00139557) 36s
          33603     6.9920173954e+03 Pr: 24118(2.03558e+06); Du: 0(0.00124593) 42s
          35671     7.2015870783e+03 Pr: 34108(1.48506e+07); Du: 0(0.00155189) 47s
          37885     7.4048679609e+03 Pr: 31619(4.35123e+07); Du: 0(0.00179926) 52s
          40109     7.6356641465e+03 Pr: 25843(2.88592e+06); Du: 0(0.00205301) 58s
          42353     7.8375259516e+03 Pr: 31443(8.49023e+06); Du: 0(0.00226205) 64s
          45258     8.1692860958e+03 Pr: 37870(5.26412e+06); Du: 0(0.00255441) 69s
          48083     8.3152673717e+03 Pr: 40961(1.14875e+07); Du: 0(0.00294817) 75s
          50545     8.4510258333e+03 Pr: 30790(3.72272e+08); Du: 0(0.00304629) 80s
          52861     8.5807507297e+03 Pr: 22492(930796); Du: 0(0.00320519) 85s
          54980     8.6629986832e+03 Pr: 30776(2.08885e+07); Du: 0(0.00337039) 91s
          57333     8.7549581833e+03 Pr: 26257(1.13046e+06); Du: 0(0.00357615) 96s
          59389     8.8000584935e+03 Pr: 36678(2.40379e+06); Du: 0(0.00364701) 101s
          61669     8.8767323679e+03 Pr: 35715(2.44371e+06); Du: 0(0.00390224) 107s
          63914     8.9782186363e+03 Pr: 43654(3.60725e+06); Du: 0(0.00423026) 113s
          66084     9.0603325781e+03 Pr: 22220(2.48776e+06); Du: 0(0.00410399) 118s
          69022     9.1231092369e+03 Pr: 22727(1.78641e+06); Du: 0(0.00444875) 124s
          71953     9.1538574545e+03 Pr: 22264(5.37124e+06); Du: 0(0.00461629) 129s
          74846     9.1867207120e+03 Pr: 26970(1.49142e+06); Du: 0(0.00477614) 135s
          77129     9.2058130738e+03 Pr: 0(0); Du: 0(0.00107487) 140s
          77129     9.2058130738e+03 Pr: 0(0); Du: 0(0.00107487) 140s
    Using EKK dual simplex solver - serial
      Iteration        Objective     Infeasibilities num(sum)
          77129     9.2058130738e+03 Pr: 1(0.000235421); Du: 0(0.000836801) 140s
          77136     9.2058130749e+03 Pr: 0(0); Du: 0(0.000836801) 140s
          77136     9.2058130749e+03 Pr: 0(0); Du: 0(0.000836801) 140s
    Solving the original LP from the solution after postsolve
    Model   status      : Optimal
    Simplex   iterations: 77136
    Objective value     :  9.2058130749e+03
    HiGHS run time      :        140.51
    LP solved for primal

    140.762363
highs_settings["run_crossover"] = "on"
YAML.write_file(joinpath(case,"Settings/highs_settings.yml"), highs_settings)
OPTIMIZER5 = GenX.configure_solver(setup["Solver"], settings_path);
EP5 = GenX.generate_model(setup,inputs,OPTIMIZER5)
    Discharge Module
    Non-served Energy Module
    Investment Discharge Module
    Unit Commitment Module
    Emissions Module (for CO2 Policy modularization
    Transmission Module
    Dispatchable Resources Module
    Storage Resources Module
    Storage Investment Module
    Storage Core Resources Module
    Storage Resources with Symmetric Charge/Discharge Capacity Module
    Thermal (Unit Commitment) Resources Module
    C02 Policies Module
    Minimum Capacity Requirement Module

    A JuMP Model
    Minimization problem with:
    Variables: 83192
    Objective function type: AffExpr
    `AffExpr`-in-`MathOptInterface.EqualTo{Float64}`: 24024 constraints
    `AffExpr`-in-`MathOptInterface.GreaterThan{Float64}`: 20334 constraints
    `AffExpr`-in-`MathOptInterface.LessThan{Float64}`: 103509 constraints
    `VariableRef`-in-`MathOptInterface.EqualTo{Float64}`: 4 constraints
    `VariableRef`-in-`MathOptInterface.GreaterThan{Float64}`: 79492 constraints
    Model mode: AUTOMATIC
    CachingOptimizer state: EMPTY_OPTIMIZER
    Solver name: HiGHS
    Names registered in the model: cCO2Emissions_systemwide, cMaxCap, cMaxCapEnergy, cMaxCapEnergyDuration, cMaxFlow_in, cMaxFlow_out, cMaxLineReinforcement, cMaxNSE, cMaxRetCommit, cMaxRetEnergy, cMaxRetNoCommit, cMinCap, cMinCapEnergy, cMinCapEnergyDuration, cNSEPerSeg, cPowerBalance, cSoCBalInterior, cSoCBalStart, cTAuxLimit, cTAuxSum, cTLoss, cZoneMinCapReq, eAvail_Trans_Cap, eCFix, eCFixEnergy, eCNSE, eCStart, eCVar_in, eCVar_out, eELOSS, eELOSSByZone, eEmissionsByPlant, eEmissionsByZone, eExistingCap, eExistingCapEnergy, eGenerationByThermAll, eGenerationByVRE, eGenerationByZone, eLosses_By_Zone, eMinCapRes, eMinCapResInvest, eNet_Export_Flows, eObj, ePowerBalance, ePowerBalanceDisp, ePowerBalanceLossesByZone, ePowerBalanceNetExportFlows, ePowerBalanceNse, ePowerBalanceStor, ePowerBalanceThermCommit, eTotalCFix, eTotalCFixEnergy, eTotalCNSE, eTotalCNSET, eTotalCNSETS, eTotalCNetworkExp, eTotalCStart, eTotalCStartT, eTotalCVarIn, eTotalCVarInT, eTotalCVarOut, eTotalCVarOutT, eTotalCap, eTotalCapEnergy, eTransMax, vCAP, vCAPENERGY, vCHARGE, vCO2Cap_slack, vCOMMIT, vFLOW, vNEW_TRANS_CAP, vNSE, vP, vRETCAP, vRETCAPENERGY, vS, vSHUT, vSTART, vTAUX_NEG, vTAUX_POS, vTLOSS, vZERO
solution5 = @elapsed GenX.solve_model(EP5,setup)
    Running HiGHS 1.6.0: Copyright (c) 2023 HiGHS under MIT licence terms
    Presolving model
    123675 rows, 81174 cols, 478190 nonzeros
    116575 rows, 74076 cols, 478470 nonzeros
    Presolve : Reductions: rows 116575(-31292); columns 74076(-9116); elements 478470(-75676)
    Solving the presolved LP
    Using EKK dual simplex solver - serial
      Iteration        Objective     Infeasibilities num(sum)
              0     1.9243583242e+03 Pr: 5545(9397.81); Du: 0(3.92542e-09) 0s
          13530     2.9999821788e+03 Pr: 30131(9.49605e+06); Du: 0(0.0013131) 5s
          17547     3.5484267823e+03 Pr: 19414(952045); Du: 0(0.00175439) 10s
          20723     4.7298157079e+03 Pr: 29321(991206); Du: 0(0.00183106) 15s
          23580     5.7123748112e+03 Pr: 32150(7.12339e+06); Du: 0(0.00148821) 21s
          26170     6.1864339355e+03 Pr: 23825(7.35638e+06); Du: 0(0.00149712) 26s
          29149     6.6441899572e+03 Pr: 27775(3.51868e+06); Du: 0(0.00148729) 31s
          31348     6.8846964690e+03 Pr: 35484(2.8051e+06); Du: 0(0.00139557) 36s
          33603     6.9920173954e+03 Pr: 24118(2.03558e+06); Du: 0(0.00124593) 42s
          35671     7.2015870783e+03 Pr: 34108(1.48506e+07); Du: 0(0.00155189) 47s
          37885     7.4048679609e+03 Pr: 31619(4.35123e+07); Du: 0(0.00179926) 52s
          40109     7.6356641465e+03 Pr: 25843(2.88592e+06); Du: 0(0.00205301) 58s
          42353     7.8375259516e+03 Pr: 31443(8.49023e+06); Du: 0(0.00226205) 63s
          45258     8.1692860958e+03 Pr: 37870(5.26412e+06); Du: 0(0.00255441) 69s
          48083     8.3152673717e+03 Pr: 40961(1.14875e+07); Du: 0(0.00294817) 75s
          50545     8.4510258333e+03 Pr: 30790(3.72272e+08); Du: 0(0.00304629) 80s
          52861     8.5807507297e+03 Pr: 22492(930796); Du: 0(0.00320519) 85s
          54980     8.6629986832e+03 Pr: 30776(2.08885e+07); Du: 0(0.00337039) 91s
          57333     8.7549581833e+03 Pr: 26257(1.13046e+06); Du: 0(0.00357615) 96s
          59389     8.8000584935e+03 Pr: 36678(2.40379e+06); Du: 0(0.00364701) 101s
          61669     8.8767323679e+03 Pr: 35715(2.44371e+06); Du: 0(0.00390224) 107s
          63914     8.9782186363e+03 Pr: 43654(3.60725e+06); Du: 0(0.00423026) 113s
          66084     9.0603325781e+03 Pr: 22220(2.48776e+06); Du: 0(0.00410399) 118s
          69022     9.1231092369e+03 Pr: 22727(1.78641e+06); Du: 0(0.00444875) 124s
          71953     9.1538574545e+03 Pr: 22264(5.37124e+06); Du: 0(0.00461629) 129s
          74408     9.1814081583e+03 Pr: 29329(5.2193e+06); Du: 0(0.00473215) 134s
          77012     9.2060247446e+03 Pr: 0(0); Du: 0(0.00509972) 140s
          77129     9.2058130738e+03 Pr: 0(0); Du: 0(0.00107487) 140s
          77129     9.2058130738e+03 Pr: 0(0); Du: 0(0.00107487) 140s
    Using EKK dual simplex solver - serial
      Iteration        Objective     Infeasibilities num(sum)
          77129     9.2058130738e+03 Pr: 1(0.000235421); Du: 0(0.000836801) 140s
          77136     9.2058130749e+03 Pr: 0(0); Du: 0(0.000836801) 140s
          77136     9.2058130749e+03 Pr: 0(0); Du: 0(0.000836801) 140s
    Solving the original LP from the solution after postsolve
    Model   status      : Optimal
    Simplex   iterations: 77136
    Objective value     :  9.2058130749e+03
    HiGHS run time      :        140.44
    LP solved for primal

    140.74829025