/*
CanZE
Take a closer look at your ZE car
Copyright (C) 2015 - The CanZE Team
http://canze.fisch.lu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or any
later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lu.fisch.canze.actors;
/**
* Battery
*
* This class represents a mathematical representation of the battery and it's charging behavior.
* Please note that this class has no relation with the real battery in the car. It is ONLY used
* for prediction and it's SOC, or any parameter, or any calculation is an approximation. For non
* predictive functions, always use the paramters as they are supplied by the car's LBC.
*
*/
public class Battery {
/*
* Meta code on usage
*
* Battery battery;
* // initialize the values. No need to initialize dcPower
* battery = new Battery();
* battery.setTemperature (...);
* battery.setStateOfCharge (...);
* battery.setChargerPower (...);
* for (int t=1; t<=60; t++) {
* draw (battery, t); // imaginary method that plots SOC, range, time
* battery.iterateCharging (60);
* }
*
* Some rough parameters derived from this document: http://www.cse.anl.gov/us%2Dchina%2Dworkshop%2D2011/pdfs/batteries/LiFePO4%20battery%20performances%20testing%20for%20BMS.pdf
*
* Still to implement
* - state of health
* - discharge behavior
*
*/
private double temperature = 10.0;
private double stateOfCharge = 11.0; // watch it: in kWh!!!
private double chargerPower = 11.0; // in kW
private double capacity = 22.0; // in kWh
private double maxDcPower = 0; // in kW. This excludes the max imposed by the external charger
private double dcPower = 0; // in kW This includes the max imposed by the external charger
private int secondsRunning = 0; // seconds in iteration, reset by setStateOfChargePerc
private double dcPowerUpperLimit = 40.0; // for R240/R90 use 20
private double dcPowerLowerLimit = 2.0; // for R240/R90 use 1
private double rawCapacity = 22; // R90/Q90 use 41
private void predictMaxDcPower () {
// if the state of charge (in kW) exceeds the capacity of the battery
if (stateOfCharge >= capacity) {
// stop charging
maxDcPower = 0.0;
// if there is capacity left to charge
} else {
// calculate the SOC in percantage
double stateOfChargePercentage = stateOfCharge * 100.0 / capacity;
// get a rounded temperature
int intTemperature = (int )temperature;
// now use a model to calculate the DC power, based on SOC and temperature
maxDcPower = 19.0 + (3.6 * intTemperature) - (0.026 * stateOfChargePercentage * intTemperature) - (0.34 * stateOfChargePercentage);
//maxDcPower = 27.1 + (0.76 * intTemperature) - (0.27 * stateOfChargePercentage);
if (maxDcPower > dcPowerUpperLimit) {
maxDcPower = dcPowerUpperLimit;
} else if (maxDcPower < dcPowerLowerLimit) {
maxDcPower = dcPowerLowerLimit;
}
}
}
private void predictDcPower() {
// calculate what the battery can take
predictMaxDcPower();
// predict the efficiency of the charger (assuming it will run at the cpacity the battery can take)
double efficiency = 0.80 + maxDcPower * 0.00375;
// predict what is needed on the AC side to give thabattery what it can take
double requestedAcPower = maxDcPower / efficiency;
// if this is more than what the charger can deliver
if (requestedAcPower > chargerPower) {
// recalculate the efficiency based on the maximum the charger can deliver
efficiency = 0.80 + chargerPower * 0.00375;
// DC is maximum AC corrected for efficiency
dcPower = chargerPower * efficiency;
// if this is less than the charger can delever
} else {
// DC is what the battery can take
dcPower = maxDcPower;
}
}
/*
* iteration is in effect numerical integration of the power function to the SOC, respecting temperature and energy efficiency effects
*/
public void iterateCharging (int seconds) {
secondsRunning += seconds;
predictDcPower ();
setTemperature (temperature + (seconds * dcPower / 7200)); // assume one degree per 40 kW per 3 minutes (180 seconds)
setStateOfChargeKw (stateOfCharge + (dcPower * seconds * 0.95) / 3600); // 1kW adds 95% of 1kWh in 60 minutes
}
/*
* Getters and setters
*/
public double getTemperature() {
return temperature;
}
public void setTemperature(double temperature) {
this.temperature = temperature;
setRawCapacity(getRawCapacity());
}
public double getStateOfChargeKw() {
return stateOfCharge;
}
public void setStateOfChargeKw(double stateOfCharge) {
this.stateOfCharge = stateOfCharge;
if (this.stateOfCharge > this.capacity) this.stateOfCharge = this.capacity;
}
public double getStateOfChargePerc() {
return stateOfCharge * 100 / this.capacity;
}
public void setStateOfChargePerc(double stateOfCharge) {
setStateOfChargeKw(stateOfCharge * this.capacity / 100);
}
public double getChargerPower() {
return chargerPower;
}
public void setChargerPower(double chargerPower) {
this.chargerPower = chargerPower;
if (this.chargerPower > 43.0) {
this.chargerPower = 43.0;
} else if (this.chargerPower < 1.84) {
this.chargerPower = 1.84;
}
}
public double getMaxDcPower() {
return maxDcPower;
}
public double getDcPower() {
predictDcPower ();
return dcPower;
}
public int getTimeRunning () { return secondsRunning; }
public void setTimeRunning (int secondsRunning) {
this.secondsRunning = secondsRunning;
}
public double getDcPowerUpperLimit() {
return dcPowerUpperLimit;
}
public void setDcPowerUpperLimit(double dcPowerUpperLimit) {
this.dcPowerUpperLimit = dcPowerUpperLimit;
}
public double getDcPowerLowerLimit() {
return dcPowerLowerLimit;
}
public void setDcPowerLowerLimit(double dcPowerLowerLimit) {
this.dcPowerLowerLimit = dcPowerLowerLimit;
}
public double getRawCapacity() {
return rawCapacity;
}
public void setRawCapacity(double rawCapacity) {
this.rawCapacity = rawCapacity;
// adjust for capacity loss due to temperature differences (system wide)
if (temperature > 15.0) {
capacity = rawCapacity;
} else if (temperature > 0) {
capacity = 0.9 * rawCapacity + temperature * 2.2 / 15.0;
} else {
capacity = 0.9 * rawCapacity + temperature * 4.4 / 15.0;
}
// ensure the SOC is refreshed. This is only relevant for a very full battery
setStateOfChargeKw(getStateOfChargeKw());
}
}