Well, we just merged my big upgrade to the fuel cell code today, so I owe you guys a post with a writeup and some pretty graphs

.
A little background info. The Apollo CSM is powered by 3 bacon-type fuel cells (alkaline cells), which produce energy in the form of electrical current by the oxidation of hydrogen. Each of the fuel cell power plants consists of a stack of 31 cells, connected in series. The voltage-temperature-current relationship of these cells is not a simple one, and the cells rely heavily upon maintaining the proper temperature in order to produce the desired power output and useable voltage.
The model of fuel cell performance that NASSP used before was solid, but very simple, and I have been working since roughly October on an upgrade to it.
A contour plot of this relationship is shown below:
View attachment 24787
The colors and contour labels show voltage at particular current-temperature relationships.
The calculation for the model is:
C++:
//coefficients for 5th order approximation of fuel cell performance, taken from:
//CSM/LM Spacecraft Operational Data Book, Volume I CSM Data Book, Part I Constraints and Performance. Figure 4.1-10
double A = 0.023951368224792 * Temp + 23.9241562583015 - cloggVoltageDrop;
double B = 0.003480859912024 * Temp - 2.19986938582928;
double C = -0.0001779207513 * Temp + 0.104916556604259;
double D = 5.0656524872309E-06 * Temp - 0.002885372247954;
double E = -6.42229870072935E-08 * Temp + 3.58599071612147E-05;
double F = 3.02098031429142E-10 * Temp - 1.66275376548748E-07;
loadResistance = 784.0 / (power_load); //<R_F>, 784 = (28.0V)^2 which is the voltage that DrawPower() expects. use this calculate the resistive load on the fuel cell
//use an iterative procedure to solve for voltage and current. Should converge in ~2-3 steps, see https://gist.github.com/n7275/46a399d648721367a2bead3a6c2ae9ff
int NumSteps = 0;
while(NumSteps < 10) //10 is an absolute maximum to prevent hangs, and really should never get much higher than ~6-7 during extream transients
{
Volts = A + B * Amperes + C * Amperes*Amperes + D * Amperes*Amperes*Amperes + E * Amperes*Amperes*Amperes*Amperes + F * Amperes*Amperes*Amperes*Amperes*Amperes;
Amperes = Volts / loadResistance;
++NumSteps;
if ((abs(Volts - voltsLastTimestep) < 0.00001) && (abs(Amperes - ampsLastTimestep) < 0.00001))
{
break;
}
voltsLastTimestep = Volts;
ampsLastTimestep = Amperes;
}
power_load = Amperes * Volts; //recalculate power_load
Something note here is is the iterative solver. The way our systems code draws power from the fuel cells/batteries/inverters is with the
C++:
void e_object::DrawPower(double watts)
function. When we pass the nominal power-draw values found in the systems handbooks, the actual power drawn may be higher or lower than what DrawPower() is asking for depending upon the voltage of its power source. Because our power source here (the fuel cells) varies in output voltage as a function of temperature and current, this becomes a more complicated problem to solve than the old fuel cell code with it's fixed 28.8V and Ohm's Law voltage-current-power relationship. For this reason an iterative solver is needed. In essence, it calculates Ohm's Law recursively, using an initial guess voltage of 31V.
This process is best illustrated by the figure below. Keep in mind that this runs entirely within one timestep, and future iteration sequences use past values as their initial guess
View attachment 24789
The temperature-efficiency model is a function of cell current density and is as follows:
C++:
//efficiency and heat generation
//efficiency model calculated from APOLLO TRAINING | ELECTRICAL POWER SYSTEMSTUDY GUIDE COURSE NO.A212, and referenced documents
double heat = (power_load / (0.9063642805859956 +
-0.00040191337758397755 * power_load +
0.0000003368939782880486 * power_load * power_load +
-1.5580350625528442e-10 * power_load * power_load * power_load +
3.2902028095999155e-14 * power_load * power_load * power_load * power_load +
-2.581100488488906e-18 * power_load * power_load * power_load * power_load * power_load) - power_load)*dt; //I think this executes faster than calling pow()
When rendered and plotted, the performance map looks something like this:
View attachment 24790
If we take into account the energy lost to the structure/heating up incoming reactants, than the net heat output is:
View attachment 24791
Finally, here is a figure showing steady-state temperature of the cells, as a function of current and voltage. Because of how this is calculated it doesn't map nicely to the rectangular grid space, unfortunately, and my contour labels are a tad messed up, but it's illustrative nonetheless.
View attachment 24793
Reactant consumption has been improved in accuracy and is now:
C++:
#define H2RATIO 0.1119
#define O2RATIO 0.8881
// Reactant consumption
H2_flow = ((Amperes * MMASS[SUBSTANCE_H2]) / (2*FaradaysConstant)) * numCells * dt; //Faraday's 2nd law electrolysis. confirmed against CSM databook equation
O2_flow = H2_flow / H2RATIO * O2RATIO; //consume a stoichiometric amount of oxygen
How will this effect users?
Short answer: it won't other than that the gauges/telemetry will show more realistic temperature/voltage/power/current values for the fuel cells and busses.
If you're flying missions in NASSP than the only thing you need to remember is to follow your purge schedule.
Long answer: if you're doing things with the CSM EPS outside of its nominal operating power conditions than you need to pay attention to temperatures.
Here's some helpful tips for experimentation and playing with emergency, off-spec operation.
*Read the
Apollo Operations Handbook and
CSM Databook sections (2.6 and 4.1 respectively) on fuel cells if you plan on doing anything "off spec" ( e.g. emergency running on one cell).
*If you're operating in an [intentionally simulated] emergency power situation pay close attention to your voltage as you add and remove load.
*A hot cell produces more voltage than a cold cell so don't remove high-draw items all at once, similarly add loads slowly to colder cells, until they heat up.
This change won't break old scenarios, and you can in theory upgrade to the latest rev mid-mission, but It's probably better not to if you can stand it.
So please take it for a spin and if you find anything odd or something that doesn't make sense, let me know and I will fix it as quick as I can.
Thanks.
Matt