diff --git a/include/18650_cell.map b/include/18650_cell.map new file mode 100644 index 0000000..2ae45fe --- /dev/null +++ b/include/18650_cell.map @@ -0,0 +1,290 @@ +/*** + -------------------------------------------------------------------------------------------- + | | + | NASA Glenn Research Center | + | 21000 Brookpark Rd | + | Cleveland, OH 44135 | + | | + | File Name: amprius_cell.map | + | Author(s): George Thomas and Brian Malone | + | Date(s): August 2021 | + | | + -------------------------------------------------------------------------------------------- +***/ + +// --------------------------------------------------------------- +// | Samsung silicon nanowire Li-ion battery cell map +// --------------------------------------------------------------- +// | Declaration of a new Subelement instance called S_map +// | that is of the type BatteryCellSoCTemperatureMap +// --------------------------------------------------------------- + +Subelement BatteryCellSoCTemperatureMap S_map { + + // Constant parameters specific to this battery cell + energyDes_cell = 13./1000.; // kW-h (3.6 V nominal * 3.0 Ah tested at C/5 discharge) + powerDes_cell = 3.005; //kW + QDes_cell = 3; //Ah + // ---------------------------------------------------------------------- + // | Declare a table (or function) named TB_Wp(real SPED, real PR) + // | To satisfy the TB_Wp socket requirement. + // ---------------------------------------------------------------------- + + // Voc vs Temperature and SOC data + Table TB_Voc(real T, real SOC){ + + T = 0.0 { + SOC = {-0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2} + Voc = { 2.92334783, 2.92334783,3.00653623,3.08972464,3.17291304,3.23989855,3.31010145, + 3.3803913 ,3.44033333,3.49033333,3.52169565,3.54391304,3.58695652, + 3.62095652,3.65437681,3.68604348,3.72430435,3.75531884,3.79102899, + 3.82030435,3.84181159,3.86124638,3.88921739,3.91686957,3.96223188, + 4.00169565,4.04117391,4.06849275,4.07573913,4.08571014,4.10571014, + 4.161,4.161} + } + T = 20.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + Voc = { 2.99293893,2.99293893,3.05400763,3.11507634,3.17614504,3.23506616,3.30371247, + 3.37521374,3.43605852,3.48697455,3.5200229 ,3.54251908,3.58374046, + 3.6329313 ,3.67379644,3.70287532,3.73784733,3.76526463,3.79174809, + 3.81922901,3.84108142,3.87212214,3.90738931,3.93615267,3.98113995, + 4.02093893,4.04504071,4.07114758,4.07583969,4.08371501,4.10560814, + 4.161,4.161 } + } + T = 30.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + Voc = { 2.84084639,2.84084639,2.98428484,3.1050295 ,3.19464496,3.25566531,3.309059 , + 3.37185148,3.43473652,3.49059613,3.51955239,3.541353 ,3.58558494, + 3.62641607,3.6708881 ,3.70814547,3.7392177 ,3.76822075,3.79592981, + 3.82260427,3.84986368,3.88146592,3.91739674,3.94798779,3.98188403, + 4.02274568,4.05623296,4.06830824,4.07468871,4.08175788,4.10853306, + 4.153,4.153} + } + T = 45.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + Voc = { 2.81925101,2.81925101,2.97410931,3.09861134,3.18674899,3.24142105,3.29678138, + 3.35963563,3.42195951,3.47637247,3.51383806,3.54319838,3.59076923, + 3.61940891,3.65574089,3.7067004 ,3.74153441,3.77023887,3.79773684, + 3.82421053,3.85139271,3.88311336,3.91906478,3.94918219,3.98310931, + 4.02401215,4.05611741,4.07036842,4.07774494,4.08190283,4.10867206, + 4.153,4.153} + } + T = 60.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + Voc = { 2.79852227,2.79852227,2.95540486,3.07335223,3.17051822,3.228 ,3.29222672, + 3.34192308,3.39768016,3.46263158,3.49862348,3.53679757,3.59007692, + 3.61315789,3.65250202,3.70067206,3.74025911,3.77023887,3.79773684, + 3.82335628,3.84432794,3.87774899,3.91851822,3.94918219,3.98310931, + 4.02401215,4.05611741,4.06846559,4.07574494,4.08185425,4.10867206, + 4.151,4.151} + } + + T.interp = "linear" ; + T.extrap = "none" ; + + SOC.interp = "linear" ; + SOC.extrap = "none" ; + + extrapIsError = 0; + printExtrap = 0; + + } // end TB_Voc + + // Static output resistance vs Temperature and SOC data + Table TB_R_0(real T, real SOC){ + + T = 0.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_0 = { 0.2473913 ,0.2473913 ,0.20681159,0.16623188,0.12565217,0.09753623,0.08362319, + 0.08 ,0.07666667,0.0715942 ,0.07 ,0.0415942 ,0.05681159, + 0.067 ,0.067 ,0.067 ,0.067 ,0.067 ,0.06537681, + 0.065 ,0.065 ,0.065 ,0.065 ,0.065 ,0.065 , + 0.065 ,0.065 ,0.065 ,0.065 ,0.065 ,0.065 , + 0.065 ,0.06 } + } + T = 20.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_0 = { 0.08801527,0.08801527,0.07274809,0.05748092,0.04221374,0.03231552,0.02722646, + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 } + } + T = 30.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_0 = { 0.0677823 ,0.0677823 ,0.05252289,0.03726348,0.02733469,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.01311292,0.01809766,0.02 ,0.02 ,0.02430824,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.03 ,0.03 } + } + + T = 45.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_0 = { 0.06546559,0.06546559,0.0502834 ,0.03510121,0.02663968,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.01218623,0.01 ,0.01 ,0.01890688,0.02451417,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.03 ,0.03 } + } + + T = 60.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_0 = { 0.06546559,0.06546559,0.0502834 ,0.03510121,0.02663968,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.02072874,0.02 ,0.02 ,0.02 ,0.02451417,0.025 , + 0.025 ,0.025 ,0.025 ,0.025 ,0.025 ,0.025 , + 0.03 ,0.03 } + } + + T.interp = "linear" ; + T.extrap = "none" ; + + SOC.interp = "linear" ; + SOC.extrap = "none" ; + + extrapIsError = 0; + printExtrap = 0; + + } // end TB_R_0 + + // Static output resistance vs Temperature and SOC data + Table TB_R_Th(real T, real SOC){ + + T = 0.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_Th = { 0.09 ,0.09 ,0.09 ,0.09 ,0.09 ,0.07130435,0.06 , + 0.06 ,0.06 ,0.06 ,0.06 ,0.06 ,0.06 , + 0.08217391,0.07492754,0.07 ,0.07 ,0.07 ,0.07 , + 0.07 ,0.05318841,0.04144928,0.0573913 ,0.06884058,0.07 , + 0.07456522,0.075 ,0.075 ,0.05586957,0.055 ,0.04021739, + 0.04 ,0.04 } + } + T = 20.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_Th = { 0.08534351,0.08534351,0.07516539,0.06498728,0.05480916,0.04838931,0.04589059, + 0.045 ,0.045 ,0.04195929,0.03937405,0.03642494,0.035 , + 0.035 ,0.03848601,0.05430025,0.04534351,0.03624682,0.03115776, + 0.03 ,0.03 ,0.03 ,0.03839695,0.04 ,0.04 , + 0.04 ,0.03089059,0.03 ,0.03 ,0.02807125,0.02505344, + 0.02 ,0.02 } + } + T = 30.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_Th = { 0.0677823 ,0.0677823 ,0.05252289,0.045 ,0.045 ,0.045 ,0.045 , + 0.04207528,0.04 ,0.03690234,0.035 ,0.0317294 ,0.02798576, + 0.027 ,0.025588 ,0.025 ,0.02129705,0.02 ,0.02 , + 0.04377416,0.04190234,0.04 ,0.04 ,0.04 ,0.03121058, + 0.02820753,0.028 ,0.02055341,0.02 ,0.02 ,0.02 , + 0.001 ,0.001} + } + T = 45.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_Th = { 0.06728745,0.06728745,0.04704453,0.04 ,0.04 ,0.04 ,0.04 , + 0.04 ,0.04 ,0.03267206,0.03 ,0.03 ,0.03 , + 0.03 ,0.02603239,0.025 ,0.02091093,0.02 ,0.02 , + 0.04562753,0.04133603,0.04 ,0.04 ,0.04 ,0.0308502 , + 0.02814575,0.028 ,0.02038866,0.02 ,0.02 ,0.02 , + 0.001 ,0.001 } + } + T = 60.0 { + SOC = { -0.2, 0., 0.03333333, 0.06666667, 0.1, 0.13333333, 0.16666667, + 0.2, 0.23333333, 0.26666667, 0.3, 0.33333333, 0.36666667, + 0.4, 0.43333333, 0.46666667, 0.5, 0.53333333, 0.56666667, + 0.6, 0.63333333, 0.66666667, 0.7, 0.73333333, 0.76666667, + 0.8, 0.83333333, 0.86666667, 0.9, 0.93333333, 0.96666667, + 1., 1.2 } + R_Th = { 0.06728745,0.06728745,0.04704453,0.04 ,0.04 ,0.04 ,0.04 , + 0.04 ,0.04 ,0.03267206,0.03 ,0.03 ,0.03 , + 0.03 ,0.02603239,0.025 ,0.02091093,0.02 ,0.02 , + 0.02 ,0.02433198,0.03378543,0.035 ,0.035 ,0.0304251 , + 0.02536437,0.025 ,0.02024291,0.02 ,0.02 ,0.02 , + 0.001 ,0.001 } + } + + T.interp = "linear" ; + T.extrap = "none" ; + + SOC.interp = "linear" ; + SOC.extrap = "none" ; + + extrapIsError = 0; + printExtrap = 0; + + } // end TB_R_Th + +} // end S_map (subelement) diff --git a/model/battery_test.mdl b/model/battery_test.mdl new file mode 100644 index 0000000..7c8aad2 --- /dev/null +++ b/model/battery_test.mdl @@ -0,0 +1,118 @@ +/*** + ------------------------------------------------------------------------------- + | | + | NASA Glenn Research Center | + | 21000 Brookpark Rd | + | Cleveland, OH 44135 | + | | + | File Name: SW13.mdl | + | Author(s): George Thomas, Brian Malone | + | Date(s): October 2020 | + | | + | Description: Battery test model with backflow | + | | + | | + ------------------------------------------------------------------------------- +***/ + +cout<<"======================================="< + + // Set up the thermal port and thermal mass submodel/socket + setOption("switchThermPort", "TRUE"); + Subelement EThermalMass S_eThermMass { + Cp = 0.95; // J/(kg°C)= (°C)/(1.8∗°R)∗kg/2.205lb∗0.000948BTU/J=0.000239∗ J/(kg°C) -> Simulink: 1020*2*0.000239 = 0.48756, or ~0.5 + //Cp = 1020.; // J/(kg°C)= (°C)/(1.8∗°R)∗kg/2.205lb∗0.000948BTU/J=0.000239∗ J/(kg°C) -> Simulink: 1020*2*0.000239 = 0.48756, or ~0.5 + + } + + // Vout_des = 524; + Vout_des = 528.5; + Vout_guess = 520; + SpecificEnergy = 2; // Wh/kg + +} + +Element Cable Cable_Single +{ + R = 0.001; //Ohms + L = 0; //Henries +} + + +Element ConstantPowerLoad CLoad +{ + Pdemand = 120.; // kW (single cell 1 C current is 3.5 Amps, roughly 14 W) + eff = 0.999; +} + +// Create a magic thermal output port that represents the heat extraction +// from the battery. +Element TMS{ + ThermalOutputPort Q_O; + // Assume cold side of TMS is ISA temperature + //real T_cool = 518.67, Q_cool; + real T_cool = 500, Q_cool; + real R_cool = 500.; // cooling heat transfer "admittance" to ambient. + + void calculate() { + Q_cool = R_cool*(Q_O.MassTemp-T_cool); + Q_O.HeatTransferRate = Q_cool; + } +} +linkPorts("TMS.Q_O","Li_ion_battery.Q_I","Q1"); + +//------------------------------------------------------------------------------------------------- +// Component Linkages +//------------------------------------------------------------------------------------------------- +linkPortI( "Li_ion_battery.EP_O", "Cable_Single.EP_I" ); +linkPortI( "Cable_Single.EP_O", "CLoad.EP_I" ); +// linkPortI( "CableLeft.EP_O", "DeltaDC.EP_I" ); +// linkPortI( "DeltaDC.EP_O", "CableRight.EP_I" ); +// linkPortI( "CableRight.EP_O", "CLoad.EP_I" ); + +findSourcesAndPropagate(); + +solverSequence = { "Cable_Single", "Li_ion_battery", "CLoad", "TMS" }; + +void BFPrintOutput() +{ + cout << " Case = " << CASE << endl; + cout << " Battery open circuit voltage = " << Li_ion_battery.Voc << endl; + cout << " Battery output voltage (actual/guess) = " << Li_ion_battery.Vout << "/" << Li_ion_battery.Vout_guess << endl; + cout << " Battery heat balance (Qin/Oout) = " << Li_ion_battery.Q_heat << "/" << Li_ion_battery.Q_I.HeatTransferRate << endl; + // cout << " Battery output Resistance (R_0, R_Th) = " << Li_ion_battery.R_0 << ", " << Li_ion_battery.R_Th << endl; + // cout << " Battery current (calc) = " << (Li_ion_battery.Voc-Li_ion_battery.Vout)/Li_ion_battery.RTotal << endl; + cout << " Battery current/power = " << Li_ion_battery.EP_O.I.r << "/" << Li_ion_battery.EP_O.S.r << endl; + cout << " Load power = " << CLoad.P << endl; + cout << " Number of series cells in battery = " << Li_ion_battery.S_map.num_series << endl; + cout << " Number of parallel cells in battery = " << Li_ion_battery.S_map.num_parallel << endl; + cout << " Battery temperature/temp state (oC) = " << Li_ion_battery.T_batt << "/" << (Li_ion_battery.S_eThermMass.T - 491.67)*5./9. << endl; + // cout << " Cable current (output port) = " << Cable_Single.EP_O.I.r << endl; + // cout << " Load input voltage = " << CLoad.EP_I.V.r << endl; + // cout << " Load input power = " << CLoad.EP_I.S.r << endl; + // cout << " Calculated load eff = " << (CLoad.P/CLoad.EP_I.S.r)**sign(CLoad.P) << endl; // GLT: account for negative power flow + // cout << " Load eff = " << CLoad.eff << endl; +} + +// Initial independent variable guesses are here. +CLoad.Vreal = 519.9; //BPM was 3.59 +CLoad.Vimag = 0; + diff --git a/run/battery_test.run b/run/battery_test.run new file mode 100644 index 0000000..6e6a57c --- /dev/null +++ b/run/battery_test.run @@ -0,0 +1,177 @@ +/******************************************************************************* +© Copyright 2003. The U.S. Government, as Represented by the Administrator of +the National Aeronautics and Space Administration (NASA). All rights reserved. +Includes content licensed from the U.S. Government, National Aeronautics and +Space Administration under United States Copyright Registration Numbers +V3503D364 and V3482D344. +© 2008-2015 NPSS® Consortium, www.NPSSConsortium.org/AllRightsReserved +*******************************************************************************/ + +/******************************************************************************* +NPSS® software and related documentation is export controlled with an Export +Control Classification Number(ECCN) of 9D991, controlled for Anti-Terrorism +reasons, under U.S. Export Administration Regulations 15 CFR 730-774. It may +not be transferred to a country checked under anti-terrorism on the Commerce +Country Chart structure or to foreign nationals of those countries in the U.S. +or abroad without first obtaining a license from the Bureau of Industry and +Security, United States Department of Commerce. Violations are punishable by +fine, imprisonment, or both. +*******************************************************************************/ + +// Make GasTbl the default thermopackage +// #ifndef THERMO +// #define THERMO GasTbl +// #endif + +//include the power train components +//#include "PTE_Viewer.view" +#include "npssel.view" +#include "npssel_runtime.view" + +// add in electric port +#include "ElectricPort.prt" +#include "InterpretedPort.int" + +MODELNAME = "battery_test"; + +//model file +#include "model/battery_test.mdl" + +/************************ +On design +************************/ +setOption("switchDes","DESIGN"); +autoSolverSetup(); +//solver.solutionMode = "ONE_PASS"; +//solver.switchUBC = "ON"; + +solver.maxIterations=5000; +solver.maxJacobians=5000; +solver.defaultPerturbation = 0.0001; // tell the solver not to perturb so far when calcing jacobian. default: 0.001 +//solver.defaultDxLimit = 0.03; +cout << endl << solver.dependentNames << endl << solver.independentNames << endl; + +// Constant power load 14 W for Case 0 and 1 +run(); +BFPrintOutput(); +page.display(); // output data to out file + +/************************ +Off design +************************/ + +setOption("switchDes","OFFDESIGN"); +autoSolverSetup(); + +CASE++; run(); +BFPrintOutput(); +page.display(); + +CLoad.Pdemand = -120.; // kw Charge the battery roughly at 1 C (14 W) + +// Constant power load -14 W for Case 2 +CASE++; run(); +BFPrintOutput(); +page.display(); + +// Setup a transient run to match JCC's Simulink data +CLoad.Pdemand = 10.; // initial power point in profile +run(); // do another steady-state run just to initialize us. + +// BTW the Simulink model has NO cooling, so to represent that, just zero out the heat transfer resistance +TMS.R_cool = 0; + +// Also start out the temperature at 22 C to match the data +Li_ion_battery.T_batt = 22; +Li_ion_battery.S_eThermMass.T = 22. * 9./5. + 491.67; // 22 C converted to R + +// Turn on integrator for battery SOC +Li_ion_battery.ind_SOC.autoSetup = TRUE; +Li_ion_battery.integ_SOC.autoSetup = TRUE; + +setOption("switchDes","OFFDESIGN"); +setOption("solutionMode", "TRANSIENT"); +transient.stopTime = 6560.0; +transient.baseTimeStep = 0.1; +transient.setup(); +initializeHistory(); +autoSolverSetup(); + +solver.defaultPerturbation = 0.000005; +solver.defaultDxLimit = 0.001; +//solver.solutionMode = "ONE_PASS"; + +cout << "Transient Solver Vars = " << endl << solver.dependentNames << endl << solver.independentNames << endl; + +// Transient data out to a CSV file for plotting +OutFileStream transientCSVStream { filename = "output/batt_transient.csv"; } + +// Write the CSV headers +transientCSVStream << "Li_ion_battery.Voc,"; +transientCSVStream << "Li_ion_battery.Vout,"; +transientCSVStream << "Li_ion_battery.Q_heat,"; +transientCSVStream << "Li_ion_battery.R_0,"; +transientCSVStream << "Li_ion_battery.R_Th,"; +transientCSVStream << "Li_ion_battery.EP_O.I.r,"; +transientCSVStream << "Li_ion_battery.EP_O.S.r,"; +transientCSVStream << "Li_ion_battery.T_batt,"; +transientCSVStream << "Li_ion_battery.S_eThermMass.T,"; +transientCSVStream << "Li_ion_battery.SOC,"; +transientCSVStream << "Li_ion_battery.dSOCqdt,"; +transientCSVStream << "Li_ion_battery.C_rate,"; +transientCSVStream << "time" << endl; + +// Use a variable to print only every 100 timesteps. +real time_old = 0; +void csvTransientPrint() +{ + transientCSVStream << Li_ion_battery.Voc << ", "; + transientCSVStream << Li_ion_battery.Vout << ", "; + transientCSVStream << Li_ion_battery.Q_heat << ", "; + transientCSVStream << Li_ion_battery.R_0 << ", "; + transientCSVStream << Li_ion_battery.R_Th << ", "; + transientCSVStream << Li_ion_battery.EP_O.I.r << ", "; + transientCSVStream << Li_ion_battery.EP_O.S.r << ", "; + transientCSVStream << Li_ion_battery.T_batt << ", "; + transientCSVStream << Li_ion_battery.S_eThermMass.T << ", "; + transientCSVStream << Li_ion_battery.SOC << ", "; + transientCSVStream << Li_ion_battery.dSOCqdt << ", "; + transientCSVStream << Li_ion_battery.C_rate << ", "; + transientCSVStream << time << endl; + + if (time-time_old >= 300) + { + cout << "T_batt (oC), " << Li_ion_battery.T_batt << ", "; + //cout << "HeatRate (BTU/s), " << Li_ion_battery.Q_heat << ", "; + cout << "BattCurrent (A), " << Li_ion_battery.EP_O.I.r << ", "; + cout << "BattSOC, " << Li_ion_battery.SOC << ", "; + cout << "BattVoltage (V), " << Li_ion_battery.EP_O.V.r << ", "; + cout << "C-rate, " << Li_ion_battery.C_rate << ", "; + cout << "CASE, " << CASE << ", "; + cout << "time (s), " << time << endl; + time_old = time; + } +} + +void transient_profile() { + // Based time points + if ( time < 570 ) { CLoad.Pdemand = 10.; } + else if ( time < 855 ) { CLoad.Pdemand = 0.; } // BPM: if 0 causes failure, put in a small value (0.1) + else if ( time < 1000 ) { CLoad.Pdemand = 119.5 ; } + else if ( time < 1415 ) { CLoad.Pdemand = 0.; } + else if ( time < 1565 ) { CLoad.Pdemand = 120.; } + else if ( time < 3640 ) { CLoad.Pdemand = 60.; } + else if ( time < 4100 ) { CLoad.Pdemand = 18.5; } + else if ( time < 4620 ) { CLoad.Pdemand = 0.5; } + else if ( time < 4760 ) { CLoad.Pdemand = 79.5; } + else if ( time < 5680 ) { CLoad.Pdemand = 6.5; } + else if ( time < 6600 ){ CLoad.Pdemand = 0.;} + + // Stop if battery gets fully drained (or close to it) + if (Li_ion_battery.SOC <= 0.01) { + // quit(); + } +} + +postsolverSequence = { "csvTransientPrint", "transient_profile" }; +run(); diff --git a/src/Battery.int b/src/Battery.int new file mode 100644 index 0000000..35a7b56 --- /dev/null +++ b/src/Battery.int @@ -0,0 +1,411 @@ +/*** + -------------------------------------------------------------------------------------------- + | | + | NASA Glenn Research Center | + | 21000 Brookpark Rd | + | Cleveland, OH 44135 | + | | + | File Name: Battery.int | + | Author(s): George Thomas, Jeffrey Csank, David Sadey, Tom Lavelle, Brian Malone | + | Date(s): August 2021 | + | | + -------------------------------------------------------------------------------------------- +***/ + +#ifndef __BATTERY__ +#define __BATTERY__ +#include "PSL_math.fnc" + +int ESO_Id_ExceededEnergyDes = (10000 * 145) + (100 * 22) + 1; +ESOregCreate(ESO_Id_ExceededEnergyDes, 1, "Energy Constraint Exceeded"); + +class Battery extends ElectricElement { + + //---------------------------- + // ****** DOCUMENTATION ****** + //---------------------------- + + // title = ""; + + description = "The " + isA() + " serves as a voltage source with constant + impedance. It also contains simplified logic for calculating energy consumed + to be used with a MDP solver setup. This energy is used to compute mass via a + flat assumption for specific energy. Users can also specify specific power. + Mass is calculated as the larger of the two masses (design power/spc power) + and (design energy/spc energy)"; + + usageNotes = isA() + + " + +- The input design parameters that must be specified by the modeler when creating an instance +of this " + isA() + " component are mass specific power (SpecificPower), design efficiency +(effDes), frequency, and voltage (Vreal and Vimag). This component will produce electrical +power at the specified voltage and frequency, with losses according to its design efficiency. + +- When running ONDESIGN, this component will determine its output impedance, which is +calculated such that the component operates with the specified design efficiency value. +When running OFFDESIGN, the model will run with losses according to the impedance calculated +from the ONDESIGN run. + +- This component, being a source component, must have its electricPowerType specified, and +will propagate this power type information to components downstream of it when +findSourcesAndPropagate() is called. This component supports DC, single phase, and 3 phase AC. + +- This component produces power and includes a node. Being a component including a node, a +voltage must be specified at this component. However, being a source component, its voltage is +typically set to a fixed value (unless the modeler changes it to be otherwise), and it does +not need solver variables to operate (except when running in MDP mode). + +- Note that, as a component that contains a node and has its voltage known at the beginning +of the solverSequence, it uses its prePass() method to call the electrical port update +function to pass this voltage information to electrical components that it is connected to. +Note that in the current implementation of the NPSS Power System Library, these components +containing nodes do not know what currents are going through their ports at the beginning of +an iteration. Because of this, they must be connected to power transmission components (cables +and breakers), and these transmission components must be run before node-bearing +transformation components like this one, as the transmission components will calculate and +populate these current values. When calculate() is called, this component's voltage and current +will be known at its ports. Note that this design is intended to be analogous to a common +approach taken in fluid networks within NPSS rocket models. + +- The battery block contains variables intended to be used in a multi-design-point (MDP) +simulation. This MDP logic is intended to consist of several different assemblies containing +systems including this source block; each one of these assemblies represents the same system +but running at a different design point (e.g. cruise, climb, and hover for an eVTOL concept). +To enable the solver variables that this component uses for MDP, set Option multiDes to true. +Then for each of the design point assemblies, set the appropriate value for segmentTime (how +long the mission segment is, in hours, that this assembly represents. Also select a segment +that represents the most important segment and give it an initial guess value for design +energy (energyDes). You will also need to disable autoSetup in ind_energyDes and dep_energyDes +or otherwise remove them from all assemblies other than the main MDP one. Finally, then set up +logic that copies energyDes from the main assembly to the other MDP assemblies before each of +them run, and set up a solver pair at the top level that varies the design energy for the main +mission segment, until the sum of the energies consumed in all of the segments is less than +the design energy. For an example on setting up this MDP logic, see baseline_all_elecMDP.run. + +- This component, like other power system components in the NPSS Power System Library, can +optionally include thermal models. An optional thermal model is enabled by setting +switchThermPort to TRUE, and plugging an EThermalMass subelement into the S_eThermMass socket. +Doing these will add a temperature state (existing within EThermalMass) and a thermal port to +the model. The thermal port is intended to connect this component to a second component that +represents the mechanism by which heat is extracted from this component. This second component +could represent a heat exchanger, cold plate, or just model heat transfer from the first +component to the surrounding environment. For more information, see EThermalMass. + +- The " + isA() + " component does not use solver variables, however it includes sizing logic. +this logic calculates mass as well as calculates the design torque and speed values. These +values are used by a performance map (if one is plugged into the S_map socket) to calculate +efficiency at the current operating point. For more information about performance maps for +this component, see MotorGeneratorMap.int"; + + //------------------------------ + // ****** SETUP VARIABLES ****** + //------------------------------ + + real actualSpecificPower { + value = 0; IOstatus = "output"; // [kw/kg] + description = "Specific power used during on-design."; + } + + real actualSpecificEnergy { + value = 0; IOstatus = "output"; // [wh/kg] + description = "Specific energy used during on-design."; + } + + eff { + value = 0.95; IOstatus = "output"; units = "none"; + description = "Actual (calculated) efficiency of this source element."; + } + + real frequency { + value = 0; IOstatus = "input"; units = "Hz"; // [hertz] + description = "Output frequency"; + } + + Loss_r { + value = 0; IOstatus = "output"; units = "kW"; // [kilowatts] + description = "Real component of losses in this load."; + } + + Loss_j { + value = 0; IOstatus = "output"; units = "kW"; // [kilowatts] + description = "Imaginary component of losses in this load."; + } + + Q_heat { + value = 0; IOstatus = "output"; units = "Btu/sec"; // [BTUs / sec] + description = "Power dissipation in BTU/s at current time."; + } + + real T_batt { + value = 20; IOstatus = "input"; units = "C"; // [degrees C] + description = "Battery Temperature."; + } + + real desired_energyDes { + value = 10./1000.; IOstatus = "input"; units = "none"; // [kW-h] + description = "Desired design energy capacity for this battery."; + } + + real energyDes { + value = 0; IOstatus = "output"; units = "none"; // [kW-h] + description = "Actual design energy capacity for this battery."; + } + + real SOC { + value = 1; IOstatus = "output"; units = "none"; // [fraction] + description = "State of charge for this battery."; + } + + real dSOCqdt { + value = 0; IOstatus = "output"; units = "none"; // [fraction/s] + description = "Time derivative of state of charge for this battery (calculated in map file)."; + } + + real powerDes { + value = 0; IOstatus = "output"; units = "none"; // [kW] + description = "Design power for this source."; + } + + real QDes { + value = 0; IOstatus = "output"; units = "none"; // [kW] + description = "Design Ah for this battery."; + } + + real R_0 { + value = 0.01; IOstatus = "output"; units = "none"; // [Ohm]; tR_0 in Python boring amprius_battery + description = "Static battery output resistance."; + } + + real R_Th { + value = 0.01578; IOstatus = "output"; units = "none"; // [Ohm]; tR_Th in Python boring amprius_battery + description = "Dynamic battery output resistance."; + } + + real RTotal { + value = 0; IOstatus = "output"; units = "none"; // [Ohm] + description = "Total battery resistance."; + } + + real Vout_des { + value = 4.2; IOstatus = "output"; units = "V"; // [volts] + description = "Design value for battery output voltage."; + } + + real Vout { + value = 3.6; IOstatus = "output"; units = "V"; // [volts] + description = "Battery output voltage at current operating point."; + } + + real Vout_guess { + value = 3.6; IOstatus = "output"; units = "V"; // [volts] + description = "Guess value for battery output voltage at current operating point."; + } + + real Voc { + value = 3.7; IOstatus = "output"; units = "V"; // [volts] + description = "Open circuit battery voltage (before battery's output impedance)."; + } + + real SpecificPower { + value = 13; IOstatus = "input"; units = "none"; // [kW/kg] + description = "Power to weight ratio for the source."; + } + + real SpecificEnergy { + value = 130; IOstatus = "input"; units = "none"; // [W-h/kg] + description = "Energy to weight ratio for the source."; + } + + real C_rate_des { + value = 1; IOstatus = "input"; units = "none"; // [-] + description = "C-rate at design point (discharge rate normalized to capacity)."; + } + + real C_rate { + value = 1; IOstatus = "input"; units = "none"; // [-] + description = "C-rate at current operating point (discharge rate normalized to capacity)."; + } + + Mass { + value = 1.; IOstatus = "output"; units = "kg"; // [kilograms] + description = "Computed on-design mass for the source."; + } + + //------------------------------------ + // ****** OPTION VARIABLE SETUP ****** + //------------------------------------ + + Option switchDes { + allowedValues = { "DESIGN", "OFFDESIGN" } + description = "Determines if the element is in design or off-design mode."; + rewritableValues = FALSE; + trigger = TRUE; + } + + Option switchThermPort { + allowedValues = { "TRUE", "FALSE" } + description = "Determines if component needs thermal port."; + rewritableValues = FALSE; // enables converter optimization + trigger = TRUE; + } + + //---------------------------------------------------------- + // ****** SETUP PORTS, FLOW STATIONS, SOCKETS, TABLES ****** + //---------------------------------------------------------- + + /* ELECTRICAL PORTS */ + + ElectricOutputPort EP_O { + description = "Electric output port."; + ElectricPowerType.allowedValues = { "DC" }; + setOption("ElectricPowerType", "DC"); // only DC output allowed as this is a battery pack + } + + /* SOCKETS */ + + Socket S_map { + allowedValues = { "T_batt", "SOC", "Vout_des", "desired_energyDes" } + description = "Socket for battery cell data map and logic to size pack from cells."; + socketType = "BatteryCellSoCTemperatureMap"; + } + + Socket S_eThermMass { + allowedValues = { "Q_heat", "Mass" } + description = "Thermal mass socket."; + socketType = "EThermalMass"; + } + + //----------------------------------------------------- + // ****** ADD SOLVER INDEPENDENTS & DEPENDENTS ****** + //----------------------------------------------------- + + // Solver pair to allow output voltage to sag as current and other vars vary. + Independent ind_Vout { + varName = "Vout_guess"; + autoSetup = TRUE; + indepRef = "1000"; + description = "Varies the battery output voltage to match Vout = Voc - Vdrop."; + } + Dependent dep_Vout { + eq_lhs = "Vout"; + eq_rhs = "Vout_guess"; + autoSetup = TRUE; + } + + // SOC state variable. + Independent ind_SOC { + varName = "SOC"; + autoSetup = FALSE; + description = "Varies state of charge (intended to be used with SOC integrator)."; + } + Integrator integ_SOC { + stateName = "SOC"; + derivativeName = "dSOCqdt"; + eq_lhs = "0"; + eq_rhs = "1"; + autoSetup = FALSE; + description = "Balances normalized capacity increments in the battery. Only intended to be used transiently."; + } + + // Solver pair to link up battery temperature variable used for performance and the temperature state. + Independent ind_T_batt { + varName = "T_batt"; + autoSetup = FALSE; + indepRef = "10000"; + description = "Varies the battery temperature to match the eThermalMass temperature state variable."; + } + Dependent dep_T_batt { + eq_lhs = "T_batt*9./5. + 491.67"; // deg C to deg R + eq_rhs = "S_eThermMass.T"; // deg R + autoSetup = FALSE; + } + + //------------------------------------------- + // ****** VARIABLE CHANGED METHODOLOGY ****** + //------------------------------------------- + + void variableChanged(string name, any oldVal) { + if (name == "switchThermPort") { + if (switchThermPort == "TRUE") { + create("", "ThermalInputPort", "Q_I"); + ind_T_batt.autoSetup = TRUE; + dep_T_batt.autoSetup = TRUE; + } + else { + ind_T_batt.autoSetup = FALSE; + dep_T_batt.autoSetup = FALSE; + } + } + } + + //----------------------------------------------- + // ****** PERFORM ENGINEERING CALCULATIONS ****** + //----------------------------------------------- + + void calculate() { + + // Declare design point power. Energy is calculated in the map logic. + powerDes = EP_O.S.r; + + // Run the battery map if it is present + if(!S_map.isEmpty()) { + S_map.execute(); + } + else { + // Map logic will calculate number of parallel/series cells and also calculates energy design capacity. + // However if we have no map (no pack building logic), just say our capacity is equal to the desired value. + if (switchDes == "DESIGN") { + energyDes = desired_energyDes; + } + } + + // Calculate output voltage (which the solver will force to be equal to the guess value) + RTotal = R_0 + R_Th; + Vout = Voc - EP_O.I.r*RTotal; + + if (switchDes == "DESIGN") { + + real energyBasedMass = (energyDes*1000) / SpecificEnergy; // (kW-h/1000) / (W-h/kg) = kg + real powerBasedMass = EP_O.S.r / SpecificPower; + + if (energyBasedMass > powerBasedMass) { + Mass = energyBasedMass; // compute mass based on assumed specific energy + } else { + Mass = powerBasedMass; // compute mass based on assumed specific power + } + + actualSpecificEnergy = 1000 * energyDes/Mass; // [Wh/kg] + actualSpecificPower = powerDes/Mass; // [kW/kg] + } + else { + } + + // Output params common to both on and off design cases. + //C_rate = EP_O.S.r / energyDes; // (design power kW * 1 hour) / (design energy in kW-h) kW/kWh + //dSOCqdt = -C_rate/3600.; //(kW/kWh) / (1h/3600s) + C_rate = EP_O.I.r / QDes; // (design power kW * 1 hour) / (design energy in kW-h) kW/kWh + dSOCqdt = -EP_O.I.r/(QDes*3600.); //(kW/kWh) / (1h/3600s) + + C_rate = abs(C_rate); // C-rate actually does not typically have an associated sign. + Loss_r = EP_O.I.mag**2 * RTotal / 1000.; + Loss_j = 0; + eff = (EP_O.S.r / (EP_O.S.r + Loss_r))**sign(EP_O.S.r); + real KW_PER_BTU_PER_SEC = 1.05505585; + Q_heat = sqrt(Loss_r**2 + Loss_j**2); + Q_heat /= KW_PER_BTU_PER_SEC; + + if (switchThermPort == "TRUE") { + // run the thermal mass model. + if (!S_eThermMass.isEmpty()) { + S_eThermMass.execute(); + } + } + } + + void prePass() { + EP_O.frequency = frequency; + EP_O.setIVRMS(EP_O.I.r, 0, Vout_guess, 0); + } +} +#endif diff --git a/src/BatteryCellSoCTemperatureMap.int b/src/BatteryCellSoCTemperatureMap.int new file mode 100644 index 0000000..3e20861 --- /dev/null +++ b/src/BatteryCellSoCTemperatureMap.int @@ -0,0 +1,192 @@ +/*** + -------------------------------------------------------------------------------------------- + | | + | NASA Glenn Research Center | + | 21000 Brookpark Rd | + | Cleveland, OH 44135 | + | | + | File Name: BatteryCellSoCTemperatureMap.int | + | Author(s): George Thomas | + | Date(s): August 2021 | + | | + -------------------------------------------------------------------------------------------- +***/ + +#ifndef __BATTERY_CELL_SOC_TEMPERATURE_MAP__ +#define __BATTERY_CELL_SOC_TEMPERATURE_MAP__ + +#include + +// Variables that exist in the parent element +// and are used in this subelement. AKA input params. +extern real SOC; +extern real T_batt; +// Used for pack sizing +extern real Vout_des; +extern real desired_energyDes; +extern real powerDes; + +// Variables that exist in the parent element +// and are set by this subelement. AKA output params. +extern real Voc; +extern real R_0; +extern real R_Th; +// Output to tell user what the ACTUAL capacity is. +extern real energyDes; +extern real QDes; + +class BatteryCellSoCTemperatureMap extends Subelement { + +//------------------------------------------------------------ +// ******* DOCUMENTATION ******* +//------------------------------------------------------------ + + title = ""; + + description = isA() + " manages the calculation of battery performance + map variables. The battery parameters output from these maps are open- + circuit voltage and internal resistance parameters. This subelement also + abstracts away all cell-level calculations (i.e., they live here and not + in battery.int) + + Inputs to these maps are state of charge (SOC) and battery temperature."; + + + usageNotes = isA() + +" + +- Revisit, we may need usage notes... For now, see description. +"; + + background = ""; + +//------------------------------------------------------------ +// ******* SETUP VARIABLES ******** +//------------------------------------------------------------ + + real Voc_cell { + value = 0; IOstatus = OUTPUT; units = NONE; + description = "Open circuit voltage for one cell."; + } + real R_0_cell { + value = 0; IOstatus = OUTPUT; units = NONE; + description = "Static output resistance for one cell."; + } + real R_Th_cell { + value = 0; IOstatus = OUTPUT; units = NONE; + description = "Dynamic output resistance for one cell."; + } + real energyDes_cell { + value = 10; IOstatus = OUTPUT; units = NONE; // kW-h + description = "Energy design capacity for one cell."; + } + real QDes_cell { + value = 10; IOstatus = OUTPUT; units = NONE; // kW-h + description = "Ah design capacity for one cell."; + } + real powerDes_cell { + value = 10; IOstatus = OUTPUT; units = NONE; // kW + description = "Power design for one cell."; + } + + real num_series { + value = 1; IOstatus = OUTPUT; units = NONE; + description = "Number of series cells in a string"; + } + real num_parallel { + value = 1; IOstatus = OUTPUT; units = NONE; + description = "Number of parallel sets of series cell strings."; + } +//------------------------------------------------------------ +// ******* OPTION VARIABLE SETUP ******* +//------------------------------------------------------------ + + Option switchDes { + allowedValues = { DESIGN, OFFDESIGN }; + description = "Determines if the subelement is in design or off-design mode"; + rewritableValues = FALSE; // Enables converter optimization. + trigger = FALSE; + } + + +//------------------------------------------------------------ +// ****** SETUP PORTS, FLOW STATIONS, SOCKETS, TABLES ******** +//------------------------------------------------------------ + + Socket TB_Voc { + description = "Voc versus Temperature and SOC. A function or table with the name TB_Voc must be declared at the subelement scope in order to fill this socket. The format is TB_Voc (real T, real SOC)."; + socketType = "Function"; + required = TRUE; + argTypes = { "real", "real" } + returnType = "real"; + } + + Socket TB_R_0 { + description = "R_0 versus Temperature and SOC. A function or table with the name TB_R_0 must be declared at the subelement scope in order to fill this socket. The format is TB_R_0 (real T, real SOC)."; + socketType = "Function"; + required = TRUE; + argTypes = { "real", "real" } + returnType = "real"; + } + + Socket TB_R_Th { + description = "R_Th versus Temperature and SOC. A function or table with the name TB_R_Th must be declared at the subelement scope in order to fill this socket. The format is TB_R_Th (real T, real SOC)."; + socketType = "Function"; + required = TRUE; + argTypes = { "real", "real" } + returnType = "real"; + } + +// TABLES + +//------------------------------------------------------------ +// ******* INTERNAL SOLVER SETUP ******* +//------------------------------------------------------------ + +//------------------------------------------------------------ +// ****** ADD SOLVER INDEPENDENTS & DEPENDENTS ****** +//------------------------------------------------------------ + +//------------------------------------------------------------ +// ******* VARIABLE CHANGED METHODOLOGY ******* +//------------------------------------------------------------ + +//------------------------------------------------------------ +// ******* PERFORM ENGINEERING CALCULATIONS ******* +//------------------------------------------------------------ + + void calculate() { + + //------------------------------------------------------------------- + // execute the maps: get cell open circuit voltage and resistances + //------------------------------------------------------------------- + Voc_cell = TB_Voc(T_batt, SOC); + R_0_cell = TB_R_0(T_batt, SOC); + R_Th_cell = TB_R_Th(T_batt, SOC); + + if ( switchDes == DESIGN ) { + //---------------------------------------------------------------------- + // On design, we need to size the pack. That is, figure out number of + // series and parallel cells in the pack. + //---------------------------------------------------------------------- + num_parallel = max(ceil(powerDes / powerDes_cell),ceil(desired_energyDes / energyDes_cell)); + //num_parallel = max((powerDes / powerDes_cell),(desired_energyDes / energyDes_cell)); + num_series = ceil(Vout_des/Voc_cell); + //num_series = Vout_des/Voc_cell; + energyDes = energyDes_cell * num_parallel; + QDes = QDes_cell * num_parallel; + } // end (switchDes == DESIGN) + + //---------------------------------------------------------------------- + // Now that we've sized the pack and we have cell level Voc/R_0/R_Th, + // let's calculate the pack level quantities. + //---------------------------------------------------------------------- + Voc = Voc_cell*num_series; + R_0 = R_0_cell*num_series / num_parallel; + R_Th = R_Th_cell*num_series / num_parallel; + + } // end calculate() function + +} // end subelement class + +#endif diff --git a/src/EThermalMass.int b/src/EThermalMass.int index f8be05c..d54a4e3 100644 --- a/src/EThermalMass.int +++ b/src/EThermalMass.int @@ -152,4 +152,4 @@ class EThermalMass extends Subelement { dTqdt = Q_net /( Mass*Cp ); } } -#endif \ No newline at end of file +#endif diff --git a/src/ElectricPort.prt b/src/ElectricPort.prt index 8f18b4e..a32c713 100644 --- a/src/ElectricPort.prt +++ b/src/ElectricPort.prt @@ -23,7 +23,7 @@ ESOregCreate(ESO_Id_NoRefPort, 1, "No refport in this port (make sure the port i int ESO_Id_ImagInDC = (10000 * 142) + (100 * 22) + 1; ESOregCreate(ESO_Id_ImagInDC, 1, "Imaginary components in DC port!"); -string sourceComponents[] = { "Source", "Generator" }; +string sourceComponents[] = { "Source", "Generator", "Battery" }; string loadComponents[] = { "Motor", "ConstantPowerLoad" }; string converterComponents[] = { "Inverter", "Rectifier", "DC_DC_Converter" };