Welcome to Tesla Motors Club
Discuss Tesla's Model S, Model 3, Model X, Model Y, Cybertruck, Roadster and More.
Register

State of Charge Data over Time: what SoC is your battery mostly sitting at?

This site may earn commission on affiliate links.

MarcG

Active Member
Oct 29, 2014
4,307
6,250
San Francisco
While reading about battery degradation and best practices to maintain battery longevity, I got thinking about what State of Charge (SoC) my P85D's battery pack spends its time at and with what frequency at different SoCs.

So in an attempt to get a better idea of the distribution, I tried to get the last year's worth of data from VisibleTesla (which I have running 24/7 on a Mac Mini at home) and compiled the data in a couple of charts.

To preface the findings, here are a few points that may be skewing the data in one direction or another. Namely:

  • The VisibleTesla data was exported as is using the File -> Export -> All Data menu item
  • Each data point has several parameters, so I've deleted all but two: timestamp, and SoC (State of Charge) which is expressed in percentage values
  • While I'm driving, VisibleTesla (VT) takes samples about every 5 seconds
  • While idle and NOT sleeping, VT takes samples about ever 4 minutes
  • While idle and sleeping, VT takes samples about every 33 minutes
  • My VT is scheduled to let the car "sleep" at 10pm every weeknight (Sun-Thu) and 11:55pm on weekends (Fri-Sat)
  • My VT is scheduled to wake the car up at 6:30am every weekday (Mon-Fri) and 8am on weekends (Sat-Sun)
  • I have my car set to Energy Savings ON but Always Connected is checked
  • My normal charge limit is set to 90% and I charge it every other day
  • I've been averaging two 100% charges a month

So there will definitely be many more samples while the car is driving than when it's idle but awake, and a lot fewer when it's asleep.
With that out of the way, the two charts below represent the number of State of Charge data points collected for:

  • Every State of Charge possible (from 1% to 100%, in 1% increments) - second chart
  • 5% SoC blocks (1-5%, 6-10%, etc. until 96-100%) - first chart

Results: it looks like battery pack spends most of its time between 56% and 90%. There is a spike at 90% obviously, since this is my charge limit and I don't usually start driving immediately after it's done charging.

I read somewhere here that the battery likes it best between 50 to 65%, so it may be worth lowering my charge limit to 80% or even 70%.

Thoughts? Suggestions? What do your data points show?


5b.png



1i.png
 
Interesting. I wonder if SOC reflects % of "usable" battery, or % of total battery. IF it is "usable", then best to keep in mind that whatever is reserved at bottom end for anti-bricking is skewing the % number in terms of actual full battery capacity SOC %.
 
I'm going to throw this out there, but I'm also going to preface it by saying this is simply bad data. Without a lot of processing it's going to be skewed because of the car's sleep functions, missing data, more data samples prior to sleep than after, etc. - it's not time-normalized at all.

But here's the view from 350,000 rows (one with an automatic Y axis, one with a restricted Y axis):

Untitled.png
Untitled2.png
 
I'm going to throw this out there, but I'm also going to preface it by saying this is simply bad data. Without a lot of processing it's going to be skewed because of the car's sleep functions, missing data, more data samples prior to sleep than after, etc. - it's not time-normalized at all.

But here's the view from 350,000 rows (one with an automatic Y axis, one with a restricted Y axis):

View attachment 103060

Whoa that's quite a peak around 88 to 90%!! Do you normally charge to 90% overnight, every night, and let it sit there for a while?

FYI, my data is also skewed due to how often VT samples during driving events, and how infrequently it does when it allows the car to sleep.

Do you know of any neat tricks in Excel (or other tools) to get consistent samples at regular intervals (say 12 minutes)?
 
Whoa that's quite a peak around 88 to 90%!! Do you normally charge to 90% overnight, every night, and let it sit there for a while?

FYI, my data is also skewed due to how often VT samples during driving events, and how infrequently it does when it allows the car to sleep.

Do you know of any neat tricks in Excel (or other tools) to get consistent samples at regular intervals (say 12 minutes)?

When the car gets home, we plug it in and charge at 80A. So it quickly runs up to 90% again and stays there as long as the car is at home. Because I work primarily from my home office, unless my wife is out and about, the car is at 90%.

What you can do in Excel is to take your samples with the timestamps, and set up two more columns that are "conditional timestamp" and "conditional data". TeslaMS uses msec timestamps, so I set up a formula for the conditional TS that basically says "if data_timestamp[t] > conditionalTS[t-1] + 1000*60*12, then conditionalTS[t] = data_timestamp[t] else conditionalTS[t-1]. Then set up the next column that says "if conditionalTS[t] <> conditionalTS[t-1], then conditionalBattery[t] = data_battery[t]". Then, do your histogram based upon that data. The cell will be blank if the last timestamp was within 12 minutes of the last sampled information.

