Repository navigation
Three phase EV with a charge goal never proves: hosted result Feasible, uncapped CBC runs for hours #186
Description
A request evcc builds for a household that is close to the README example never proves on the joint path, so the hosted optimizer returns Feasible after its 10 s. Uncapped, CBC ran 110 minutes on it without closing the gap.
Request
221 steps of 15 min, 53 h. Two batteries: an EV with c_min 4140 W (6 A on three phases), c_max 11040 W, s_initial 10 kWh, s_goal 40 kWh at step 60 (08:00 next morning), and a 10 kWh home battery at 50 % with 5 kW each way. Dynamic tariff between 17 and 45 ct with ±1 ct noise, 119 distinct prices, 8 ct feed-in, 4 kWp PV bell, a piecewise constant 17 kWh/day household profile. Strategies charge_before_export and discharge_before_import.
What the solver does
Hosted, two attempts of the same request shape: Feasible in 8.2 s and 10.3 s. Locally under the production limits: probe 2.1 s unproven, cost stage 0.2 to 3.1 s, tie break LP Optimal, MILP at its cap, Feasible. Locally with an 80 s probe: still unproven. Uncapped: 110 minutes, killed.
Variants, each replayed with a 60 s limit and a 55 s probe:
| variant | path | status |
|---|---|---|
| as sent | split | Feasible, probe unproven at 55 s |
both strategies none |
split | Feasible |
| every price, demand and PV value jittered to be unique | split | Feasible |
| home battery removed | split | Feasible |
EV c_min 0 |
joint | Optimal in 0.1 s |
EV on one phase, c_min 1380 W, c_max 3680 W |
joint | Optimal in 0.3 s |
The minimum charge power of the three phase EV is the whole problem. The cost stage, which runs with the 1 ct absolute gap, proves the same model in 0.2 s. The probe has no gap and tries to close differences below a cent, which the semi-continuous charging variable over 60 cheap slots does not allow it to.
One more experiment
Giving the probe the cost stage's absolute gap, gapAbs=gap_abs * objective_scale on the probe solve, does not rescue this request at 2 s, but it turns the request from #170 into a joint Optimal in 0.7 s where it splits today and takes 5.5 s. Worth a look on its own.
The README (#181) uses the one phase variant so every chart is from an Optimal solve, locally and hosted. The three phase request is the common real world shape and deserves either a faster proof or an honest Optimal at the cost stage's gap.
Request, 221 steps
{"batteries":[{"c_max":11040,"c_min":4140,"charge_from_grid":true,"d_max":0,"p_a":0.000153252,"s_goal":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"s_initial":10000,"s_max":50000,"s_min":0},{"c_max":5000,"c_min":0,"charge_from_grid":true,"d_max":5000,"p_a":0.000153252,"s_capacity":10000,"s_initial":5000,"s_max":10000,"s_min":0}],"eta_c":0.9,"eta_d":0.9,"grid":{},"strategy":{"charging_strategy":"charge_before_export","discharging_strategy":"discharge_before_import"},"time_series":{"dt":[771,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900,900],"ft":[468.2754,493.375,437.125,378,315.875,250.75,182.75,111.75,37.75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37.75,111.75,182.75,250.75,315.875,378,437.125,493.375,546.625,596.875,644.25,688.625,730,768.5,804,836.5,866.125,892.75,916.375,937.125,954.875,969.625,981.5,990.375,996.25,999.25,999.25,996.25,990.375,981.5,969.625,954.875,937.125,916.375,892.75,866.125,836.5,804,768.5,730,688.625,644.25,596.875,546.625,493.375,437.125,378,315.875,250.75,182.75,111.75,37.75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37.75,111.75,182.75,250.75,315.875,378,437.125,493.375,546.625,596.875,644.25,688.625,730,768.5,804,836.5,866.125,892.75,916.375,937.125,954.875,969.625,981.5,990.375,996.25,999.25,999.25,996.25,990.375,981.5,969.625,954.875,937.125,916.375,892.75,866.125,836.5,804,768.5,730,688.625,644.25,596.875,546.625,493.375,437.125,378,315.875,250.75,182.75,111.75,37.75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"gt":[102.8,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,150,150,150,150,150,150,150,150,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,250,250,250,250,250,250,250,250,250,250,250,250,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,150,150,150,150,150,150,150,150,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,250,250,250,250,250,250,250,250,250,250,250,250,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,350,150,150,150,150,150,150,150,150],"p_E":[8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05,8e-05],"p_N":[0.000287,0.000295,0.000295,0.000299,0.00031,0.00032,0.000347,0.000388,0.000429,0.000433,0.000431,0.000387,0.000347,0.000302,0.000293,0.000284,0.000285,0.000279,0.000282,0.000282,0.000277,0.000276,0.000284,0.000279,0.000263,0.000268,0.00027,0.000268,0.000265,0.000267,0.000251,0.000249,0.000258,0.000253,0.000267,0.00025,0.000251,0.000258,0.000266,0.000255,0.000259,0.000262,0.000267,0.000252,0.000261,0.000249,0.000249,0.000258,0.000257,0.000257,0.000273,0.000269,0.000287,0.000279,0.000296,0.000306,0.000296,0.0003,0.00033,0.000334,0.00034,0.000358,0.000341,0.000336,0.000331,0.000315,0.000297,0.000303,0.0003,0.000278,0.000281,0.000271,0.000246,0.000238,0.000231,0.000216,0.000203,0.000172,0.000195,0.000205,0.000214,0.000214,0.000218,0.000233,0.000221,0.000228,0.000246,0.000248,0.00026,0.000267,0.000263,0.000263,0.00027,0.000286,0.000284,0.000279,0.000293,0.000291,0.000297,0.000306,0.000314,0.000327,0.00036,0.000384,0.000431,0.000447,0.000433,0.000394,0.000359,0.000291,0.000292,0.000302,0.000287,0.000297,0.000284,0.000286,0.000286,0.000268,0.000267,0.000281,0.000275,0.000267,0.000258,0.000265,0.000254,0.000259,0.00025,0.000267,0.000261,0.000254,0.000264,0.000251,0.000257,0.00026,0.000251,0.000258,0.000255,0.000265,0.000261,0.000253,0.000266,0.000267,0.000256,0.000253,0.000252,0.000264,0.00027,0.000273,0.000272,0.000294,0.000283,0.000304,0.000301,0.000313,0.000325,0.000333,0.000351,0.000349,0.000346,0.000329,0.000325,0.000317,0.000305,0.000308,0.000295,0.00029,0.000273,0.000261,0.000254,0.000234,0.000224,0.000214,0.000194,0.000183,0.000195,0.000198,0.000198,0.000209,0.000215,0.00023,0.000239,0.000237,0.000243,0.000252,0.000249,0.000261,0.000254,0.000278,0.000282,0.000285,0.00028,0.000281,0.000289,0.0003,0.000289,0.000303,0.000319,0.000335,0.000356,0.000384,0.000422,0.000442,0.000429,0.000392,0.000351,0.000298,0.00029,0.000292,0.00029,0.000286,0.000291,0.000281,0.000275,0.000275,0.000267,0.00027,0.000266,0.000265,0.000273,0.000264,0.000257]}}🤖 Generated with Claude Code
Activity
andig commentedon Oct 4, 2026
Root cause, measured on the attached request with the cost objective alone and a gapped probe, so the strategies play no part.
The LP relaxation ignores
c_min. The charging gate is the textbook pairc ≤ c_max·zandc ≥ c_min·z: for anycbelowc_mina fractionalzsatisfies both, so the relaxation charges the EV with whatever small amounts suit it, 28 slots between 28 and 365 Wh in the morning PV surplus and the leftover below one full slot. Its bound is 0.4685 € for bothc_minvalues, since neither binds in the relaxation.c_minWith
c_min4140 W every one of those small charges has to become a 4.1 kW slot or nothing, which costs 9 ct against the relaxation. The absolute gap is 1 ct, so CBC cannot stop on the bound and has to enumerate 884 binaries to prove the 9 ct are unavoidable. The 0.3757 € schedule it finds within 20 s is the optimum: withOPTIMIZER_GAP_ABSat 5 ct the same request proves to the same schedule in 1.3 s, at 10 ct too.So the defect is not the request and not CBC: the fixed 1 ct absolute gap sits below the relaxation slack the
c_minformulation produces whenever a vehicle's minimum power is a sizeable share of a slot. On this request the slack is nine times the gap. Options:c_min: a cover cut on the number of active slots the goal energy needs,Σ z ≥ ⌈E / (c_max·dt)⌉, or the leftover below one slot forced onto a second active slot.Optimalwhen the cost stage stopped on its gap and the remaining bound slack is logged, which is whatcost_stage_gapalready carries.The probe getting the cost stage's gap at all is a separate, smaller point, see above.
andig commentedon Oct 4, 2026
Tried all three options under the production limits, 10 s and one thread, on this request (221 steps), its 414 step sibling, the #170 request and the one phase variant the README uses.
1. Absolute gap scaled with the request.
gap = max(gap_abs, share × goal energy × mean import price), on the probe and the cost stage alike. Goal energy is what the vehicles still need for theirs_goal.Today both three phase requests split and come back
Feasibleafter 7.9 s, #170 after 5.4 s.2. Cover cut
Σ z ≥ ⌈E / (c_max·dt)⌉on the slots before a goal. No effect on this request, still a 7.7 s split: the relaxation meets a count of 13 with 13 fractional slivers, each only has to carryc_min·z, so the bound does not move. It also makes the unreachable goal case infeasible (the goal is soft throughs_goal_pen, the cut is hard) and produced an off-model result on #170. Dropped.3.
Optimalwhen the cost stage stopped within a gap of its bound. Changes the label and nothing else: same 7.7 s, same schedule. The remaining bound slack on this request is 1.2 ct, on the 414 step one 4.8 ct.Recommendation: option 1 with a share of 0.25 % and the 1 ct floor. It is the only one that shortens the solve, 7.9 s to 1.5 s here, it decides the tie on the joint path so the strategies get their proven answer, and it gives up a tenth of a cent on this request where a fixed 1 ct gap cannot be closed at all. A request without a goal keeps the 1 ct floor and behaves as today. Option 3 can ride along for the splits that remain, it costs nothing, but it should not be the fix.