Skip to content

Commit f3488cf

Browse files
committed
Added surface reactions to PFR flux
1 parent 0f52392 commit f3488cf

File tree

1 file changed

+37
-3
lines changed

1 file changed

+37
-3
lines changed

t3/utils/flux.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)