There will still be skew, as there are times where the data will have been missed. I was going to try and work with the data more this weekend if I could, to try and eliminate as much as I could, but I recognize that it won't be perfect.
 
When the car gets home, we plug it in and charge at 80A. So it quickly runs up to 90% again and stays there as long as the car is at home. Because I work primarily from my home office, unless my wife is out and about, the car is at 90%.

What you can do in Excel is to take your samples with the timestamps, and set up two more columns that are "conditional timestamp" and "conditional data". TeslaMS uses msec timestamps, so I set up a formula for the conditional TS that basically says "if data_timestamp[t] > conditionalTS[t-1] + 1000*60*12, then conditionalTS[t] = data_timestamp[t] else conditionalTS[t-1]. Then set up the next column that says "if conditionalTS[t] <> conditionalTS[t-1], then conditionalBattery[t] = data_battery[t]". Then, do your histogram based upon that data. The cell will be blank if the last timestamp was within 12 minutes of the last sampled information.

There will still be skew, as there are times where the data will have been missed. I was going to try and work with the data more this weekend if I could, to try and eliminate as much as I could, but I recognize that it won't be perfect.


Great info, thanks a lot! I'll play with my data when I get a chance and see if results are different.
 
Results: it looks like battery pack spends most of its time between 56% and 90%. There is a spike at 90% obviously, since this is my charge limit and I don't usually start driving immediately after it's done charging.

I read somewhere here that the battery likes it best between 50 to 65%, so it may be worth lowering my charge limit to 80% or even 70%.

Thoughts? Suggestions? What do your data points show?

Everything I've read here suggests that charging to 90% is fine. My understanding is that the real negatives from high and low SOCs are at closer to 100% and 0% (which the pack doesn't allow anyway) and that the difference between 80% and 90%, for example, is far less significant than, say, the difference between 98 and 100. (I'm making those numbers up, but that's the gist of it.)

The potential negative of charging to 80% instead of 90% is that you may see more rated range loss over time, until you manage to get the pack to recalibrate or rebalance or whatever black magic it performs at various times. So while that range loss wouldn't be due to any real pack degradation, you'd still temporarily be affected by the range loss.

That's my take on it, for what it's worth. We charge to 90% every night, with rare exception. For example, we were going to be away for a few days around Thanksgiving weekend. The Model S got home on Tuesday evening, charged to around 55%. We were going to be back on Sunday, and not driving the car again until Monday morning. So rather than let it charge to 90% Tuesday night, and sit at that SOC for several days for no reason, I did set the charge limit to 50%, so that it basically wouldn't charge at all while we were gone. When I came home on Sunday I set the charge limit back to 90%, so that the car would be fully charged for my wife on Monday morning.

I'm planning on sticking with 90%.

Edit: I have not had VT running as consistently as you, Marc, as I've had some issues with it, but I do attempt to run it 24 X 7. So just for kicks I output my data, and imported it into Excel. But I am very unfamiliar with using Excel beyond some very basic stuff, and have never used graphs. I have a column for timestamp and one for the SOC, which I have sorted, so all the same SOCs are now grouped together in increasing order of SOC. What would be the rest of the steps I need to take to create a simple graph? (If it's too complicated to explain easily, feel free to say so. I just thought I'd do this if it were simple enough.)
 
Last edited:
Great info, thanks a lot! I'll play with my data when I get a chance and see if results are different.

Ok, did some more crunching - still had the information open. :)

First two graphs are the sampled graphs in %, first one bounded at max 18% - second one at 6% to show smaller detail:
Untitled.png
Untitled2.png


And then I did some small skew tests... here's the skew of the data when limited to minimum 15 mins between samples:
skew.png

< 0 means %age skews to the left for that SOC (lower %age), > 0 means it skews to the right. Sampling with 15 mins between samples makes 90% show up about 2.5% higher (which makes sense, it eliminates a lot of driving samples) at the expense of a lot of samples < 90%. I haven't looked at the 89's yet, although I suspect that is from the period of time where Tesla's algorithm showed 3-4 miles more range when the HPWC was active compared to when inactive (i.e., immediately after charging stopped at 90%, the HPWC shut off then it dropped to 89%).
 
Last edited:
A few months ago I averaged out all sample point ever made by VisibleTesla. It came out to 64%. I drive a lot and only charge at night.

I could compute my average without additional assistance, but no fancy graph. My average, with some of the similar caveats Marc expressed with respect to how the samples were obtained, sleep, etc., is about 72.5%.
 
And one more chart, then I'm finished with this series: here's the 15-min grouped SOC ranges by 5%, X axis shows maximum of the range (e.g., "100" represents 96-100% SOC range).

grouped.png


- - - Updated - - -

Mean of all data (n=328910) = 81.76% SOC
Mean of samples with minimum 15-minute spacing (n=25982) = 82.44% SOC
 
