@@ -37,6 +37,7 @@ def generate_flux(model_path: str,
3737 fix_cantera_model : bool = True ,
3838 allowed_nodes : Optional [List [str ]] = None ,
3939 max_chemical_generations : Optional [int ] = None ,
40+ surface_names : Optional [List [str ]] = None ,
4041 ):
4142 """
4243 Generate a flux diagram for a given model and composition.
@@ -73,6 +74,7 @@ def generate_flux(model_path: str,
7374 allowed_nodes (Optional[List[str]], optional): A list of nodes to consider.
7475 any node outside this list will not appear in the flux diagram.
7576 max_chemical_generations (Optional[int], optional): The maximal number of chemical generations to consider.
77+ surface_names (Optional[List[str]], optional): List of surface names to consider.
7678
7779 Structures:
7880 profiles: {<time in s>: {'P': <pressure in bar>,
@@ -93,6 +95,7 @@ def generate_flux(model_path: str,
9395 T = T ,
9496 P = P ,
9597 V = V ,
98+ surface_names = surface_names ,
9699 a_tol = a_tol ,
97100 r_tol = r_tol ,
98101 energy = energy ,
@@ -144,6 +147,7 @@ def get_profiles_from_simulation(model_path: str,
144147 T : float ,
145148 P : float ,
146149 V : Optional [float ] = None ,
150+ surface_names : Optional [List [str ]] = None ,
147151 a_tol : float = 1e-16 ,
148152 r_tol : float = 1e-10 ,
149153 energy : bool = False ,
@@ -160,6 +164,8 @@ def get_profiles_from_simulation(model_path: str,
160164 T (float): The temperature of the mixture, in Kelvin.
161165 P (float): The pressure of the mixture, in bar.
162166 V (Optional[float], optional): The reactor volume in cm^3, if relevant.
167+ surface_names (Optional[List[str]], optional): List of surface names to consider.
168+ Pass an empty list if there are no surfaces.
163169 reactor_type (str, optional): The reactor type. Supported reactor types are:
164170 'JSR': Jet stirred reactor, which is a CSTR with constant T/P/V
165171 'BatchP': An ideal gas constant pressure and constant volume batch reactor
@@ -203,6 +209,7 @@ def get_profiles_from_simulation(model_path: str,
203209 length = V * 1e-6 / 1.0 ,
204210 area = 1.0 , # m^2
205211 n_cells = 10 ,
212+ surface_names = surface_names ,
206213 a_tol = a_tol ,
207214 r_tol = r_tol ,
208215 )
@@ -251,6 +258,7 @@ def set_pfr(gas: ct.Solution,
251258 T : float ,
252259 P : float ,
253260 flow_rate : float ,
261+ surfaces : List [ct .Interface ],
254262 a_tol : float = 1e-16 ,
255263 r_tol : float = 1e-10 ,
256264 ) -> Tuple [ct .ReactorNet , List [ct .IdealGasReactor ]]:
@@ -267,6 +275,8 @@ def set_pfr(gas: ct.Solution,
267275 T (float): Inlet temperature in K.
268276 P (float): Inlet pressure in Pa.
269277 flow_rate (float): Mass flow rate in kg/s.
278+ surfaces (List[ct.Interface]): List of surface names to consider.
279+ Pass an empty list if there are no surfaces.
270280 a_tol (float): Absolute tolerance.
271281 r_tol (float): Relative tolerance.
272282
@@ -280,14 +290,17 @@ def set_pfr(gas: ct.Solution,
280290 outlet = ct .Reservoir (gas )
281291 total_volume = length * area
282292 cell_volume = total_volume / n_cells
283- reactors = []
293+ reactors = list ()
284294 upstream = inlet
285295
286296 for _ in range (n_cells ):
287297 gas_cell = ct .Solution (model_path )
288298 gas_cell .TPX = gas .TPX
289299 reactor = ct .IdealGasReactor (gas_cell , energy = "off" , volume = cell_volume )
290300 mfc = ct .MassFlowController (upstream , reactor , mdot = flow_rate )
301+ for surface in surfaces :
302+ surface_instance = ct .Interface (model_path , name = surface .name , phases = [gas_cell ])
303+ ct .ReactorSurface (surface_instance , reactor )
291304 reactors .append (reactor )
292305 upstream = reactor
293306
@@ -412,6 +425,7 @@ def run_pfr(model_path: str,
412425 length : float ,
413426 area : float ,
414427 n_cells : int = 10 ,
428+ surface_names : Optional [List [str ]] = None ,
415429 a_tol : float = 1e-16 ,
416430 r_tol : float = 1e-10 ,
417431 ) -> Dict [float , dict ]:
@@ -427,14 +441,18 @@ def run_pfr(model_path: str,
427441 length (float): Reactor length in meters.
428442 area (float): Reactor cross-sectional area in m^2.
429443 n_cells (int): Number of CSTRs to discretize the PFR.
444+ surface_names (Optional[List[str]]): List of surface names to consider.
430445 a_tol (float): Absolute tolerance.
431446 r_tol (float): Relative tolerance.
432447
433448 Returns:
434449 dict: Outlet profiles (T, P, X, ROPs) for each residence time.
435450 """
436- gas = ct .Solution (model_path )
437- profiles = {}
451+ gas = ct .Solution (model_path , name = 'gas' )
452+ surfaces = list ()
453+ if surface_names :
454+ surfaces = [ct .Interface (model_path , name = surface_name , phases = [gas ]) for surface_name in surface_names ]
455+ profiles = dict ()
438456 stoichiometry = get_rxn_stoichiometry (gas )
439457 for tau in times :
440458 V_total = length * area
@@ -452,6 +470,7 @@ def run_pfr(model_path: str,
452470 T = T ,
453471 P = P ,
454472 flow_rate = flow_rate ,
473+ surfaces = surfaces ,
455474 a_tol = a_tol ,
456475 r_tol = r_tol ,
457476 )
@@ -467,6 +486,21 @@ def run_pfr(model_path: str,
467486 if rxn .equation not in rops [spc .name ]:
468487 rops [spc .name ][rxn .equation ] = 0
469488 rops [spc .name ][rxn .equation ] += cantera_reaction_rops [i ] * stoichiometry [spc .name ][i ]
489+ if surface_names :
490+ for surface in surfaces :
491+ surface_reactions = surface .reactions ()
492+ surface_phase = surface (surface_names .index (surface .name )).thermo
493+ surface_rops = surface_phase .net_rates_of_progress
494+ for i , rxn in enumerate (surface_reactions ):
495+ eqn = rxn .equation
496+ for spc in gas .species ():
497+ coeff = rxn .products .get (spc .name , 0 ) - rxn .reactants .get (spc .name , 0 )
498+ if coeff != 0 :
499+ if spc .name not in rops :
500+ rops [spc .name ] = {}
501+ if eqn not in rops [spc .name ]:
502+ rops [spc .name ][eqn ] = 0
503+ rops [spc .name ][eqn ] += surface_rops [i ] * coeff
470504 profile = {'T' : gas_out .T ,
471505 'P' : gas_out .P ,
472506 'X' : {s .name : x for s , x in zip (gas_out .species (), gas_out .X )},
0 commit comments