Source code for RCAIDE.Library.Methods.Powertrain.Converters.Fuel_Cells.Common.compute_stack_properties

# RCAIDE/Methods/Powertrain/Converters/Fuel_Cells/Common/compute_stack_properties.py
# 
# 
# Created:   Nov 2024, M. Clarke 
# Modified:  Jan 2025, M. Clarke 

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------
import RCAIDE
from RCAIDE.Framework.Core import Units
from RCAIDE.Library.Methods.Powertrain.Converters.Fuel_Cells.Larminie_Model import  compute_power, compute_voltage
from RCAIDE.Library.Methods.Powertrain.Converters.Fuel_Cells.Proton_Exchange_Membrane.compute_fuel_cell_performance import  evaluate_PEM ,  evaluate_max_gross_power, set_rated_current_density 
import  scipy as  sp
import  numpy as  np
import matplotlib.pyplot as  plt
import matplotlib.cm as cm

# ----------------------------------------------------------------------------------------------------------------------
#  Compute Stack Properties
# ----------------------------------------------------------------------------------------------------------------------  
[docs] def compute_stack_properties(fuel_cell_stack): """ Calculates fuel cell stack properties based on individual cell characteristics and stack configuration. Parameters ---------- fuel_cell_stack : RCAIDE.Components.Powertrain.Converters.Fuel_Cell_Stack The fuel cell stack component for which properties are being computed Returns ------- None Notes ----- This function computes geometric, electrical, and performance properties of a fuel cell stack based on individual cell properties and stack configuration. It handles both generic fuel cell stacks and proton exchange membrane (PEM) fuel cell stacks with different calculation methods. For generic fuel cells, optimization is used to find the maximum power point. For PEM fuel cells, specialized performance models are used that account for operating conditions like temperature and pressure. The function calculates: - Physical dimensions (length, width, height) based on cell arrangement - Maximum power, voltage, and current - Fuel consumption rates - Mass properties **Major Assumptions** * Cell properties are uniform across the stack * Cells are arranged in a regular geometric pattern * For PEM cells, standard atmospheric conditions are used if not specified * Thermal effects within the stack are simplified **Theory** For generic fuel cells, the maximum power point is found by optimizing: .. math:: P_{max} = \\max_{j \\in [j_{min}, j_{max}]} P(j) where j is the current density and P is the power function. For PEM fuel cells, detailed electrochemical models account for: - Activation losses - Ohmic losses - Concentration losses See Also -------- RCAIDE.Library.Methods.Powertrain.Converters.Fuel_Cells.Larminie_Model.compute_power RCAIDE.Library.Methods.Powertrain.Converters.Fuel_Cells.Proton_Exchange_Membrane.compute_fuel_cell_performance """ series_e = fuel_cell_stack.electrical_configuration.series parallel_e = fuel_cell_stack.electrical_configuration.parallel n_total = parallel_e *series_e normal_count = fuel_cell_stack.geometrtic_configuration.normal_count parallel_count = fuel_cell_stack.geometrtic_configuration.parallel_count stacking_rows = fuel_cell_stack.geometrtic_configuration.stacking_rows if int(parallel_e*series_e) != int(normal_count*parallel_count): raise Exception('Number of cells in gemetric layout not equal to number of cells in electric circuit configuration ') normal_spacing = fuel_cell_stack.geometrtic_configuration.normal_spacing parallel_spacing = fuel_cell_stack.geometrtic_configuration.parallel_spacing volume_factor = fuel_cell_stack.volume_packaging_factor euler_angles = fuel_cell_stack.orientation_euler_angles fuel_cell_length = fuel_cell_stack.fuel_cell.length fuel_cell_width = fuel_cell_stack.fuel_cell.width fuel_cell_height = fuel_cell_stack.fuel_cell.height x1 = normal_count * (fuel_cell_length + normal_spacing) * volume_factor # distance in the module-level normal direction x2 = parallel_count * (fuel_cell_width +parallel_spacing) * volume_factor # distance in the module-level parallel direction x3 = fuel_cell_height * volume_factor # distance in the module-level height direction length = x1 / stacking_rows width = x2 height = x3 *stacking_rows if euler_angles[0] == (np.pi / 2): x1prime = x2 x2prime = -x1 x3prime = x3 if euler_angles[1] == (np.pi / 2): x1primeprime = -x3prime x2primeprime = x2prime x3primeprime = x1prime if euler_angles[2] == (np.pi / 2): length = x1primeprime width = x3primeprime height = -x2primeprime # store length, width and height fuel_cell_stack.length = length fuel_cell_stack.width = width fuel_cell_stack.height = height fuel_cell = fuel_cell_stack.fuel_cell if type(fuel_cell_stack) == RCAIDE.Library.Components.Powertrain.Converters.Generic_Fuel_Cell_Stack: lb = 0.0001/(Units.cm**2.) #lower bound on fuel cell current density ub = 1.2/(Units.cm**2.) sign = -1. # used to minimize -power maximum_current_density = sp.optimize.fminbound(compute_power, lb, ub, args=(fuel_cell, sign)) P_fuel_cell = compute_power(maximum_current_density,fuel_cell) V_fuel_cell = compute_voltage(fuel_cell,maximum_current_density) # useful voltage vector efficiency = np.divide(V_fuel_cell, fuel_cell.ideal_voltage) mdot_H2 = np.divide(P_fuel_cell,np.multiply(fuel_cell.propellant.specific_energy,efficiency)) # store properties fuel_cell.rated_current_density = maximum_current_density fuel_cell.volume = fuel_cell.interface_area*fuel_cell.wall_thickness fuel_cell.mass = fuel_cell.volume*fuel_cell.cell_density*fuel_cell.porosity_coefficient fuel_cell.density = fuel_cell.mass/fuel_cell.volume fuel_cell.specific_power = fuel_cell.max_power/fuel_cell.mass fuel_cell_stack.mass_properties.mass = n_total*fuel_cell.mass fuel_cell_stack.voltage = V_fuel_cell * series_e fuel_cell_stack.maximum_voltage = V_fuel_cell * series_e fuel_cell_stack.maximum_power = P_fuel_cell * series_e fuel_cell_stack.maximum_current = fuel_cell_stack.maximum_power / fuel_cell_stack.maximum_voltage fuel_cell_stack.maximum_fuel_flow_rate = mdot_H2 * n_total elif type(fuel_cell_stack) == RCAIDE.Library.Components.Powertrain.Converters.Proton_Exchange_Membrane_Fuel_Cell: # check if mach number and temperature are passed design_altitude = 0 # call the atmospheric model to get the conditions at the specified altitude atmosphere = RCAIDE.Framework.Analyses.Atmospheric.US_Standard_1976() atmo_data = atmosphere.compute_values(design_altitude) segment = RCAIDE.Framework.Mission.Segments.Segment() segment.hybrid_power_split_ratio = 1.0 segment.battery_fuel_cell_power_split_ratio = 0.1 segment.state.conditions = RCAIDE.Framework.Mission.Common.Results() bus = RCAIDE.Library.Components.Powertrain.Distributors.Electrical_Bus() bus.fuel_cell_stacks.append(fuel_cell_stack) bus.append_operating_conditions(segment) for fuel_cell_stack in bus.fuel_cell_stacks: fuel_cell_stack.append_operating_conditions(segment,bus) for tag, bus_item in bus.items(): if issubclass(type(bus_item), RCAIDE.Library.Components.Component): bus_item.append_operating_conditions(segment,bus) for cryogenic_tank in bus.cryogenic_tanks: cryogenic_tank.append_operating_conditions(segment,bus) # compute fuel cell performance t_idx = 0 fuel_cell_stack_conditions = segment.state.conditions.energy[bus.tag].fuel_cell_stacks[fuel_cell_stack.tag] fuel_cell_stack_conditions.fuel_cell.stagnation_temperature[t_idx, 0] = atmo_data.temperature fuel_cell_stack_conditions.fuel_cell.stagnation_pressure[t_idx, 0] = atmo_data.pressure fuel_cell_stack_conditions.fuel_cell.pressure_drop[t_idx, 0] = fuel_cell.rated_p_drop_fc fuel_cell_stack_conditions.fuel_cell.stack_temperature[t_idx, 0] = fuel_cell.stack_temperature rated_current_density, rated_power_density = evaluate_max_gross_power(fuel_cell_stack,fuel_cell_stack_conditions,t_idx) set_rated_current_density(fuel_cell_stack, rated_current_density, rated_power_density) fuel_cell_stack_conditions.fuel_cell.current_density[t_idx] = rated_current_density m_dot_H2, V_fuel_cell, P_fuel_cell, _, _, _, _, _,_ = evaluate_PEM(fuel_cell_stack,fuel_cell_stack_conditions, t_idx) # store properties area_square_meters = fuel_cell.interface_area * 0.0001 fuel_cell.volume = area_square_meters*fuel_cell.wall_thickness fuel_cell.mass = fuel_cell.volume*fuel_cell.cell_density*fuel_cell.porosity_coefficient fuel_cell.density = fuel_cell.mass/fuel_cell.volume fuel_cell.specific_power = fuel_cell.max_power/fuel_cell.mass fuel_cell_stack.mass_properties.mass = n_total*fuel_cell.mass fuel_cell_stack.voltage = V_fuel_cell * series_e fuel_cell_stack.maximum_voltage = V_fuel_cell * series_e fuel_cell_stack.maximum_power = P_fuel_cell * n_total fuel_cell_stack.maximum_current = fuel_cell_stack.maximum_power / fuel_cell_stack.maximum_voltage fuel_cell_stack.maximum_fuel_flow_rate = m_dot_H2 * n_total return