Edit: I have not had VT running as consistently as you, Marc, as I've had some issues with it, but I do attempt to run it 24 X 7. So just for kicks I output my data, and imported it into Excel. But I am very unfamiliar with using Excel beyond some very basic stuff, and have never used graphs. I have a column for timestamp and one for the SOC, which I have sorted, so all the same SOCs are now grouped together in increasing order of SOC. What would be the rest of the steps I need to take to create a simple graph? (If it's too complicated to explain easily, feel free to say so. I just thought I'd do this if it were simple enough.)

It's fairly simple. Besides your two columns, create a second table with another two columns. In the first column, you'll have one row for each SoC (1 through 100). In the second column, you'll have the count (number of occurrences) of each SoC value.

Let's now assume that all the SoC values in your first table are in column B (say, the value "1" appears 639 times in 639 cells under column B, the value "2" appears 1327 times, etc.)
Also assume that the unique SoC values (1 through 100) in your second table are in column D (let's say that SoC value "1" is D1, value "2" is in D2, etc.)

The formula for each count is therefore as follows:

For SoC value "1":
=COUNTIF(B:B,INDIRECT(D1))

For SoC value "2":
=COUNTIF(B:B,INDIRECT(D2))

etc.

until value "100":
=COUNTIF(B:B,INDIRECT(D100))

So you'll end up with a table of two columns that has 100 rows with SoC values in column D and their number of occurrences in column E.

The final step is to create a column graph, which is very easy to do if you've ever created a graph in Excel. Select column graph, highlight column E, and choose column D for horizontal axis labels.

Good luck, and feel free to PM if you have additional questions!
 
After watching Prof. Jeff Dahn's presentation "Why do Li-ion batteries die? - and how to improve the situation?" a couple of times, I decided to avoid the 0 to 30 and 70 to 100 SoC ranges if conveniently possible.

This means that I will typically charge my car to 70% if I need to drive it the day after (I only have a 60 mile daily commute). I have charging scheduled at 4 am to not even have it sitting at 70% SoC for more than a couple of hours. When I don't need to drive the car, I let it sit at anything above 30% SoC (most of the time this will be around 40%).

I have entered each and every range charge in the Tesla Battery Survey sheet. Check out the chart for jpet to see the results. Because of my "special" charging habit, it "looks" like I have lost a lot of range compared to the other cars. But I'm confident this only looks this way because the BMS has great difficulty measuring the real capacity of my battery. I've never been below 8% SoC.

What I do whenever I need a range charge, is to have it balance the cells so that they regularly (about once a month) get calibrated to keep the pack as a whole in a healthy condition.
 
For SoC value "1":
=COUNTIF(B:B,INDIRECT(D1))

I don't think that INDIRECT is what you want, is it? INDIRECT gives you the value of the cell referenced by INDIRECT's target. INDIRECT would expect a cell reference in cell D1 (not a number). For example, let's say cell B1 contained "5000" and cell D1 contained "B1" - INDIRECT(D1) would return "5000" (looking up the value of the cell reference found in cell D1). In this case, if D1 contained "1", you'll get a REF! error.

Assuming the same columns - B1:B500000 contains 500,000 rows of data containing SOC numbers - e.g., "90", "89", "40", etc., and D1 through D101 contain the numbers "0" through "100" representing SOC's...

In column E1, I would place:
=COUNTIF(B:B,"="&D1)

and copy-paste that formula to each cell E2 through E101 (it'll adjust "D1" to "D2", "D3", etc.)
 
I don't think that INDIRECT is what you want, is it? INDIRECT gives you the value of the cell referenced by INDIRECT's target. INDIRECT would expect a cell reference in cell D1 (not a number). For example, let's say cell B1 contained "5000" and cell D1 contained "B1" - INDIRECT(D1) would return "5000" (looking up the value of the cell reference found in cell D1). In this case, if D1 contained "1", you'll get a REF! error.

Assuming the same columns - B1:B500000 contains 500,000 rows of data containing SOC numbers - e.g., "90", "89", "40", etc., and D1 through D101 contain the numbers "0" through "100" representing SOC's...

In column E1, I would place:
=COUNTIF(B:B,"="&D1)

and copy-paste that formula to each cell E2 through E101 (it'll adjust "D1" to "D2", "D3", etc.)

Hmm ok, but INDIRECT works for me:

Screen Shot 2015-12-03 at 3.11.58 PM.png


I did try your formula and it also works. More than one way to skin a cat, I guess!
 
Below is my graph. (Thanks for the help, Marc!)

While I have been below a 27% SOC, it's been rare, and apparently VT must not have been recording data at those times, which is why there is no data for SOCs below 27%.

SOC1percent increments.jpg


Edit: Looking at this, I'm guessing the peak at 71, and the high points around it may be due to that being the typical SOC when my wife parks at work. (She drives the car much more than I do.) The peak at 50 is likely what the car is left with when she arrives home. To work is downhill, from work is uphill, so using roughly 19% SOC to work, and 21% SOC from work would make sense.
 
Last edited: