#define FLEET
using System;
using System.IO;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace PHEMlightdll
{
public class Start
{
private List<string> _DataPath;
private CEPHandler DataInput;
public Helpers Helper = new Helpers();
//******************* Parameters of Array or Single calculation *******************
//********************************* INPUT ******************************************
//*** DATATYP | UNIT | VARIBLE | Description ***
//List<string> | [-] | DataFiles (VEH, FC, EMI)| Name of file (e.g. "PC_D_EU4" path neede if not in "Default Vehicles") or aggregated name (PC, HDV, BUS, TW) by FleetMix calculation
//List<double> / double | [s] | Time | Time signal
//List<double> / double | [m/s] | Velocity | Velocity signal
//double | [m/s^2]| acc | Acceleration (ONLY NEDDED BY SINGLE CALCULATION)
//List<double> / double | [%] | Gradient | Gradient of the route
//out List<VehicleResult> | [-] | VehicleResultsOrg | Returned result list
//bool | [-] | fleetMix = false | Optional parameter if fleetMix should be calculate
//string | [-] | CommentPref = "c" | Optional parameter for comment prefix
//********************************* OUPUT: VehicleResultsOrg **********************
//*** DATATYP | UNIT | VARIBLE | Description ***
//string | [-] | vehicle | Name of the vehicle
//string | [-] | cycle | Name of the cycle
//double | [s] | time | Time
//double | [m/s] | speed | Velocity
//double | [kW] | power | Calculated power at the engine (ICE for conventional and HEV vehicles, electric engine for BEVs) including engine inertia and auxiliaries; not limited for engine fullload and braking limitations
//double | [kW] | P_pos | Positive engine power limited with engine rated power
//double | [-] | pNormRated | Engine power normalised with rated engine power and limited with the power range (fullload and drag) as specified in the characteristic curve for fuel consumption
//double | [-] | pNormDrive | Engine power normalised with "P_drive" and limited with the power range (fullload and drag) as specified in the characteristic curve for emissions
//double | [m/s^2]| acc | Caclulated/given acceleration
//Dictionary<string, double>| [*] | Emissiondata | Calculated emissions for all components which are defined in the emission curves. Unit dependent of emission component
#region calculate
//Calculate data from array
public bool CALC_Array(List<string> DataFiles,
List<double> Time,
List<double> Velocity,
List<double> Gradient,
out List<VehicleResult> VehicleResultsOrg,
bool fleetMix = false,
Correction DataCor = null,
string CommentPref = "c")
{
//Declaration
int i;
double acc;
List<VehicleResult> _VehicleResult = new List<VehicleResult>();
//Initialisation
Helper.ErrMsg = null;
//Borrow
Helper.CommentPrefix = CommentPref;
_DataPath = new List<string>();
//Set path by normal calculation (on given) and set path by fleetmix (on Default Vehicles) calculation
for (i = 0; i < DataFiles.Count; i++)
{
if ((DataFiles[i].LastIndexOf(@"\")) >= 0)
{
_DataPath.Add(DataFiles[i]);
}
else
{
//_DataPath.Add(Assembly.GetExecutingAssembly().Location.Substring(0, Assembly.GetExecutingAssembly().Location.LastIndexOf(@"\")) + @"\Default Vehicles\" + Helper.PHEMDataV);
_DataPath.Add(DataFiles[i + 1].Substring(0, DataFiles[i + 1].LastIndexOf(@"\")));
_DataPath.Add(DataFiles[i + 1].Substring(0, DataFiles[i + 1].LastIndexOf(@"\")));
_DataPath.Add(DataFiles[i + 1].Substring(0, DataFiles[i + 1].LastIndexOf(@"\")));
i += 1;
}
}
//Read the vehicle and emission data
#if FLEET
if (fleetMix)
{
//Set the vehicle class
Helper.gClass = _DataPath[0];
//Generate the class
DataInput = new CEPHandler();
//Read the FleetShares
if (!DataInput.ReadFleetShares(DataFiles[1], Helper))
{
VehicleResultsOrg = null;
return false;
}
//Read the vehicle and emission data
if (!DataInput.GetFleetCEP(_DataPath, DataFiles[0], Helper, DataCor))
{
VehicleResultsOrg = null;
return false;
}
}
else
#endif
{
//Get vehicle string
if (!Helper.setclass(DataFiles[0]))
{
VehicleResultsOrg = null;
return false;
}
//Generate the class
DataInput = new CEPHandler();
//Read the vehicle and emission data
if (!DataInput.GetCEP(_DataPath, Helper, DataCor))
{
VehicleResultsOrg = null;
return false;
}
}
//Calculate emissions per second
for (i = 1; i <= Time.Count - 1; i++)
{
//Calculate the acceleration
acc = (Velocity[i] - Velocity[i - 1]) / (Time[i] - Time[i - 1]);
//Calculate and save the data in the List
_VehicleResult.Add(PHEMLight.CreateVehicleStateData(Helper,
DataInput.CEPS[Helper.gClass],
Time[i - 1],
Velocity[i - 1],
acc,
Gradient[i - 1]));
if (Helper.ErrMsg != null)
{
VehicleResultsOrg = null;
return false;
}
}
VehicleResultsOrg = _VehicleResult;
return true;
}
//Calculate single data
public bool CALC_Single(List<string> DataFiles,
double Time,
double Velocity,
double acc,
double Gradient,
out List<VehicleResult> VehicleResultsOrg,
bool fleetMix = false,
Correction DataCor = null,
string CommentPref = "c")
{
//Declaration
List<VehicleResult> _VehicleResult = new List<VehicleResult>();
VehicleResultsOrg = _VehicleResult;
//Borrow
Helper.CommentPrefix = CommentPref;
_DataPath = new List<string>();
//Set path by normal calculation (on given) and set path by fleetmix (on Fleetshare file) calculation
for (int i = 0; i < DataFiles.Count; i++)
{
if ((DataFiles[i].LastIndexOf(@"\")) >= 0)
{
_DataPath.Add(DataFiles[i]);
}
else
{
//_DataPath.Add(Assembly.GetExecutingAssembly().Location.Substring(0, Assembly.GetExecutingAssembly().Location.LastIndexOf(@"\")) + @"\Default Vehicles\" + Helper.PHEMDataV);
_DataPath.Add(DataFiles[i + 1].Substring(0, DataFiles[i + 1].LastIndexOf(@"\")));
_DataPath.Add(DataFiles[i + 1].Substring(0, DataFiles[i + 1].LastIndexOf(@"\")));
_DataPath.Add(DataFiles[i + 1].Substring(0, DataFiles[i + 1].LastIndexOf(@"\")));
i += 1;
}
}
//Read the vehicle and emission data
#if FLEET
if (fleetMix)
{
//Set the vehicle class
Helper.gClass = "AggClass_" + DataFiles[0];
//Generate the class
DataInput = new CEPHandler();
//Read the FleetShares
if (!DataInput.ReadFleetShares(DataFiles[1], Helper))
{
VehicleResultsOrg = null;
return false;
}
//Read the vehicle and emission data
if (!DataInput.GetFleetCEP(_DataPath, DataFiles[0], Helper, DataCor))
{
VehicleResultsOrg = null;
return false;
}
}
else
#endif
{
//Get vehicle string
if (!Helper.setclass(DataFiles[0]))
{
VehicleResultsOrg = null;
return false;
}
//Generate the class
DataInput = new CEPHandler();
//Read the vehicle and emission data
if (!DataInput.GetCEP(_DataPath, Helper, DataCor))
{
VehicleResultsOrg = null;
return false;
}
}
//Calculate and save the data in the List
_VehicleResult.Add(PHEMLight.CreateVehicleStateData(Helper,
DataInput.CEPS[Helper.gClass],
Time,
Velocity,
acc,
Gradient));
VehicleResultsOrg = _VehicleResult;
return true;
}
#endregion
#region ExportData
private Dictionary<string, cErgEntry> ErgEntries = new Dictionary<string, cErgEntry>();
private List<string> ErgEntryList = new List<string>(); //Needed because Dictionary is not sorted
//Add to ERG file
private void AddToErg(string IDstring, string Head, string Unit)
{
if (!ErgEntries.ContainsKey(IDstring))
{
ErgEntries.Add(IDstring, new cErgEntry(Head, Unit));
ErgEntryList.Add(IDstring);
}
}
//Create result head
private string ErgHead()
{
//Declaration
StringBuilder s = new StringBuilder();
string key = null;
bool First = true;
foreach (string key_loopVariable in ErgEntryList)
{
key = key_loopVariable;
if (!First)
s.Append(",");
s.Append(ErgEntries[key].Head);
First = false;
}
//Return value
return s.ToString();
}
//Create result units
private string ErgUnits()
{
//Declaration
StringBuilder s = new StringBuilder();
bool First = true;
string key = null;
foreach (string key_loopVariable in ErgEntryList)
{
key = key_loopVariable;
if (!First)
s.Append(",");
s.Append(ErgEntries[key].Unit);
First = false;
}
//Return value
return s.ToString();
}
//Output sequence for the emissions
private void OutSeq(VehicleResult _VehicleResult, bool STA = false, bool add = false)
{
string Unit = "/km";
List<string> OutSeqStr = new List<string> { "FC", "FC_EL", "CO2", "NOX", "CO", "HC", "PM", "PN" };
if (STA) Unit = "/h";
if (!add)
{
//Clear the result arrays
ErgEntries.Clear();
ErgEntryList.Clear();
AddToErg("FC", "FC", "[g" + Unit + "]");
AddToErg("FC_EL", "Engine Power", "[kWh" + Unit + "]");
AddToErg("CO2", "CO2", "[g" + Unit + "]");
AddToErg("NOX", "NOx", "[g" + Unit + "]");
AddToErg("CO", "CO", "[g" + Unit + "]");
AddToErg("HC", "HC", "[g" + Unit + "]");
AddToErg("PM", "PM", "[g" + Unit + "]");
AddToErg("PN", "PN", "[#" + Unit + "]");
foreach (string id in _VehicleResult.EmissionData.Emi.Keys)
{
if (!OutSeqStr.Contains(id)) AddToErg(id.ToUpper(), id, "[g" + Unit + "]");
}
}
else
{
foreach (string id in _VehicleResult.EmissionData.Emi.Keys)
{
if (!ErgEntries.ContainsKey(id.ToUpper())) AddToErg(id.ToUpper(), id, "[g" + Unit + "]");
}
}
}
//Export the data
public bool ExportData(string path, List<VehicleResult> _VehicleResult)
{
if (path == null || _VehicleResult == null || _VehicleResult.Count == 0) return false;
//Write head
StringBuilder allLines = new StringBuilder();
string lineEnding = "\r\n";
//Produce emission output sequence. Only first one needed because should be always the same for a single vehicle
OutSeq(_VehicleResult[0], true);
//Vehicle type
allLines.AppendLine("Vehicletype: ," + _VehicleResult[0].Vehicle);
//Header and unit
allLines.AppendLine("Time, Speed, Gradient, Accelaration, Engine power raw, P_pos, P_norm_rated, P_norm_drive," + ErgHead());
allLines.AppendLine("[s], [m/s], [%], [m/s^2], [kW], [kW], [-], [-]," + ErgUnits());
//Write data
foreach (VehicleResult Result in _VehicleResult)
{
allLines.Append(Result.Time.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Speed.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Grad.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Accelaration.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Power.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.PPos.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.PNormRated.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.PNormDrive.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
foreach (string id in ErgEntryList)
{
if (Result.EmissionData.Emi.ContainsKey(id))
allLines.Append(Result.EmissionData.Emi[id].ToString("0.0000", CultureInfo.InvariantCulture) + ",");
else
allLines.Append("-,");
}
allLines.Append(lineEnding);
}
// Write the string to a file.
if (path.IndexOf(".", 0) < 0)
{
path = path + ".sta";
}
try
{
StreamWriter file = new StreamWriter(path);
file.WriteLine(allLines);
file.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
//Export summerized data
public bool ExportSumData(string path, List<VehicleResult> _VehicleResult)
{
if (path == null || _VehicleResult == null) return false;
StringBuilder allLines = new StringBuilder();
if (path.IndexOf(".", 0) < 0)
{
path = path + ".erg";
}
//Produce emission output sequence for all calculated vehicles
OutSeq(_VehicleResult[0], false);
foreach (VehicleResult Result in _VehicleResult)
{
OutSeq(Result, false, true);
}
if (!File.Exists(path))
{
//Write head
allLines.AppendLine("PHEMLight Results");
allLines.AppendLine("");
allLines.AppendLine("Vehicle, Cycle, Time, Speed, Gradient, Accelaration, Engine power raw, P_pos, P_norm_rated, P_norm_drive," + ErgHead());
allLines.AppendLine("[-], [-], [s], [km/h], [%], [m/s^2], [kW], [kW], [-], [-]," + ErgUnits());
}
//Write data
foreach (VehicleResult Result in _VehicleResult)
{
allLines.Append(Result.Vehicle + ",");
allLines.Append(Result.Cycle + ",");
allLines.Append(Result.Time.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Speed.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Grad.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Accelaration.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.Power.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.PPos.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.PNormRated.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
allLines.Append(Result.PNormDrive.ToString("0.0000", CultureInfo.InvariantCulture) + ",");
foreach (string id in ErgEntryList)
{
if (Result.EmissionData.Emi.ContainsKey(id))
allLines.Append(Result.EmissionData.Emi[id].ToString("0.0000", CultureInfo.InvariantCulture) + ",");
else
allLines.Append("-,");
}
allLines.Append("\r\n");
}
// Write the string to a file.
try
{
StreamWriter file = new StreamWriter(path, true);
file.WriteLine(allLines);
file.Close();
return true;
}
catch (Exception ex)
{
return false;
}
}
public VehicleResult GenerateSumData(List<VehicleResult> _VehicleResult)
{
//Declaration
string vehicle = "";
string cycle = "";
double sum_time = 0;
double sum_speed = 0;
double sum_grad = 0;
double sum_power = 0;
double sum_pPos = 0;
double sum_pNormRated = 0;
double sum_pNormDrive = 0;
double sum_acc = 0;
Dictionary<string, double> sum_Emi = new Dictionary<string, double>();
if (_VehicleResult == null || _VehicleResult.Count == 0) return new VehicleResult("", "", 0, 0, 0, 0, 0, 0, 0, 0, sum_Emi);
//Vehicle and cycle are always the same here
vehicle = _VehicleResult[0].Vehicle;
cycle = _VehicleResult[0].Cycle;
//Write data
foreach (VehicleResult Result in _VehicleResult)
{
sum_speed += Result.Speed * 3.6;
sum_power += Result.Power;
if (Result.PPos > 0) { sum_pPos += Result.PPos; }
sum_grad += Result.Grad;
sum_pNormRated += Result.PNormRated;
sum_pNormDrive += Result.PNormDrive;
sum_acc += Result.Accelaration;
foreach(string id in Result.EmissionData.Emi.Keys)
{
if (sum_Emi.ContainsKey(id))
sum_Emi[id] += Result.EmissionData.Emi[id];
else
sum_Emi.Add(id, Result.EmissionData.Emi[id]);
}
}
//Build average
sum_time = _VehicleResult[_VehicleResult.Count - 1].Time - _VehicleResult[0].Time;
sum_power /= _VehicleResult.Count;
sum_pPos /= _VehicleResult.Count;
sum_grad /= _VehicleResult.Count;
sum_pNormRated /= _VehicleResult.Count;
sum_pNormDrive /= _VehicleResult.Count;
sum_acc /= _VehicleResult.Count;
if (sum_speed > 0)
{
foreach (string id in sum_Emi.Keys.ToList())
{
sum_Emi[id] /= sum_speed;
}
}
else
{
foreach (string id in sum_Emi.Keys)
{
sum_Emi[id] = 0;
}
}
sum_speed /= _VehicleResult.Count;
return new VehicleResult(vehicle,
cycle,
sum_time,
sum_speed,
sum_grad,
sum_power,
sum_pPos,
sum_pNormRated,
sum_pNormDrive,
sum_acc,
sum_Emi);
}
#endregion
}
//Calculation
class PHEMLight
{
#region CreateVehicleStateData
static public VehicleResult CreateVehicleStateData(Helpers Helper,
CEP currCep,
double time,
double inputSpeed,
double inputAcc,
double Gradient = 0,
Correction DataCor = null)
{
//Declaration
double speed = Math.Max(inputSpeed, 0);
double acc;
double P_pos;
//Speed/Acceleration limitation
if (speed == 0)
acc = 0;
else
acc = Math.Min(inputAcc, currCep.GetMaxAccel(speed, Gradient, (Helper.pClass == Constants.strBEV | Helper.uClass == Constants.strHybrid)));
//Calculate the power
double power = currCep.CalcPower(speed, acc, Gradient, (Helper.pClass == Constants.strBEV | Helper.uClass == Constants.strHybrid));
double P_eng = currCep.CalcEngPower(power);
double Pwheel = 0;
if (Helper.uClass == Constants.strHybrid) Pwheel = currCep.CalcWheelPower(speed, acc, Gradient);
//Power limitation
if (P_eng >= 0)
P_pos = power;
else
P_pos = 0;
//Calculate the result values (BEV)
if (Helper.pClass == Constants.strBEV)
{
return new VehicleResult(Helper.gClass,
"",
time,
speed,
Gradient,
P_eng,
P_pos,
P_eng / currCep.RatedPower,
P_eng / currCep.DrivingPower,
acc,
currCep.GetAllEmission(P_eng, speed, Helper));
}
//Calculate the decel costing
double decelCoast = currCep.GetDecelCoast(speed, acc, Gradient);
//Calculate the result values (Zero emissions by costing, Idling emissions by v <= 0.5m / s²)
if (acc >= decelCoast || speed <= Constants.ZERO_SPEED_ACCURACY)
{
if (Helper.uClass == Constants.strHybrid)
return new VehicleResult(Helper.gClass,
"",
time,
speed,
Gradient,
P_eng,
P_pos,
P_eng / currCep.RatedPower,
P_eng / currCep.DrivingPower,
acc,
currCep.GetAllEmission(Pwheel, speed, Helper));
else
return new VehicleResult(Helper.gClass,
"",
time,
speed,
Gradient,
P_eng,
P_pos,
P_eng / currCep.RatedPower,
P_eng / currCep.DrivingPower,
acc,
currCep.GetAllEmission(P_eng, speed, Helper));
}
else
{
if (Helper.uClass == Constants.strHybrid)
return new VehicleResult(Helper.gClass,
"",
time,
speed,
Gradient,
P_eng,
P_pos,
P_eng / currCep.RatedPower,
P_eng / currCep.DrivingPower,
acc,
currCep.GetAllEmission(Pwheel, speed, Helper, true));
else
return new VehicleResult(Helper.gClass,
"",
time,
speed,
Gradient,
P_eng,
P_pos,
P_eng / currCep.RatedPower,
P_eng / currCep.DrivingPower,
acc,
currCep.GetAllEmission(P_eng, speed, Helper, true));
}
}
#endregion
}
}